偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

前端構(gòu)建新世代,Esbuild 原來還能這么玩!

開發(fā) 前端
Esbuild 是由 Figma 的 CTO 「Evan Wallace」基于 Golang 開發(fā)的一款打包工具,相比傳統(tǒng)的打包工具,主打性能優(yōu)勢(shì),在構(gòu)建速度上可以快 10~100 倍。

Hello,我是三元同學(xué)。之前停更了一段時(shí)間,因?yàn)榈昧肆鞲?,一直在家養(yǎng)病,沒來得及更新文章,跟讀者朋友們先說聲抱歉~今天給大家?guī)淼氖俏易罱鼘懙脑瓌?chuàng)文章,由于近段時(shí)間一直在研究前端構(gòu)建相關(guān)的領(lǐng)域,像 Esbuild、Vite 這些都接觸得比較多了,而且這些工具現(xiàn)在在前端圈也比較熱門,備受業(yè)界關(guān)注,因此我想我有必要把我研究過的一些東西分享給大家,希望能對(duì)你有所幫助。

什么是 Esbuild?

Esbuild 是由 Figma 的 CTO 「Evan Wallace」基于 Golang 開發(fā)的一款打包工具,相比傳統(tǒng)的打包工具,主打性能優(yōu)勢(shì),在構(gòu)建速度上可以快 10~100 倍。

架構(gòu)優(yōu)勢(shì)

1. Golang 開發(fā)

采用 Go 語言開發(fā),相比于 單線程 + JIT 性質(zhì)的解釋型語言 ,使用 Go 的優(yōu)勢(shì)在于 :

  • 一方面可以充分利用多線程打包,并且線程之間共享內(nèi)容,而 JS 如果使用多線程還需要有線程通信(postMessage)的開銷;
  • 另一方面直接編譯成機(jī)器碼,而不用像 Node 一樣先將 JS 代碼解析為字節(jié)碼,然后轉(zhuǎn)換為機(jī)器碼,大大節(jié)省了程序運(yùn)行時(shí)間。

2. 多核并行

內(nèi)部打包算法充分利用多核 CPU 優(yōu)勢(shì)。Esbuild 內(nèi)部算法設(shè)計(jì)是經(jīng)過精心設(shè)計(jì)的,盡可能充分利用所有的 CPU 內(nèi)核。所有的步驟盡可能并行,這也是得益于 Go 當(dāng)中多線程共享內(nèi)存的優(yōu)勢(shì),而在 JS 中所有的步驟只能是串行的。

3. 從零造輪子

從零開始造輪子,沒有任何第三方庫的黑盒邏輯,保證極致的代碼性能。

4. 高效利用內(nèi)存

一般而言,在 JS 開發(fā)的傳統(tǒng)打包工具當(dāng)中一般會(huì)頻繁地解析和傳遞 AST 數(shù)據(jù),比如 string -> TS -> JS -> string,這其中會(huì)涉及復(fù)雜的編譯工具鏈,比如 webpack -> babel -> terser,每次接觸到新的工具鏈,都得重新解析 AST,導(dǎo)致大量的內(nèi)存占用。而 Esbuild 中從頭到尾盡可能地復(fù)用一份 AST 節(jié)點(diǎn)數(shù)據(jù),從而大大提高了內(nèi)存的利用效率,提升編譯性能。

與 SWC 對(duì)比

速度

下面拿純 Esbuild 和 SWC 來編譯代碼,作為 Transformer 來轉(zhuǎn)換 800+ 個(gè) tsx 文件,不寫任何的 JS 膠水代碼(如 esbuild-register、esbuild-loader、swc-loader 本身為了適配相應(yīng)的宿主工具,會(huì)寫一堆 JS 膠水代碼,影響判斷)。

從這個(gè)例子可以看出,Esbuild 與 SWC 在性能上是在一個(gè)量級(jí)的,這里通過倉庫的例子 Esbuild 略快,但不排除其他例子里面 SWC 比 Esbuild 略快的場(chǎng)景。

兼容性

Esbuild 本身的限制,包括如下:

  • 沒有 TS 類型檢查
  • 不能操作 AST
  • 不支持裝飾器語法
  • 產(chǎn)物 target 無法降級(jí)到 ES5 及以下

意味著需要 ES5 產(chǎn)物的場(chǎng)景只用 Esbuild 無法勝任。

相比之下,SWC 的兼容性更好:

  • 產(chǎn)物支持 ES5 格式
  • 支持裝飾器語法
  • 可以通過寫 JS 插件操作 AST

應(yīng)用場(chǎng)景

