自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

寫了個插件,一口氣解決項(xiàng)目中所有精度丟失問題!

開發(fā) 前端
當(dāng)我們擁有了decimal.js之后,每當(dāng)我們進(jìn)行運(yùn)算的時候,就必須引入它進(jìn)行使用,每一個頁面都得重復(fù)這一操作,于是萌生了一個想法——我想自動!不想手動!

前言

大家好,我是林三心,用最通俗易懂的話講最難的知識點(diǎn)是我的座右銘,基礎(chǔ)是進(jìn)階的前提是我的初心。

JS 繞不開的精度丟失問題

在 javascript 中,當(dāng)我們進(jìn)行運(yùn)算時

0.1 + 0.2

你覺得輸出是 0.3 嗎?顯然不是的,由于 javascript 存在精度丟失問題,導(dǎo)致了輸出的不是你預(yù)期的

圖片圖片

image.png

至于為什么會精度丟失呢?我之前出過一篇文章專門講了這個原因你知道 0.1+0.2 !==0.3是進(jìn)制問題,但你講不出個所以然,是吧???,感興趣的朋友可以看看,由于這不是本文的重點(diǎn),所以我在這就不過多講解~

解決精度丟失的方案?

我會選擇使用 decimal.js 這個庫,文檔在 文檔,他的基本使用如下:

// 先安裝
npm install decimal.js

// 后使用
const Decimal = require('decimal.js');

new Decimal(0.1).add(0.2) // 加法 輸出 0.3
new Decimal(0.1).sub(0.2) // 減法
new Decimal(0.1).mul(0.2) // 乘法
new Decimal(0.1).div(0.2) // 除法

使用 decimal.js進(jìn)行運(yùn)算,能解決精度丟失的問題~

不想手動!想自動!

很煩啊!

當(dāng)我們擁有了decimal.js之后,每當(dāng)我們進(jìn)行運(yùn)算的時候,就必須引入它進(jìn)行使用,每一個頁面都得重復(fù)這一操作,于是萌生了一個想法——我想自動!不想手動!

思路

那要怎么才能自動呢?由于前段時間群里很多人說想學(xué)習(xí)寫 babel 插件,所以剛好,針對這個需求,我可以實(shí)現(xiàn)一個 babel 插件,它的功能是:將項(xiàng)目中 0.1 + 0.2 這種表達(dá)式,轉(zhuǎn)換為 new Decimal(0.1).add(0.2)

0.1 + 0.2
// 轉(zhuǎn)換為
new Decimal(0.1).add(0.2)

這樣就能一次性把項(xiàng)目中的精度丟失問題解決了~

開發(fā) babel 插件

前置準(zhǔn)備

涉及到三個問題:

  • webpack 和 rollup 如何選擇
  • rollup 打包環(huán)境的搭建
  • 如何發(fā)布到 npm 上

這三個問題具體我在上一篇文章【如何使用Rollup開發(fā)一個npm包并發(fā)布】里有提到過了,在本文我就不過多講解

搭建一個 Rollup 打包環(huán)境

新建一個 babel-plugin-sx-accuracy文件夾,用來開發(fā) babel 插件

名字可以自己取,但是為了規(guī)范,最好是 babel-plugin- 開頭

接著進(jìn)入 babel-plugin-sx-accuracy 文件夾,輸入

npm init
npm i rollup @rollup/plugin-babel -D
npm i decimal.js -S

package.json 中的內(nèi)容為:

"name": "babel-plugin-sx-accuracy",
  "version": "1.0.20",
  "description": "",
  "main": "dist/index.js",
  "type": "module",
  "scripts": {
    "build": "rollup -c"
  },
  "files": [
    "dist/*",
    "src/*"
  ],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@rollup/plugin-babel": "^6.0.3",
    "rollup": "^3.26.2"
  },
  "dependencies": {
    "decimal.js": "^10.4.3"
  }
}

然后在根目錄下新建 rollup.config.js 文件,用來配置 rollup 打包

// rollup.config.js
import babel from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/index.js',
    format: 'cjs',
  },
  plugins: [
    babel({
      babelHelpers: 'bundled',
    }),
  ],
};

最后新建 src/index.js,我們的插件邏輯就寫在這里

圖片圖片

什么是抽象語法樹(AST)?

我們可以借助一個網(wǎng)站,來一睹抽象語法樹的真容~ https://astexplorer.net/

圖片圖片

