首頁 > 軟體

no-bundle 構建原理淺析

2022-08-16 18:04:01

為什麼需要構建工具?

  • 處理其他型別檔案使其能被瀏覽器正常載入 —— 許多其他型別的檔案需要編譯處理為 ES6 模組才能被瀏覽器正常載入(JSX、Vue、TS、CSS、Image 等)。
  • 解決參照路徑的問題 —— 許多第三方依賴包在通過第三方 URL 參照時,不僅過程煩瑣,而且往往難以進行靈活的版本控制與更新,因此需要構建工具來解決這類問題。
  • 為開發提供輔助工具 —— 對於現實中的專案開發而言,一些便利的輔助開發技術,例如熱更新、sourceMap 等還是需要由構建工具來提供。

什麼是無包構建

它的構建方式是:

  • 在構建時只需處理模組的編譯而無須打包,把模組間的相互依賴關係完全交給瀏覽器來處理。
  • 瀏覽器會載入入口模組,分析依賴後,再通過網路請求載入被依賴的模組。

這種通過瀏覽器原生的模組進行解析的方式又稱為 Native-ESM(Native ES Module)。

//./src/index.html
...
<!-- 注意: type="module" -->
<script type="module" src="./modules/foo.js"></script>
...
//.src/modules/foo.js
import { bar } from './bar.js'
import { appendHTML } from './common.js'
...
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.15/slice.js').then((module) => {...})

基於瀏覽器的 JS 模組載入功能

HTML 中的 Script 參照注意點:

  • 入口模組檔案在頁面中參照時需要帶上 type="module" 屬性。
  • 帶有 type="module" 屬性的 script在瀏覽器中通過 defer 的方式非同步執行(非同步下載,不阻塞 HTML,順次執行),即使是行內的 script 程式碼也遵循這一原則(而普通的行內 script 程式碼則忽略 defer 屬性)。
  • 帶有 type="module" 屬性且帶有 async 屬性的 script,在瀏覽器中通過 async 的方式非同步執行(非同步下載,不阻塞 HTML,按該模組和所依賴的模組下載完成的先後順序執行,無視 DOM 中的載入順序),即使是行內的 script 程式碼,也遵循這一原則(而普通的行內 script 程式碼則忽略 async 屬性)。
  • 即使多次載入相同模組,也只會執行一次。

模組內依賴的參照

  • 只能使用 import ... from '...' 的 ES6 風格的模組匯入方式,或者使用 import(...).then(...) 的 ES6 動態匯入方式,不支援其他模組化規範的參照方式(例如 require、define 等)。
  • 匯入的模組只支援使用相對路徑('/xxx', './xxx', '../xxx')和 URL 方式('https://xxx', 'http://xxx')進行參照,不支援直接使用包名開頭的方式('xxxx', 'xxx/xxx')。
  • 只支援參照MIME Type為 text/javascript 方式的模組,不支援其他型別檔案的載入(例如 CSS 等)。

無包構建工具的介紹:

Vite

Vite 是 Vue 框架的作者尤雨溪最新推出的基於 Native-ESM 的 Web 構建工具。

在開發環境下基於 Native-ESM 處理構建過程,只編譯不打包,在生產環境下則基於 Rollup 打包。

Vite對匯入模組的解析

對 HTML 檔案的預處理

啟動 Vite 時,會通過 serverPluginHtml.ts 注入 /vite/client 執行時的依賴模組,該模組用於處理熱更新,以及提供更新 CSS 的方法 updateStyle。

對外部依賴包的解析

  • resolver.ts 負責找到對應在 node_modules 中的真實依賴包程式碼(Vite 會在啟動服務時對專案 package.json 中的 dependencies 做預處理讀取並存入快取目錄 node_modules/.vite_opt_cache 中)。
  • serverPluginModuleRewrite.ts 負責把原始碼中的 bare modules 加上 /@module/ 字首。
  • serverPluginModuleResolve.ts 負責解析加上字首後的模組。

對 Vue檔案的解析

對 Vue 檔案的解析是通過 serverPluginVue.ts 處理的,分離出 Vue 程式碼中的 script/template/style 程式碼片段,並分別轉換為 JS 模組,然後將 template/style 模組的 import寫到script 模組程式碼的頭部。