對(duì)于 Esbuild 和 SWC,很多時(shí)候我們都在對(duì)比兩者的性能而忽略了應(yīng)用場(chǎng)景。對(duì)于前端的構(gòu)建工具來說主要有這樣幾個(gè)垂直的功能:

  • Bundler
  • Transformer
  • Minimizer

從上面的速度和兼容性對(duì)比可以看出,Esbuild 和 SWC 作為 transformer 性能是差不多的,但 Esbuild 兼容性遠(yuǎn)遠(yuǎn)不及 SWC。因此,SWC 作為 Transformer 更勝一籌。

但作為 Bundler 以及 Minimizer,SWC 就顯得捉襟見肘了,首先官方的 swcpack 目前基本處于不可用狀態(tài),Minimizer 方面也非常不成熟,很容易碰到兼容性問題。

而 Esbuild 作為 Bundler 已經(jīng)被 Vite 作為開發(fā)階段的依賴預(yù)打包工具,同時(shí)也被大量用作線上 esm CDN 服務(wù),比如esm.sh等等;作為 Minimizer ,Esbuild 也已足夠成熟,目前已經(jīng)被 Vite 作為 JS 和 CSS 代碼的壓縮工具用上了生產(chǎn)環(huán)境。

綜合來看,SWC 與 Esbuild 的關(guān)系類似于當(dāng)下的 Babel 和 Webpack,前者更適合做兼容性和自定義要求高的 Transformer(比如移動(dòng)端業(yè)務(wù)場(chǎng)景),而后者適合做 Bundler 和 Minimizer,以及兼容性和自定義要求均不高的 Transformer。

插件機(jī)制

esbuild 插件就是一個(gè)對(duì)象,里面有name和setup兩個(gè)屬性,name是插件的名稱,setup是一個(gè)函數(shù),其中入?yún)⑹且粋€(gè) build 對(duì)象,這個(gè)對(duì)象上掛載了一些鉤子可供我們自定義一些構(gòu)建邏輯。以下是一個(gè)簡(jiǎn)單的esbuild插件示例:

  1. let envPlugin = { 
  2.   name'env'
  3.   setup(build) { 
  4.      // 文件解析時(shí)觸發(fā) 
  5.     // 將插件作用域限定于env文件,并為其標(biāo)識(shí)命名空間"env-ns" 
  6.     build.onResolve({ filter: /^env$/ }, args => ({ 
  7.       path: args.path, 
  8.       namespace: 'env-ns'
  9.     })) 
  10.  
  11.     // 加載文件時(shí)觸發(fā) 
  12.     // 只有命名空間為"env-ns"的文件才會(huì)被處理 
  13.     // 將process.env對(duì)象反序列化為字符串并交由json-loader處理 
  14.     build.onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({ 
  15.       contents: JSON.stringify(process.env), 
  16.       loader: 'json'
  17.     })) 
  18.   }, 
  19.  
  20. require('esbuild').build({ 
  21.   entryPoints: ['app.js'], 
  22.   bundle: true
  23.   outfile: 'out.js'
  24.   // 應(yīng)用插件 
  25.   plugins: [envPlugin], 
  26. }).catch(() => process.exit(1)) 

使用如下:

  1. *// 應(yīng)用了env插件后,構(gòu)建時(shí)將會(huì)被替換成process.env對(duì)象* 
  2.  
  3. import { PATH } from 'env' 
  4.  
  5. console.log(`PATH is ${PATH}`) 

不過在編寫插件的時(shí)候有一些需要注意的地方:

  1. Esbuild 插件機(jī)制只可作用于 build API,而不適用于 transformAPI,這意味著 webpack 當(dāng)中的 esbuild-loader 這種只使用 Esbuild transform 功能的地方無法利用 Esbuild 的插件機(jī)制。
  2. 插件中的 filter 正則是使用 go 原生的正則實(shí)現(xiàn)的,用來過濾文件,為了不使性能過于劣化,規(guī)則應(yīng)該盡可能嚴(yán)格。同時(shí)它本身和 JS 的正則也有所區(qū)別,比如前瞻(?<=)、后顧(?=)和反向引用(\1)就不支持。
  3. 實(shí)際的插件應(yīng)該考慮到自定義緩存(減少 load 的重復(fù)開銷)、sourcemap 合并(源代碼正確映射)和錯(cuò)誤處理??梢詤⒖?Svelte plugin。

虛擬模塊支持

與 Rollup 對(duì)比