這里我們可以記住幾個點(diǎn)

  • 每一個代碼片段都有屬于自己的節(jié)點(diǎn)類型
  • 代碼最外層的節(jié)點(diǎn)類型為 Program
  • 像 0.1+0.2 這種表達(dá)式,節(jié)點(diǎn)類型為 BinaryExpression
  • BinaryExpression節(jié)點(diǎn)里會有幾個重要的東西

operaor:運(yùn)算符號

left:左邊的數(shù)字

right:右邊的數(shù)字

其實(shí)抽象語法樹的節(jié)點(diǎn)類型有很多種,我列舉一些:

  • 標(biāo)識符(Identifier):表示變量、函數(shù)名等標(biāo)識符的節(jié)點(diǎn)
  • 字面量(Literal):表示字面量值,如字符串、數(shù)字、布爾值等
  • 表達(dá)式語句(ExpressionStatement):表示包含表達(dá)式的語句節(jié)點(diǎn)
  • 賦值表達(dá)式(AssignmentExpression):表示賦值操作的表達(dá)式節(jié)點(diǎn),如 x = 5
  • 二元表達(dá)式(BinaryExpression):表示包含二元操作符的表達(dá)式節(jié)點(diǎn),如 x + y
  • 一元表達(dá)式(UnaryExpression):表示包含一元操作符的表達(dá)式節(jié)點(diǎn),如 -x
  • 函數(shù)聲明(FunctionDeclaration):表示函數(shù)聲明的節(jié)點(diǎn),包括函數(shù)名、參數(shù)和函數(shù)體
  • 變量聲明(VariableDeclaration):表示變量聲明的節(jié)點(diǎn),包含變量名和可選的初始值
  • 條件語句(IfStatement):表示 If 條件語句的節(jié)點(diǎn),包括條件表達(dá)式、if 分支和可選的 else 分支
  • 循環(huán)語句(WhileStatement、ForStatement):表示循環(huán)語句的節(jié)點(diǎn),分別代表 While 循環(huán)和 For 循環(huán)
  • 對象字面量(ObjectLiteral):表示對象字面量的節(jié)點(diǎn),包含對象屬性和屬性值
  • 數(shù)組字面量(ArrayLiteral):表示數(shù)組字面量的節(jié)點(diǎn),包含數(shù)組元素
  • 函數(shù)調(diào)用(CallExpression):表示函數(shù)調(diào)用的節(jié)點(diǎn),包含調(diào)用的函數(shù)名和參數(shù)列表
  • 返回語句(ReturnStatement):表示返回語句的節(jié)點(diǎn),包含返回的表達(dá)式

當(dāng)然大家現(xiàn)階段不需要去記,大家只需要記得這兩個類型就行了:

  • 代碼最外層的節(jié)點(diǎn)類型為 Program
  • 像 0.1+0.2 這種表達(dá)式,節(jié)點(diǎn)類型為 BinaryExpression

其實(shí),我們平時在 webpack 開發(fā)時會接觸到一系列的插件,他們的功能比如有

  • 去除 console.log
  • 壓縮代碼
  • 去除注釋

其實(shí)他們的原理整體上都是一致的,分為三步:

  • 第一步:將代碼轉(zhuǎn)換成抽象語法樹
  • 第二步:使用 babel 為我們提供的方法,對語法樹進(jìn)行增刪改查
  • 第三步:將處理后的語法樹重新轉(zhuǎn)換成代碼

而我們將要開發(fā)的插件,也是用到這個過程,但是第一步和第三步我們不需要管,我們只需要完成第二步中的增刪改查操作即可~

注意點(diǎn):在第二步中,babel 會對抽象語法樹進(jìn)行深度遍歷,遍歷到目標(biāo)節(jié)點(diǎn)后,又會重新回到上層節(jié)點(diǎn)去重新遍歷下一個目標(biāo)節(jié)點(diǎn),所以一個節(jié)點(diǎn)會被遍歷兩次,一來一回 進(jìn)去是 enter 回去是 exit

圖片圖片

插件基本代碼結(jié)構(gòu)

下文使用 AST 來表達(dá)抽象語法樹

export default function ({ template: template, types: t }) {

  return {
    visitor: {
      Program: {
        exit: function (path) {
        }
      },
      BinaryExpression: {
        exit: function (path) {
        }
      }
    }
  }
}

開發(fā)一個 babel 插件,文件必須默認(rèn)返回一個函數(shù),接收一個對象參數(shù),里面有個屬性我們需要用到

  • template: 是@babel/template的一個方法,他能使用模板的方式生成AST節(jié)點(diǎn)

