<span style="display:block;text-align:center;"></span>

昨天是三八妇女节,很开心他们能好好的进来玩玩了;不得不说工作至今我内人给我的帮忙是微小的,由衷感谢她对我的反对,三八妇女节高兴!

单位转换

我的项目中设计到很多长度、间隔、速度、体积等单位信息,而作为一个成熟的产品天然须要单位可配置;通过剖析其实只须要思考前端的展现局部即可,因而,要实现一个前端单位转换的性能;

业务剖析

咱们能够想到首先须要一个配置界面用来配置单位;须要一个函数传入value、_原始单位_、_显示单位_,输入转换后的value;很显著这个性能比拟独立、性能繁多,咱们能够写一个包来实现。

思考到有些单位转换后可能会呈现小数很长、或精度失落的状况,所以咱们还须要一个小数点显示几位和进位的配置。

同时;容许个性化的单项配置,比方房间默认单位是m,玻璃的单位却是mm,所以咱们容许个性化配置

  • 配置: 单位配置界面;
  • 配置: 小数点配置;
  • 配置: 个性化单项配置(单位、小数点)
  • function:convertUnit 函数 实现单位转换(逆转换)
  • function:precision 函数 实现小数精度管制

技术剖析

  1. 我司我的项目都是基于umi V3框架来实现的;所以咱们能够思考写一个Umi插件来实现咱们想要的性能;当然间接写一个 js 包也未尝不可,然而umi插件有个劣势那就是配置集中和 Import All From Umi,集体示意最喜爱这个特点 ;咱们也能够通过配置来实现包大小的管制,只导出/打包须要的;
  2. 转换单位有个库比拟举荐 convert-units,convert-units有一个简略的链式 API,易于浏览。它还能够配置参加其打包的度量单位,也能够自定义度量单位。
Umi 插件convert-unitsReactTypescript

数据结构

️ 配置这块当然是越简略越好了;