作為打包器,一般需要兩種形式的模塊,一種存在于真實(shí)的磁盤文件系統(tǒng)中,另一種并不在磁盤而在內(nèi)存當(dāng)中,也就是虛擬模塊。Rollup 本身就天然支持虛擬模塊,Vite 基于它的插件機(jī)制,也重度使用了虛擬模塊的功能,以 wasm 文件的處理為例:

  1. const wasmHelperId = '/__vite-wasm-helper' 
  2. // helper 函數(shù)實(shí)現(xiàn) 
  3. const wasmHelper = async (opts = {}, url: string) => { 
  4.   // 省略具體實(shí)現(xiàn) 
  5. export const wasmPlugin = (config: ResolvedConfig): Plugin => { 
  6.   return { 
  7.     name'vite:wasm'
  8.     resolveId(id) { 
  9.       if (id === wasmHelperId) { 
  10.         return id 
  11.       } 
  12.     }, 
  13.     async load(id) { 
  14.       if (id === wasmHelperId) { 
  15.         return `export default ${wasmHelperCode}` 
  16.       } 
  17.       if (!id.endsWith('.wasm')) { 
  18.         return 
  19.       } 
  20.       const url = await fileToUrl(id, config, this) 
  21.       // 虛擬模塊 
  22.       return ` 
  23. import initWasm from "${wasmHelperId}" 
  24. export default opts => initWasm(opts, ${JSON.stringify(url)}) 
  25.     } 
  26.   } 

但 Rollup 的虛擬模塊也有一些限制,為了與真實(shí)模塊區(qū)分開,默認(rèn)約定要在路徑前面拼上一個(gè)'\0'。這樣會(huì)對(duì)路徑產(chǎn)生一定的入侵性,直接放到瀏覽器進(jìn)行 import 會(huì)出問題(Vite 內(nèi)部也將 \0 替換成 __xx 這種形式,以免直接將 帶\0 路徑放到瀏覽器中 import):

Esbuild 中對(duì)于虛擬模塊的支持更加友好一些,直接通過 namespace 來區(qū)分真實(shí)模塊和虛擬模塊,這樣也不會(huì)有 \0 這樣 hack 操作。

編譯能力

使用 Esbuild 的虛擬模塊,可以完成很豐富的功能,除了上述插件實(shí)例中在內(nèi)存中計(jì)算出 env 的值作為模塊內(nèi)容,還可以模塊名當(dāng)做一個(gè)函數(shù)來進(jìn)行編譯,甚至可以在編譯階段實(shí)現(xiàn)函數(shù)遞歸的過程。比如這個(gè) Esbuild 插件:

  1.   name'fibo'
  2.   setup(build) { 
  3.     build.onResolve({ filter: /^fib\(\d+\)/ }, args => { 
  4.       return { path: args.path, namespace: 'fib' } 
  5.     }) 
  6.     build.onLoad({ filter: /^fib\(\d+\)/, namespace: 'fib' }, args => { 
  7.       const match = /^fib\((\d+)\)/.exec(args.path); 
  8.       n = Number(match[1]); 
  9.        
  10.       console.log(n); 
  11.       let contents = n < 2 ? `export default ${n+1}` : ` 
  12.           import n1 from 'fib(${n - 1})' 
  13.           import n2 from 'fib(${n - 2})' 
  14.           export default n1 + n2` 
  15.       return { contents } 
  16.     }) 
  17.   } 

引入這個(gè)插件,可以解析如下的 import 語句:

  1. import fib5 from 'fib(5)' 
  2.  
  3. console.log(fib5) 
  4.  
  5. // 13 

所有的模塊都是虛擬模塊,在真實(shí)文件系統(tǒng)中并不存在

另外,還能借助虛擬模塊來進(jìn)行 URL Import,支持如下的 import 代碼:

  1. import React from 'https://esm.sh/react@17' 

這也可以在插件當(dāng)中實(shí)現(xiàn),可參考示例。

落地場(chǎng)景

1. 代碼壓縮工具

Esbuild 的代碼壓縮功能非常優(yōu)秀,可以甩開傳統(tǒng)的壓縮工具一個(gè)量級(jí)以上的性能差距。Vite 在 2.6 版本也官宣在生產(chǎn)環(huán)境中直接使用 Esbuild 來壓縮 JS 和 CSS 代碼。

2. 代替 ts-node

社區(qū)已經(jīng)有了相應(yīng)的方案 esno: https://github.com/antfu/esno

  1. ts-node index.ts 
  2. // 替換為 
  3. esno hello.ts 

3. 代替 ts-jest

使用 esbuild-jest 代替ts-jest,我曾經(jīng)嘗試在某些大型包中使用 esbuild-jest 來作為 transformer,相比 ts-jest,整體大概提升 3 倍測(cè)試效率。

Github 地址:https://github.com/aelbore/esbuild-jest

4. 第三方庫 Bundler

Vite 中在開發(fā)階段使用 Esbuild 來進(jìn)行依賴的預(yù)打包,將所有用到的第三方依賴轉(zhuǎn)成 ESM 格式 Bundle 產(chǎn)物,并且未來有用到生產(chǎn)環(huán)境的打算。

同時(shí)業(yè)界也有一些平臺(tái)基于純 Esbuild 來做線上 cjs -> esm 的 CDN 服務(wù),比如 esm.sh 和 skypack:

5. 打包 Node 庫

為什么要打包 Node 庫:

  • 減少 node_modules 代碼,避免業(yè)務(wù)安裝一大堆 node_modules 的代碼,減少安裝體積
  • 提高啟動(dòng)速度,所有代碼打到一個(gè)文件,減少了大量的文件 io 操作
  • 更安全。所有代碼打包也是鎖定依賴版本的一種方式,可以避免之前出現(xiàn)的 coa 包導(dǎo)致的大面積 CI 掛掉的問題,可參考云謙的這篇文章。

這方面 Esbuild 的作用跟現(xiàn)在 vercel 團(tuán)隊(duì)出品的 ncc 差不多,但會(huì)對(duì)代碼的寫法有一些限制,無法分析動(dòng)態(tài) require 或者 import 語句含有變量的情況:

6. 小程序編譯

對(duì)于小程序的場(chǎng)景,也可以使用 Esbuild 來代替 Webpack,大大提升編譯速度,對(duì)于 AST 的轉(zhuǎn)換則通過 Esbuild 插件嵌入 SWC 來實(shí)現(xiàn),實(shí)現(xiàn)快速編譯。詳見 132 的分享 esbuild 上生產(chǎn)。

7. Web 構(gòu)建

Web 場(chǎng)景就顯得比較復(fù)雜了,對(duì)于兼容性和周邊工具生態(tài)的要求比較高,比如低瀏覽器語法降級(jí)、CSS 預(yù)編譯器、HMR 等等,如果要用純 Esbuild 來做,還需要補(bǔ)充很多能力。

之前三元同學(xué)基于 Esbuild 實(shí)現(xiàn)了一套 Web 開發(fā)腳手架 ewas,已經(jīng)在 Github 開源,并且已成功落地到我之前的小冊(cè)項(xiàng)目當(dāng)中,相比 create-react-app 啟動(dòng)速度提升了 100 倍以上(30s -> 0.3s)。倉庫地址: https://github.com/sanyuan0704/ewas。

如今 Remix 1.0 正式發(fā)布,底層使用 Esbuild 構(gòu)建,帶來了極致的性能體驗(yàn),成為 Next.js 強(qiáng)有力的競(jìng)爭(zhēng)對(duì)手。

但總體來說,目前 Esbuild 對(duì)于真實(shí)的 Web 場(chǎng)景還有很多能力不支持,還有一些硬傷,包括語法不支持降級(jí)到ES5,拆包不靈活、不支持 HMR,對(duì)于真正能作為 Webpack 一樣的構(gòu)建工具來講還有很長(zhǎng)的路要走。

 

責(zé)任編輯:姜華 來源: 三元同學(xué)
相關(guān)推薦

2024-10-28 07:10:00

scroll標(biāo)記前端網(wǎng)格布局

2022-10-31 08:47:21

人臉識(shí)別按鍵鍵盤

2022-12-06 17:30:04

2020-05-09 16:45:56

ping命令Linux

2018-01-25 14:53:20

iPhone技巧刪除照片

2021-08-29 18:13:03

緩存失效數(shù)據(jù)

2024-03-25 08:03:32

技術(shù)面試ShowMeBug協(xié)同編程

2018-10-28 17:54:00

分布式事務(wù)數(shù)據(jù)

2021-08-20 15:43:54

iPhone手機(jī)iOS

2017-11-27 12:24:02

命令行代碼指令

2021-02-07 08:13:18

@DateTimeFo@NumberFormSpring

2024-10-17 16:55:08

2017-03-07 09:49:18

存儲(chǔ)

2021-04-01 05:40:53

分庫分表數(shù)據(jù)庫MySQL

2020-08-14 08:19:25

Shell命令行數(shù)據(jù)

2020-07-29 10:00:13

MySQL雙主架構(gòu)數(shù)據(jù)庫

2020-01-07 10:35:21

QQQQ群手機(jī)QQ

2017-12-06 15:14:51

AndroidItemRecyclerVie

2025-03-20 12:33:36

2023-11-16 12:39:13

模型數(shù)據(jù)
點(diǎn)贊
收藏

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