a-calc
来源于实际业务的字符串四则运算的库, 可解决以下问题(注意1.0.6之后有破坏性更新!):
- 体积:未压缩体积35-43KB 。gzip压缩体积13-15KB
- js数字计算精度问题
- 数字计算可能输出科学计数法
- 数字格式化, 数字千分位输出, 数字直接格式化成百分比, 保留数字的正负号, 直接输出分数等
- 带单位的数字计算或格式化, 例如:
0.1% + 2%
- 科学计数法写法的计算, 例如:
-2e3 + 6
- 支持四种舍入规则:去尾、进一、四舍五入、四舍六入(一种更精准的方法)
支持的运算符 : + - * / % **
安装
npm install a-calc
引入
commonjs
const {calc, fmt} = require("a-calc")
const {calc, fmt} = require("a-calc/cjs")
es module
import {calc, fmt} from "a-calc"
const {calc, fmt} from "a-calc/es"
browser
<script src="node_modules/a-calc/browser/index.js"></script>
<script>
const {calc, fmt} = a_calc
</script>
四则运算(支持科学计数法写法)
calc("0.1 + 0.2")
calc("0.1 + 0.2 * 0.3 / 0.4 * (0.5 + 0.6)")
calc("-2e2 + 3e+2")
关于空格
空格在无歧义的情况下是非必须的,甚至可以正确解析下面人眼都难以解析的写法calc("-2e+2+3e+2")
,但是没有人应该这样编写计算式来考验a-calc
解析能力,请记住始终在你的计算式包含空格,这样也更漂亮和清晰,就像我写的示例那样!!!
顺便举个有歧义的计算式 calc("50%%2", {_unit: true})
这种歧义显然是在带单位计算的时候出现,由于解析器不知道你的单位是%
还是 %%
所以你要用空格给出明确的意思,正确的写法应该是 calc("50% % 2", {_unit: true})
总之始终加空格!
填充变量并计算(重要)
计算后的值为精准值且不会出现科学计数法
let a = 0.000001
let b = 888.789
calc("a + b", {a,b})
calc("a * (b + c) % d + 7.123", [
{a: 1, b: 2},
{c: 3, d: 4}
])
calc("1 + o.a / arr[0].d",{
o: { a: 2 },
arr: [{ d: 8 }]
})
calc("a + b - c",[
{a: 1},
{b: 2, c: 3}
])
带单位的计算
实际情况不总是那么理想, 也许我们不得不计算两个百分比数字, 幸好现在a-calc支持这些操作, 但是请注意,单位会从第一个携带单位的数字上取, 后面的单位会被忽略
calc("1 + 2%", {_unit: true})
calc("1.123$$$ + 2.88% | + =6", {_unit: true})
在 1.0.6
之后带单位的计算可以有更多的参数,_unit
的取值可以为boolean | "on" | "off" | "auto" | "space"
参数看起来很多其实和之前的用法差不多, true "on" "auto"
这三个值的效果是一样的,表示自动识别数字后的单位,false "off"
表示关闭单位计算,最大的不同是 "space"
这个值表示只将空格作为单位分隔符,例如你的单位恰好是 +-
这种情况在普通模式下会被识别成运算符,就可以使用 "space"
模式,但是此时的空格就是必须的了,你要这样写:calc("2+- * 3")
最终结果为:6+-
计算并格式化
格式化支持如下功能:限制小数位数,保留正负号,百分比输出,科学计数法输出,千分位输出,并且他们是可以组合的,不过有个别的情况组合是不生效的,这个自己去试一下,组合情况太多,不在一一列举。
calc("0.1 + 0.2 | =2")
calc("0.11111 + 0.11111 | <=4")
calc("0.11 + 0.11 | <=4")
calc("0.1 + 0.2 | >= 5")
calc("0.0000001+ 0.0000001 | >= 5")
calc("1 + 1 | +")
calc("10000000 + 100000000 | ,")
calc("0.025 + 0.2 | /")
calc("1 + 1 | %")
calc("1 + 1 | \\e")
calc("10000000 + 100000000 | +,=10")
四种舍入规则
舍入规则通过在格式化字符串的部分加入,他们的符号分别为:
~-
去尾,默认的舍入规则~+
进一~5
四舍五入~6
四舍六入,该舍入规则相较四舍五入更为精准,规则在舍入的后一位为5的时候有所不同,他会查看5后面的位置,如果后面的数字不为0那么会进一,如果后面的数字为0 ,那么会看5前面的数字是否为偶数,如果是则不进,不是则进
calc("0.11 + 0.22 | =1 ~+")
calc("0.55 | =1 ~5")
calc("0.65 | =1 ~6")
这个新加入的舍入规则似乎会让格式化的部分更加的长,但是实际情况不是这样,一般一个项目的舍入规则是固定的,所以舍入规则部分的格式化应该被封装在默认的格式化参数中,在实际使用的时候完全不需要书写这部分内容,参考下面的默认格式化
说明
只格式化
calc("0.1 | =2")
fmt("0.1 | =2")
fmt("1000000 | ,")
高级技巧
错误处理
通常直接使用calc做计算要求输入的计算式是完全正确的, 默认 a-calc 不会帮你处理计算式的错误, 这个可以自己做过滤, 但在项目里我们可能不想做这件事情那么就需要额外的高级API, 在输入的计算式有误的时候静默捕获并给出一个合适的返回值
calc("1 + 2sd + d",{
_fill_data: {d: 3},
_error: "-",
})
calc("1 + 2sd + d", {
d: 8,
_error: "-"
})
默认格式化
在实际项目中可以利用默认格式化优化开发体验
calc("111111 + 11111 | ,",{_fmt: "=2"})
在项目中的使用姿势(vue3为例)
在项目中编写庞大的第二个参数是不好的, 所以第二个参数你应该想办法固定他, 下面只是一个在VUE项目中的演示
集成到vue3模板
import { calc, fmt } from "a-calc";
import type { App } from "vue";
import { getCurrentInstance } from "vue";
function bind_calc ( app: App<Element> )
{
app.config.globalProperties.calc = function ( expr: string, obj: any )
{
const instance: any = getCurrentInstance();
const data_arr = [ instance.setupState, instance.data ];
let _fmt, _error, _unit;
if ( obj !== undefined ) {
data_arr.unshift( obj );
_fmt = obj._fmt === undefined ? undefined : obj._fmt;
_error = obj._error === undefined ? undefined : obj._error;
_unit = obj._unit === undefined ? undefined : obj._unit;
}
return calc( expr, {
_fill_data: data_arr,
_error: _error === undefined ? "-" : _error,
_fmt,
_unit
} );
};
app.config.globalProperties.fmt = app.config.globalProperties.calc;
}
集成到script setup中
interface CalcConfig
{
_fmt?: string;
_error: string;
_unit?: boolean;
}
function use_calc ( state: ReactiveVariable<any>, config: CalcConfig = { _error: "-" } )
{
const data_arr = [ state ];
const _fmt = config._fmt === undefined ? undefined : config._fmt;
const _error = config._error === undefined ? undefined : config._error;
const _unit = config._unit === undefined ? undefined : config._unit;
return function ( expr: string, data = {} )
{
data_arr.unshift( data );
return calc( expr, {
_fill_data: data_arr,
_error: _error === undefined ? "-" : _error,
_fmt,
_unit
} );
};
}
下面是使用示例
<script lang="ts" setup>
const state = reactive({
a: 1,
b: 2,
c: 3
})
const calc = use_calc(state)
calc("a + b * c")
</script>
版本变更
- 1.0.12
- 文档添加库体积说明
- 修复表达式为空的时候加上 _error 参数依然异常的问题, 并添加相应的单元测试
- 1.0.10
- 1.0.6
- 破坏性变更:所有暴露出的小驼峰命名全部改成了蛇形命名法例如原来的
_fillData
现在变成了 _fill_data
,原因是蛇形命名法更清晰。 - 内部代码极大的简化,解析器几乎完全重写,带来更稳定的体验
- 原先的设计就是calc函数具备所有fmt的功能,可是1.0.6之前的版本虽然符合这个设计,但calc和fmt是分别实现的,现在fmt只是calc的别名。
- 支持新的运算符 **
- 支持新的格式化字符 % 可以将数字输出成百分比
- 支持新的格式化字符
\e
,可以将数字格式化成科学计数法 - 修复格式化字符串非法的时候可能造成死循环的问题
- 解决1/0是 Infinity 的问题
- 增加若干单元测试
- 更详细的类型提示
- 更新文档,添加vue3集成的示例代码
- 0.0.80
- 带来4种舍入规则,分别为:去尾、进一、四舍五入、四舍六入
- 更多边界情况的检测
- fmt允许不传入格式化字符串,这个特性允许你使用 fmt 来清除小数点后多余的0
- 0.0.79
- 0.0.78
- 支持科学计数法的计算
- 完整的单元测试
- 更多边界情况的检测
- 0.0.72
- 支持单个数值的带单位写法, 例如
calc("1元", {_unit: true})
或者 fmt("1元 | =2",{_unit: true})
- 补充文档
注意
视频教程
待定
问题提交
(如果遇到了什么问题, 请第一时间向我发送反馈邮件, 718879459@qq.com 对于bug我会第一时间修复他)