函數(shù)內(nèi)部的東西,我們也介紹下

  • vistor: 你可以理解為修改AST節(jié)點(diǎn)的入口
  • Program、BinaryExpression: 你需要修改的AST節(jié)點(diǎn)類型
  • exit: 就是剛剛說的 一來一回 中的,回
  • path: 就是被遍歷到的AST節(jié)點(diǎn)對象

插件完全實(shí)現(xiàn)

// 定義構(gòu)造函數(shù)的名稱常量
const DECIMAL_FUN_NAME = 'Decimal'
// 運(yùn)算符號映射 decimal.js 的四個方法
const OPERATIONS_MAP = {
  '+': 'add',
  '-': 'sub',
  '*': 'mul',
  '/': 'div'
}
// 運(yùn)算符號數(shù)組
const OPERATIONS = Object.keys(OPERATIONS_MAP)

export default function ({ template: template }) {
  
  // require decimal.js 的節(jié)點(diǎn)模板
  const requireDecimalTemp = template(`const ${DECIMAL_FUN_NAME}=require('decimal.js')`);
  // 將運(yùn)算表達(dá)式轉(zhuǎn)換為decimal函數(shù)的節(jié)點(diǎn)模板
  const operationTemp = template(`new ${DECIMAL_FUN_NAME}(LEFT).OPERATION(RIGHT).toNumber()`);

  return {
    visitor: {
      Program: {
        exit: function (path) {
          // 調(diào)用方法,往子節(jié)點(diǎn)body
          // 中插入 const Decimal = require('decimal.js')
          // 表達(dá)式
          path.unshiftContainer("body",
          requireDecimalTemp())
        }
      },
      BinaryExpression: {
        exit: function (path) {
          const operator = path.node.operator;
          if (OPERATIONS.includes(operator)) {
            // 調(diào)用方法替換節(jié)點(diǎn)
            path.replaceWith(
              // 傳入 operator left right
              operationTemp({
                LEFT: path.node.left,
                RIGHT: path.node.right,
                OPERATION: OPERATIONS_MAP[operator]
              })
            )
          }
        }
      }
    }
  }
}

打包 & 發(fā)布 NPM

當(dāng)開發(fā)完成后,我們先 npm run build進(jìn)行打包

然后運(yùn)行 npm publish 發(fā)布到 NPM 上

圖片圖片

項(xiàng)目使用

首先安裝 babel-plugin-sx-accuracy

npm i babel-plugin-sx-accuracy

只需要在項(xiàng)目中的 .babelrc 或者 babel.config.js 中加入 babel-plugin-sx-accuracy即可

{
  "presets": ["@babel/preset-env"],
  "plugins": ["babel-plugin-sx-accuracy"]
}

我們來試試,一開始代碼是

console.log(0.1 + 0.2)
console.log(0.3 - 0.1)
console.log(0.2 * 0.1)
console.log(0.3 / 0.1)

打包后我們看看產(chǎn)物,并且輸出的也都是沒有精度丟失的結(jié)果?。?!

圖片圖片

責(zé)任編輯:武曉燕 來源: 前端之神
相關(guān)推薦

2021-06-08 22:43:07

IPC方式Qt

2021-03-29 12:22:25

微信iOS蘋果

2020-10-22 12:30:33

MySQL

2023-12-18 23:09:25

開源優(yōu)化引擎

2020-03-31 08:12:25

Kafka架構(gòu)數(shù)據(jù)庫

2021-05-18 09:03:16

Gomapslice

2021-12-06 08:30:49

SpringSpring Bean面試題

2020-04-14 13:32:56

@Transacti失效場景

2020-09-24 09:08:04

分布式系統(tǒng)架構(gòu)

2021-03-01 18:52:39

工具在線瀏覽器

2022-05-24 11:50:46

延時消息分布式

2020-10-21 06:39:21

CPU寄存器架構(gòu)

2020-07-08 07:45:44

OAuth2.0授權(quán)

2024-01-29 00:29:49

通信技術(shù)行業(yè)

2022-08-14 15:40:55

表情DIY

2021-01-04 11:23:21

手機(jī)無線電通訊

2024-03-26 09:42:27

分片算法應(yīng)用

2024-03-28 12:52:00

AI模型

2020-04-16 12:42:42

附近的人共享單車App

2020-08-12 09:55:07

附近的人數(shù)據(jù)庫MySQL
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號