對 CSS 檔案的解析

對 CSS 檔案的解析是通過 serverPluginCSS.ts 處理的,解析過程主要是將 CSS 檔案的內容轉換為下面的 JS 程式碼模組,其中的 updateStyle 由注入 HTML 中的 /vite/client 模組提供

import { updateStyle } from "/vite/client"
const css = "..."
updateStyle(""..."", css) // id, cssContent
export default css

Vite 中的其他輔助功能

  • 多框架:除了在預設的 Vue 中使用外,還支援在 React 和 Preact 專案中使用。
  • 熱更新(HMR) :預設提供的 3 種框架的腳手架模板中都內建了 HMR 功能,同時也提供了 HMR 的 API 供第三方外掛或專案程式碼使用。
  • 自定義組態檔:支援使用自定義組態檔來細化構建設定,設定項功能參考 config.ts。
  • HTTPS 與 HTTP/2:支援使用 --https 啟動引數來開啟使用 HTTPS 和 HTTP/2 協定的開發伺服器。
  • 服務代理:在自定義設定中支援設定代理,將部分請求代理到第三方服務。
  • 模式與環境變數:支援通過 mode 來指定構建模式為 development 或 production。相應模式下自動讀取 dotenv 型別的環境變陣列態檔(例如 .env.production.local)。
  • 生產環境打包:生產環境使用 Rollup 進行打包,支援傳入自定義設定,設定項功能參考 build/index.ts。

Vite 的使用限制

  • 面向支援 ES6 的現代瀏覽器,在生產環境下,編譯目標引數 esBuildTarget 的預設值為 es2019,最低支援版本為 es2015(因為內部會使用 esbuild 處理編譯壓縮,用來獲得最快的構建速度)。
  • 對 Vue 框架的支援目前僅限於最新的 Vue 3 版本,不相容更低版本。

Snowpack

  • 從整體功能來說和上述 Vite工具提供的功能大致相同。
  • Snowpack 在生產環境下預設使用無包構建而非打包模式。

與 Vite 相同的功能點

兩者都支援各種程式碼轉換載入器、熱更新、環境變數(需要安裝 dotenv 外掛)、服務代理、HTTPS 與 HTTP/2 等。

與 Vite 的差異點

  • 相同的功能,實現細節不同: 例如對 Bare Module 的處理,除了轉換後字首名稱不同外(Vite 使用 /@module/ 字首,而 Snowpack 使用 /web_modules/ 字首)
  • 工具穩定性
  • 外掛體系: 除了版本差異外,Snowpack 提供了較完善的外掛體系,支援使用者和社群釋出自定義外掛。
  • 打包工具:在生產環境下,Vite 使用 Rollup 作為打包工具,而 Snowpack 則需要引入外掛來實現打包功能,官方支援的打包外掛有 @snowpack/plugin-webpack 和 @snowpack/plugin-parcel,暫未提供 Rollup 對應的外掛。
  • 特殊優化:Vite 中內建了對 Vue 的大量構建優化,因此對 Vue 專案而言,選擇 Vite 通常可以獲得更好的開發體驗。

無包構建 VS 打包構建

無包構建的優點

  • 初次構建啟動快: 無包構建流程中,模組依賴分析與編譯都是在瀏覽器渲染頁面時非同步處理的
  • 按需編譯:在瀏覽器渲染時,根據入口模組分析載入所需模組,編譯過程按需處理,因此相比之下處理內容更少,速度也會更快。
  • 增量構建速度快:rebuild 過程中,只需處理編譯單個模組。

無包構建的缺點

  • 瀏覽器網路請求數量劇增: 無包構建最主要面對的問題是,它的執行模式決定了在一般專案裡,渲染頁面所需發起的請求數遠比打包構建要多得多,使得開啟頁面會產生瀑布式的大量網路請求,將對頁面的渲染造成延遲。這也是 Vite 在開發環境下才使用無包構建,在生產環境下則仍舊使用打包構建的原因吧。
  • 瀏覽器的相容性:

以上就是no-bundle 構建原理淺析的詳細內容,更多關於no-bundle 構建原理的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com