{    "default": {            //默认配置(房屋设计单位)        "m": "m",           //key局部 示意后端返回的数据单位,value局部示意 显示的单位        "m/s": "m/s",        "precision": 1    // 精度,[*0* 整数,*1*一位小数,*2* 两位小数]    },    "glass.length": {    //个性化单项配置(玻璃长度)        "m": "cm",        "precision": 1    }}

<span style="display:block;text-align:center;"></span>

代码实现

咱们先实现单位转换函数

其实这个很简略,

import configureMeasurements from 'convert-units';import length from 'convert-units/definitions/length';const convert = configureMeasurements({    length,});console.log(convert(1).from('m').to('cm'));//output  1000

所以咱们的函数是

type Units = LengthUnits;/** * 单位转换 * @param value * @param from 'cm' | 'm' | 'km'; * @param to 'cm' | 'm' | 'km'; * @returns */function convert(value: number, from: Units, to: Units) {  return from === to ? value : ConvertUnits(value).from(from).to(to);}console.log(convert(1,'m','cm'));//output  1000

留神这里from,to写成 string 会报一个类型谬误,这里咱们用type关键字独自申明一下。

而后实现精度进位函数

/** * 精度 * @param value 默认保留两位小数 * @param precision 0 | 1 | 2 * @param round true | false 是否四舍五入 * @returns */function precision(value: number, precision: number = 2,round:boolean = false): number {  const p = 1 / Number('1e-' + precision);  // Number.EPSILON防止精度失落  return Math[!round?'trunc':'round']((value + Number.EPSILON) * p) / p;}export { ConvertUnits,convert, precision };console.log(precision(1.345, 0));//output  1console.log(precision(1.345, 1));//output  1.3console.log(precision(1.345, 2));//output  1.34,console.log(precision(1.345, 2, true));//output  1.35,

这个函数没什么讲的, 留神精度失落问题和防止应用 toFixed即可。

单位配置

咱们先看一下成果

很简略,依据数据结构咱们间接做状态治理就行,界面局部咱们不讲,不在此次探讨范畴;

  const [ unitConfigs, setUnitConfig ] =  useState({     //前期能够换成useSelector,用models治理状态对接后端API。     default: { mm: 'mm', 'cm/s': 'cm/s', precision: 2 },    'glass.length': { mm: 'cm', precision: 1 },   });

开始,先新启一个 Umi 插件我的项目

yarn create umi ● Umi Plugin (for plugin development) ● yarn ● taobao (recommended for China) ● umi-plugin-convert-unit
  1. 首先咱们思考应用convert-units,咱们能够通过 Umi 插件中的Ïapi.addOnDemandDepsÏ主动装置convert-units;

2. 咱们看到 convert-units官网的一句话。

convert-units 按需援用度量单位

import configureMeasurements from 'convert-units';import volume from 'convert-units/definitions/volume';/*  `configureMeasurements` is a closure that accepts a directory  of measures and returns a factory function (`convert`) that uses  only those measures.*/const convert = configureMeasurements({    volume,    mass,    length,});

因而,没有援用的模块是不会打包的。

然而问题来了,咱们想通过如下相似配置来动静导入所须要的度量模块,应该怎么操作呢?

///.umirc.tsexport default{  ConvertUnits: ["area", "length", "mass", "pressure", "speed", "volume"],...

检查一下api.writeTmpFileAPI 发现这个 API 是反对模版字符串的;通过动静给定context模板上下文来实现动静导入,代码如下;

/// **index.ts**//主动装置convert-units  api.addOnDemandDeps(() => [{ name: "convert-units", version: "^3.0.0-beta.5" }]);  api.onGenerateFiles(async () => {    api.writeTmpFile({      path: "index.ts",      tplPath: join(__dirname, "./core/convert.tpl"),      context: {        measures: (api.config.ConvertUnits || ["length"]).map((value: any, index: number) => ({ value, index })),        upper: function () {          return this.value.slice(0, 1).toUpperCase() + this.value.slice(1).toLowerCase();        },        first: function () {          return this.index === 0;        },        link: function () {          return this.index !== 0;        },      },    }); // types.d.ts    api.writeTmpFile({      path: "types.d.ts",      content: "export {}",    });  });Ï

轻轻通知你,Umi 插件模版字符串用的是<<mustache>>,不要问我你怎么晓得的;

/// **core/convert.tpl**import configureMeasurements, {{{#measures}}  {{value}},{{#upper}}{{.}}{{/upper}}Systems,{{#upper}}{{.}}{{/upper}}Units,{{/measures}}} from "convert-units";// Measures: The names of the measures being usedtype Measures = {{#measures}}{{#first}}'{{value}}'{{/first}}{{#link}} | '{{value}}'{{/link}}{{/measures}};// Systems: The systems being used across all measurestype Systems = {{#measures}}{{#first}}{{#upper}}{{.}}{{/upper}}Systems{{/first}}{{#link}} | {{#upper}}{{.}}{{/upper}}Systems{{/link}}{{/measures}};// Units: All the units across all measures and their systemstype Units ={{#measures}}{{#first}}{{#upper}}{{.}}{{/upper}}Units{{/first}}{{#link}} | {{#upper}}{{.}}{{/upper}}Units{{/link}}{{/measures}};let config = {{{#measures}}  {{value}},{{/measures}}};const ConvertUnits = configureMeasurements<Measures, Systems, Units>(config);

OK yarn link 到我的项目中试一下,咱们只配置 ConvertUnits: ['length', 'speed'],,两个度量单位,看看成果。完满 !

/// **src/.umi/plugin-convertunits/index.ts**// @ts-nocheckimport configureMeasurements, {  length,LengthSystems,LengthUnits,  speed,SpeedSystems,SpeedUnits,} from "convert-units";// Measures: The names of the measures being usedtype Measures = 'length' | 'speed';// Systems: The systems being used across all measurestype Systems = LengthSystems | SpeedSystems;// Units: All the units across all measures and their systemstype Units = LengthUnits | SpeedUnits;let config = {  length,  speed,};const ConvertUnits = configureMeasurements<Measures, Systems, Units>(config);...function convert(value: number, from: Units, to: Units){...}function precision(value: number, precision: number = 2){...}

OK ,至此 Umi 插件开发的外围局部基本上就这些。

⚠️ 如果convert-units中提供的度量单位不满足的时候,咱们能够独自扩大他,并通过api.writeTmpFile写入 ts 文件,再将上方config字面量进行构造扩大即可;

插件应用

1️ package.json 中装置插件

yarn add @umi/plugin-convert-units

2️ .umirc.ts 中配置插件、及插件配置(好绕口)

export default defineConfig({  plugins: ['@umi/plugin-convert-units'],  ConvertUnits: ['length', 'speed'],  });

3️ import 之:

import { convert, precision } from 'umi';

4️ 联合咱们之前的配置实现单位主动转换

... const {        unitConfig: { defaultConfig, ...otherConfig },      } = yield select((state: { configModel: any }) => state.configModel);...array.map((item:any)=>{  let { value, unit, showUnit,key, ...other } = item;  const unitConfigItem = otherConfig?.[key] || defaultConfig;  //容错  if(!showUnit) showUnit = unit;  // 转换单位  if (!!showUnit && !!unit) {    value = convert(Number(value), showUnit, unit);  }  // 精度  value = precision(Number(value), unitConfigItem?.['precision']) ;  return {value, unit, key, ...other}})...

总结

  • 数据结构:咱们比拟喜爱简略易扩大的数据结构;
  • convert-units:这个转换单位的第三方库反对TS,_动静载入_,_单位类型限度_;
  • Umi 插件:通过插件咱们能够很容易的集成一些通用的性能,不必再思考类型查看、装置版本的问题。
  • mustache.js:这是一种无逻辑的模板语法。它能够用于 HTML、配置文件、源代码 - 任何货色,它通过应用散列或对象中提供的值扩大模板中的标签来工作。没有 if 语句、else 子句或 for 循环。

最初附上mustache.js搞笑的动图

本文由mdnice多平台公布