<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Esbuild 為什么那么快

          共 4807字,需瀏覽 10分鐘

           ·

          2021-07-04 09:44

          全文 2500 字,閱讀耗時約 10 分鐘,歡迎點贊關(guān)注轉(zhuǎn)發(fā)。

          Esbuild 是什么

          Esbuild 是一個非常新的模塊打包工具,它提供了與 Webpack、Rollup、Parcel 等工具「相似」的資源打包能力,卻有著高的離譜的性能優(yōu)勢:

          從上到下,耗時逐步上升達到數(shù)百倍的差異,這個巨大的性能優(yōu)勢使得 Esbuild 在一眾基于 Node 的構(gòu)建工具中迅速躥紅,特別是 Vite 2.0 宣布使用 Esbuild 預(yù)構(gòu)建依賴后,前端社區(qū)關(guān)于它的討論熱度迅速上升。

          那么問題來了,這是怎么做到的?我翻閱了很多資料后,總結(jié)了一些關(guān)鍵因素:

          下面展開一一細講。

          為什么快

          語言優(yōu)勢

          大多數(shù)前端打包工具都是基于 JavaScript 實現(xiàn)的,而 Esbuild 則選擇使用 Go 語言編寫,兩種語言各自有其擅長的場景,但是在資源打包這種 CPU 密集場景下,Go 更具性能優(yōu)勢,差距有多大呢?比如計算 50 次斐波那契數(shù)列,JS 版本:

          function fibonacci(num{
              if (num < 2) {
                  return 1
              }
              return fibonacci(num - 1) + fibonacci(num - 2)
          }

          (() => {
              let cursor = 0;
              while (cursor < 50) {
                  fibonacci(cursor++)
              }
          })()

          Go 版本:

          package main

          func fibonacci(num int) int{
              if num<2{
                  return 1
              }
              
              return fibonacci(num-1) + fibonacci(num-2)
          }

          func main(){
              for i := 0; i<50; i++{
                  fibonacci(i)
              }
          }

          JavaScript 版本執(zhí)行耗時大約為 「332.58s」,Go 版本執(zhí)行耗時大約為 「147.08s」,兩者相差約 「1.25」 倍,這個簡單實驗并不能精確定量兩種語言的性能差別,但感官上還是能明顯感知 Go 語言在 CPU 密集場景下會有更好的性能表現(xiàn)。

          歸根到底,雖然現(xiàn)代 JS 引擎與10年前相比有巨大的提升,但 JavaScript 本質(zhì)上依然是一門解釋型語言,JavaScript 程序每次執(zhí)行都需要先由解釋器一邊將源碼翻譯成機器語言,一邊調(diào)度執(zhí)行;而 Go 是一種編譯型語言,在編譯階段就已經(jīng)將源碼轉(zhuǎn)譯為機器碼,啟動時只需要直接執(zhí)行這些機器碼即可。

          這種語言層面的差異在打包場景下特別突出,說的夸張一點,JavaScript 運行時還在解釋代碼的時候,Esbuild 已經(jīng)在解析用戶代碼;JavaScript 運行時解釋完代碼剛準備啟動的時候,Esbuild 可能已經(jīng)打包完畢,退出進程了!

          所以在編譯運行層面,Go 前置了源碼編譯過程,相對 JavaScript 邊解釋邊運行的方式有更高的執(zhí)行性能。

          多線程優(yōu)勢

          Go 天生具有多線程運行能力,而 JavaScript 本質(zhì)上是一門單線程語言,直到引入 WebWorker 規(guī)范之后才有可能在瀏覽器、Node 中實現(xiàn)多線程操作。

          我曾經(jīng)研讀過 Rollup、Webpack 的代碼,就我熟知的范圍內(nèi)兩者均未使用 WebWorker 提供的多線程能力。反觀 Esbuild,它最核心的賣點就是性能,它的實現(xiàn)算法經(jīng)過非常精心的設(shè)計,盡可能飽和地使用各個 CPU 核,特別是打包過程的解析、代碼生成階段已經(jīng)實現(xiàn)完全并行處理。

          除了 CPU 指令運行層面的并行外,Go 語言多個線程之間還能共享相同的內(nèi)存空間,而 JavaScript 的每個線程都有自己獨有的內(nèi)存堆。這意味著 Go 中多個處理單元,例如解析資源 A 的線程,可以直接讀取資源 B 線程的運行結(jié)果,而在 JavaScript 中相同的操作需要調(diào)用通訊接口 woker.postMessage 在線程間復(fù)制數(shù)據(jù)。

          所以在運行時層面,Go 擁有天然的多線程能力,更高效的內(nèi)存使用率,也就意味著更高的運行性能。

          節(jié)制

          對,沒錯,節(jié)制!

          Esbuild 并不是另一個 Webpack,它僅僅提供了構(gòu)建一個現(xiàn)代 Web 應(yīng)用所需的最小功能集合,未來也不會大規(guī)模加入我們業(yè)已熟悉的各類構(gòu)建特性。最新版本 Esbuild 的主要功能特性有:

          • 支持 js、ts、jsx、css、json、文本、圖片等資源
          • 增量更新
          • Sourcemap
          • 開發(fā)服務(wù)器支持
          • 代碼壓縮
          • Code split
          • Tree shaking
          • 插件支持

          可以看到,這份列表中支持的資源類型、工程化特性非常少,甚至并不足以支撐一個大型項目的開發(fā)需求。在這之外,官網(wǎng)明確聲明未來沒有計劃支持如下特性:

          • Elm, Svelte, Vue, Angular 等代碼文件格式
          • Ts 類型檢查
          • AST 相關(guān)操作 API
          • Hot Module Replace
          • Module Federation

          而且,Esbuild 所設(shè)計的插件系統(tǒng)也無意覆蓋以上這些場景,這就意味著第三方開發(fā)者無法通過「插件」這種無侵入的方式實現(xiàn)上述功能,emmm,可以預(yù)見未來可能會出現(xiàn)很多魔改版本。

          Esbuild 只解決一部分問題,所以它的架構(gòu)復(fù)雜度相對較小,相對地編碼復(fù)雜度也會小很多,相對于 Webpack、Rollup 等大一統(tǒng)的工具,也自然更容易把性能做到極致。節(jié)制的功能設(shè)計還能帶來另外一個好處:完全為性能定制的各種附加工具。

          定制

          回顧一下,在 Webpack、Rollup 這類工具中,我們不得不使用很多額外的第三方插件來解決各種工程需求,比如:

          • 使用 babel 實現(xiàn) ES 版本轉(zhuǎn)譯
          • 使用 eslint 實現(xiàn)代碼檢查
          • 使用 TSC 實現(xiàn) ts 代碼轉(zhuǎn)譯與代碼檢查
          • 使用 less、stylus、sass 等 css 預(yù)處理工具

          我們已經(jīng)完全習慣了這種方式,甚至覺得事情就應(yīng)該是這樣的,大多數(shù)人可能根本沒有意識到事情可以有另一種解決方案。Esbuild 起了個頭,選擇完全!完全重寫整套編譯流程所需要用到的所有工具!這意味著它需要重寫 js、ts、jsx、json 等資源文件的加載、解析、鏈接、代碼生成邏輯。

          開發(fā)成本很高,而且可能被動陷入封閉的風險,但收益也是巨大的,它可以一路貫徹原則,以性能為最高優(yōu)先級定制編譯的各個階段,比如說:

          • 重寫 ts 轉(zhuǎn)譯工具,完全拋棄 ts 類型檢查,只做代碼轉(zhuǎn)換
          • 大多數(shù)打包工具把詞法分析、語法分析、符號聲明等步驟拆解為多個高內(nèi)聚低耦合的處理單元,各個模塊職責分明,可讀性、可維護性較高。而 Esbuild 則堅持性能第一原則,不惜采用反直覺的設(shè)計模式,將多個處理算法混合在一起降低編譯過程數(shù)據(jù)流轉(zhuǎn)所帶來的性能損耗
          • 一致的數(shù)據(jù)結(jié)構(gòu),以及衍生出的高效緩存策略,下一節(jié)細講

          這種深度定制一方面降低了設(shè)計成本,能夠保持編譯鏈條的架構(gòu)一致性;一方面能夠貫徹性能第一的原則,確保每個環(huán)節(jié)以及環(huán)節(jié)之間交互性能的最優(yōu)。雖然伴隨著功能、可讀性、可維護性層面的的犧牲,但在編譯性能方面幾乎做到了極致。

          結(jié)構(gòu)一致性

          上一節(jié)我們講到 Esbuild 選擇重寫包括 js、ts、jsx、css 等語言在內(nèi)的轉(zhuǎn)譯工具,所以它更能保證源代碼在編譯步驟之間的結(jié)構(gòu)一致性,比如在 Webpack 中使用 babel-loader 處理 JavaScript 代碼時,可能需要經(jīng)過多次數(shù)據(jù)轉(zhuǎn)換:

          • Webpack 讀入源碼,此時為字符串形式
          • Babel 解析源碼,轉(zhuǎn)換為 AST 形式
          • Babel 將源碼 AST 轉(zhuǎn)換為低版本 AST
          • Babel 將低版本 AST generate 為低版本源碼,字符串形式
          • Webpack 解析低版本源碼
          • Webpack 將多個模塊打包成最終產(chǎn)物

          源碼需要經(jīng)歷 string => AST => AST => string => AST => string ,在字符串與 AST 之間反復(fù)橫跳。

          而 Esbuild 重寫大多數(shù)轉(zhuǎn)譯工具之后,能夠在多個編譯階段共用相似的 AST 結(jié)構(gòu),盡可能減少字符串到 AST 的結(jié)構(gòu)轉(zhuǎn)換,提升內(nèi)存使用效率。

          總結(jié)

          單純從編譯性能的維度看,Esbuild 確實完勝世面上所有打包框架,差距甚至能在百倍之大:


          耗時性能差異速度產(chǎn)物大小
          Esbuild0.11s1x1198.5 kloc/s0.97mb
          Esbuild (1 thread)0.40s4x329.6 kloc/s0.97mb
          webpack 419.14s174x6.9 kloc/s1.26mb
          parcel 122.41s204x5.9 kloc/s1.56mb
          webpack 525.61s233x5.1 kloc/s1.26mb
          parcel 231.39s285x4.2 kloc/s0.97mb

          但這是有代價的,刨除語言層面的天然優(yōu)勢外,在功能層面它直接放棄對 less、stylus、sass、vue、angular 等資源的支持,放棄 MF、HMR、TS 類型檢查等功能,正如作者所說:

          ?

          This will involve saying "no" to requests for adding major features to esbuild itself. I don't think esbuild should become an all-in-one solution for all frontend needs!

          ?

          在我看來,Esbuild 當下與未來都不能替代 Webpack,它不適合直接用于生產(chǎn)環(huán)境,而更適合作為一種偏底層的模塊打包工具,需要在它的基礎(chǔ)上二次封裝,擴展出一套既兼顧性能又有完備工程化能力的工具鏈,例如 Snowpack, Vite, SvelteKit, Remix Run 等。

          總的來說,Esbuild 提供了一種新的設(shè)計思路,值得學(xué)習了解,但對大多數(shù)業(yè)務(wù)場景還不適合直接投入生產(chǎn)使用。

          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉电影网站 | 午夜毛片 | 国产亚洲色婷婷 | 亚州无线一区欧美国产日产 | 北条麻妃办公室性爱在线观看 |