accounting、currency.js、dinero.js 和 numeral 都是用于处理 JavaScript 中数字格式化、货币显示及计算的库。accounting 和 numeral 是早期的经典方案,主要用于格式化,但目前已不再维护。currency.js 专注于轻量级的货币处理,适合简单的展示需求。dinero.js 则提供了不可变的对象模型,专注于金融级别的计算精度和安全性。开发者需要根据项目的维护性要求、计算复杂度以及是否需要处理货币精度来选择合适工具。
在前端开发中,处理货币和数字格式化看似简单,实则暗藏陷阱。JavaScript 的原生浮点数计算容易导致精度丢失,而不同地区的货币格式差异巨大。accounting、currency.js、dinero.js 和 numeral 是生态中常见的解决方案,但它们的设计目标和维护状态截然不同。本文将从维护状态、格式化能力、计算安全性和 API 设计四个维度进行深度对比。
选择第三方库时,维护状态是首要考量因素。一个不再维护的库可能包含未修复的安全漏洞,且无法兼容未来的 JavaScript 版本。
accounting 和 numeral 均已停止维护多年。
currency.js 和 dinero.js 目前相对活跃。
currency.js 专注于轻量级场景,更新稳定。dinero.js 专注于金融级精度,v1 版本稳定,v2 版本正在重构中。// 风险提示:以下库已不再维护,新项目慎用
import accounting from 'accounting';
import numeral from 'numeral';
// 推荐:以下库维护状态较好
import currency from 'currency.js';
import Dinero from 'dinero.js';
格式化是这些库最基础的功能。我们需要观察它们如何处理千位分隔符、小数位以及货币符号。
accounting 提供专门的货币格式化方法,配置项较为传统。
// accounting: 专门针对货币
accounting.formatMoney(12345678, { symbol: '¥', precision: 2 });
// 输出:¥12,345,678.00
currency.js 链式调用,默认处理货币符号,支持简单配置。
// currency.js: 链式格式化
currency(12345678, { symbol: '¥' }).format();
// 输出:¥12,345,678.00
dinero.js 基于对象创建,格式化方法更严谨,支持国际化参数。
// dinero.js: 基于对象实例
Dinero({ amount: 12345678, currency: 'CNY' }).format();
// 输出:¥12,345,678.00 (取决于环境配置)
numeral 通用数字格式化,需要通过字符串模板定义格式,灵活性高但易出错。
// numeral: 字符串模板定义格式
numeral(12345678).format('¥0,0.00');
// 输出:¥12,345,678.00
这是区分普通格式化库和金融库的关键。JavaScript 的 0.1 + 0.2 !== 0.3 问题在货币计算中是致命的。
accounting 本质上是对原生 Number 的包装,不解决 浮点数精度问题。
// accounting: 无安全计算,直接使用原生数学
// 不推荐用于计算,仅用于展示
const total = 0.1 + 0.2;
accounting.formatMoney(total); // 可能显示 ¥0.30000000000000004
currency.js 内部使用整数运算来处理小数,避免了大部分精度问题,适合简单加减。
// currency.js: 内部处理精度
const val1 = currency(0.1);
const val2 = currency(0.2);
val1.add(val2).format(); // 输出:¥0.30
dinero.js 严格使用整数(最小货币单位)进行计算,完全避免浮点数误差,最安全。
// dinero.js: 基于最小单位(如分)计算
const d1 = Dinero({ amount: 10, currency: 'USD' }); // 10 分
const d2 = Dinero({ amount: 20, currency: 'USD' }); // 20 分
d1.add(d2).format(); // 输出:$0.30
numeral 同样基于原生 Number,不解决 精度问题,仅提供格式化。
// numeral: 无安全计算
const total = 0.1 + 0.2;
numeral(total).format('0.00'); // 可能显示 0.30,但内部值仍不精确
API 的设计影响代码的可读性和可维护性。
accounting 采用传统函数式调用,配置通过对象传递。
// accounting: 函数 + 配置对象
accounting.formatNumber(1000, { thousand: ',', decimal: '.' });
currency.js 采用链式调用,代码流畅,适合快速开发。
// currency.js: 链式调用
currency(1000).multiply(2).format();
dinero.js 采用不可变对象模式,每次操作返回新实例,便于调试和追踪。
// dinero.js: 不可变实例
const d = Dinero({ amount: 1000, currency: 'USD' });
const newD = d.add(d); // d 不变,newD 是新对象
numeral 采用链式调用,但依赖字符串模板,类型安全性较差。
// numeral: 链式 + 字符串模板
numeral(1000).format('0,0');
| 特性 | accounting | currency.js | dinero.js | numeral |
|---|---|---|---|---|
| 维护状态 | ❌ 已停止 | ✅ 活跃 | ✅ 活跃 | ❌ 已停止 |
| 主要用途 | 货币格式化 | 轻量货币计算 | 金融级计算 | 通用数字格式化 |
| 精度安全 | ❌ 原生浮点 | ✅ 内部整数处理 | ✅ 严格整数单位 | ❌ 原生浮点 |
| API 风格 | 函数式 | 链式调用 | 不可变对象 | 链式 + 模板 |
| 包体积 | 小 | 极小 | 中等 | 小 |
在选择这些库时,请根据业务场景的复杂度做出决定。
对于现代新项目:
dinero.js。它的不可变设计和整数运算能避免很多财务 bug。虽然 API 稍微繁琐,但安全性值得付出。currency.js 是很好的平衡点。它比 dinero.js 简单,比 accounting 安全。Intl.NumberFormat,而不是 numeral。原生 API 性能更好且无依赖。对于旧项目维护:
accounting 或 numeral,且没有计算精度问题,可以暂时保留。但在新功能开发中,应逐步迁移到更安全的方案。最终结论:
dinero.js 是处理金融逻辑的专业工具 — 安全但较重。currency.js 是前端展示的轻量利器 — 简单且够用。accounting 和 numeral 已成为历史 — 除非维护旧代码,否则不应再引入新项目。
仅建议在维护旧项目时使用,因为该库已多年未更新,缺乏现代 JavaScript 特性支持。如果你的项目只需要简单的货币符号格式化且无法引入新依赖,它可以勉强胜任。但在新项目中,应避免使用此库,转而选择更活跃的替代方案。
适合需要轻量级货币格式化且涉及简单加减计算的场景。它的 API 简单直观,包体积小,不会给项目带来太多负担。如果你的需求主要是前端展示货币金额,且不需要复杂的金融逻辑,这是一个不错的选择。
适合对货币计算精度和安全性有严格要求的金融类应用。它采用不可变数据模式,避免了浮点数误差,并提供了丰富的货币操作方法。如果你的项目涉及复杂的金额计算、分配或转换,应优先选择此库以确保数据准确。
仅建议在维护旧项目或非货币类的通用数字格式化场景中使用。该库已停止维护,存在潜在的安全和兼容性风险。如果只需要格式化百分比、字节大小等非货币数字,且能接受潜在风险,可以考虑,但新项目推荐使用 Intl API 或其他活跃库。
accounting.js is a tiny JavaScript library for number, money and currency parsing/formatting. It's lightweight, fully localisable, has no dependencies, and works great client-side or server-side. Use standalone or as a nodeJS/npm and AMD/requireJS module.
Visit the plugin homepage for demos and documentation: http://openexchangerates.github.io/accounting.js/
Please checkout or download the latest stable tag before using in production. Bug reports and pull requests are welcome.
Maintained by Open Exchange Rates and originally by @josscrowcroft and other contributors.
v0.4.1 - Alias accounting.formatNumber() as accounting.format()
v0.4 - Transferred repository to Open Exchange Rates for ongoing maintenance
v0.3.2 - Fixed package.json dependencies (should be empty object)
v0.3.0
npm install accounting and then var accounting = require("accounting"); in your nodeJS scripts.acounting.unformat now also aliased as acounting.parsedefaults methodv0.2.2 - Fixed same issue as #Num: #24 in formatNumber; switch to Google Closure Compiler for minified version.
v0.2.1 - Fixed issue #Num: #24 (locally-defined settings object was being modified by formatMoney)
v0.2
accounting.settings.currency.formatv0.1.4
v0.1.3
format parameter to control symbol and value position (default "%s%v", or [symbol][value])v0.1.2
accounting.formatColumn( [[1,12,123,1234], [1234,123,12,1]] ), returns matching array with inner columns lined up)v0.1.1
accounting.toFixed(value, precision)), which treats floats more like decimals for more accurate currency roundingNaN errors when no value in unformatv0.1 - First version