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

Webpack 打包太慢?來試試 Bundleless

開發(fā) 開發(fā)工具
Webpack 將各個(gè)資源打包整合在一起形成 bundle,當(dāng)資源越來越多時(shí),打包的過程也將越來越慢。

 ????一 引言

Webpack 最初是為了解決前端模塊化以及使用 Node.Js 生態(tài)的問題而出現(xiàn),在過去的 8 年時(shí)間里,Webpack 的能力越來越強(qiáng)大。

??

??

 

但因?yàn)槎嗔舜虬鼧?gòu)建這一層,隨著項(xiàng)目的增長,打包構(gòu)建速度越來越慢,每次啟動都要等待幾十秒甚至幾分鐘,然后啟動一輪構(gòu)建優(yōu)化,隨著項(xiàng)目的進(jìn)一步增大,構(gòu)建速度又會降低,陷入不斷優(yōu)化的循環(huán)。

??

??

 

在項(xiàng)目達(dá)到一定的規(guī)模時(shí),基于 Bundle 的構(gòu)建優(yōu)化的收益變得越來越有限,無法實(shí)現(xiàn)質(zhì)的提升。我們從另一個(gè)角度思考,webpack 之所以慢,主要的原因還是在于他將各個(gè)資源打包整合在一起形成 bundle,如果我們不需要 bundle 打包的過程,直接讓瀏覽器去加載對應(yīng)的資源,我們將有可能可以跳出這個(gè)循環(huán),實(shí)現(xiàn)質(zhì)的提升。

??

??

 

在 Bundleless 的架構(gòu)下,我們不再需要構(gòu)建一個(gè)完整的 bundle,同時(shí)在修改文件時(shí),瀏覽器也只需要重新加載單個(gè)文件即可。由于沒有了構(gòu)建這一層我們將能夠?qū)崿F(xiàn)以下的目標(biāo):

  • 極快的本地啟動速度,只需要啟動本地服務(wù)。
  • 極快的代碼編譯速度,每次只需要處理單個(gè)文件。
  • 項(xiàng)目開發(fā)構(gòu)建的時(shí)間復(fù)雜度始終為 O(1),使得項(xiàng)目能夠持續(xù)保持高效的構(gòu)建。
  • 更加簡單的調(diào)試體驗(yàn),不再強(qiáng)依賴 sourcemaps 即可實(shí)現(xiàn)穩(wěn)定的單文件的 debug。

基于以上的可能性 Bundleless 將重新定義前端的本地開發(fā),讓我們重新找回前端在 10 年前修改單個(gè)文件之后,只需要刷新即可即時(shí)生效的體驗(yàn),同時(shí)疊加上前端的 HotModuleReplace 相關(guān)技術(shù),我們可以把刷新也省去,最終實(shí)現(xiàn)保存即生效。

實(shí)現(xiàn) Bundleless 一個(gè)很重要的基礎(chǔ)能力是模塊的動態(tài)加載能力,這一主要的思路會有兩個(gè):

  • System.js 之類的 ES 模塊加載器,好處是具有較高的兼容性。
  • 直接利用 Web 標(biāo)準(zhǔn)的 ESModule,面向未來,同時(shí)整體架構(gòu)也更加簡單。

在本地開發(fā)過程中兼容性的影響不是特別大,同時(shí) ESModule 已經(jīng)覆蓋了超過 90% 的瀏覽器,我們完全可以利用 ESModule 的能力讓瀏覽器自主加載需要的模塊,從而更加低成本同時(shí)面向未來實(shí)現(xiàn) Bundleless。

社區(qū)中在近一兩年也出現(xiàn)了很多基于 ESModule 的開發(fā)工具,如 Vite、Snowpack、es-dev-server 等。本文將主要分享基于瀏覽器的 ESModule 能力實(shí)現(xiàn) Bundless 本地開發(fā)的相關(guān)思路、核心技術(shù)點(diǎn)以及 Vite 的相關(guān)實(shí)現(xiàn)和在供應(yīng)鏈 POS 場景下的落地實(shí)踐。

二 從資源加載看 Bundle 和 Bundleless 的不同

下面以大家最熟悉的 create-react-app 默認(rèn)項(xiàng)目為例,從實(shí)際的頁面渲染資源的加載過程對比 Bundle 和 Bundleless 的區(qū)別。

??

??

 

基于 Webpack 的 bundle 開發(fā)模式

??

??

 

上面的圖具體的模塊加載機(jī)制可以簡化為下圖:

??

??

 

在項(xiàng)目啟動和有文件變化時(shí)重新進(jìn)行打包,這使得項(xiàng)目的啟動和二次構(gòu)建都需要做較多的事情,相應(yīng)的耗時(shí)也會增長。

基于 ESModule Bundleless 模式

??

??

 

從上圖可以看到,已經(jīng)不再有一個(gè)構(gòu)建好的 bundle、chunk 之類的文件,而是直接加載本地對應(yīng)的文件。

??

??

 

從上圖可以看到,在 Bundleless 的機(jī)制下,項(xiàng)目的啟動只需要啟動一個(gè)服務(wù)器承接瀏覽器的請求即可,同時(shí)在文件變更時(shí),也只需要額外處理變更的文件即可,其他文件可直接在緩存中讀取。

對比總結(jié)

??

??

 

Bundleless 模式可以充分利用瀏覽器自主加載的特性,跳過打包的過程,使得我們能在項(xiàng)目啟動時(shí)獲取到極快的啟動速度,在本地更新時(shí)只需要重新編譯單個(gè)文件。下面將分享如何基于瀏覽器 ESModule 的能力實(shí)現(xiàn) Bundleless 的開發(fā)。

三 如何實(shí)現(xiàn) Bundleless

如何使用 ESModule 模塊加載

實(shí)現(xiàn) Bundleless 的第一步是要讓瀏覽器自主加載對應(yīng)的模塊。

使用 type="module" 開啟 ESModule

<div id="root"></div> 
<script type="module">
// 直接在 script 標(biāo)簽中使用 type="module" 即可使用 ESModule 的方式
import React from 'https://cdn.pika.dev/react'
import ReactDOM from 'https://cdn.pika.dev/react-dom'

ReactDOM.render('Hello World', document.getElementById('root'))
</script>

利用 import-maps 支持 bare import

分享一個(gè)在 chrome 中已經(jīng)實(shí)現(xiàn)了的 import-maps 的標(biāo)準(zhǔn) ,可以讓我們直接用 import React from 'react' 這樣的寫法,未來我們可以利用此能力實(shí)現(xiàn)線上的 Bundleless 部署。

<div id="root"></div> 
<!-- 開啟 chrome://flags/#enable-experimental-productivity-features -->
<script type="importmap">
{
"imports": {
"react": "https://cdn.pika.dev/react",
"react-dom": "https://cdn.pika.dev/react-dom"
}
}
</script>
<script type="module">
// 支持 bare import
import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render('Hello World!', document.getElementById('root'))
</script>

以上我們介紹了瀏覽器中原生的 ESModule 是如何使用的。面向本地開發(fā)的場景,我們只需要啟動一個(gè)本地的 devServer 承載瀏覽器的請求映射到對應(yīng)的本地文件,同時(shí)動態(tài)地將項(xiàng)目中 import 的資源路徑指向我們的本地地址,即可讓瀏覽器直接加載本地的文件,比如可以使用下面的寫法,將入口 JS 文件直接指向本地的路徑,然后 devServer 再攔截相應(yīng)的請求返回對應(yīng)的文件。

<div id="root"></div> 
<!-- 直接指向本地路徑 -->
<script type="module" src="/src/main.jsx"></script>

如何加載非 JS 的文件資源

通過 ESModule 我們借助瀏覽器的能力實(shí)現(xiàn)了 JS 的自主加載,但實(shí)際的項(xiàng)目代碼中我們不僅僅會 import JS 文件,也會有下面的寫法:

// main.jsx 
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css' // import css 文件
import App from './App' // import jsx 文件

// 使用 JSX 語法
ReactDOM.render(<App />, document.getElementById('root'))

而瀏覽器在處理文件時(shí)是依據(jù) Content-Type 的,不關(guān)心具體的文件類型,所以我們需要在瀏覽器發(fā)起請求時(shí),將對應(yīng)的資源轉(zhuǎn)化為 ESModule 格式,同時(shí)設(shè)置對應(yīng)的 Content-Type 為 JS,返回給瀏覽器執(zhí)行,瀏覽器就會按照 JS 的語法進(jìn)行解析處理,整體的流程可見下圖:

??

??

 

以下是 Vite 的相關(guān)實(shí)現(xiàn),在請求返回的過程中,對不同的文件進(jìn)行動態(tài)處理:

??

??

 

 

如何實(shí)現(xiàn) HotModuleReplace

HotModuleReplace 能夠在我們修改代碼后,不需要刷新頁面,直接在當(dāng)前場景下生效,結(jié)合 Bundleless 極快的生效速度,我們能夠?qū)崿F(xiàn)幾乎沒有延遲的保存即生效的體驗(yàn)。對于 React,在 Webpack 場景下目前只能通過使用 react-hot-loader 來實(shí)現(xiàn),但這一塊受限于具體的實(shí)現(xiàn),有一些場景會存在 bug,作者也建議遷移到 React 團(tuán)隊(duì)實(shí)現(xiàn)的 react-refresh,而這一塊在 Webpack 中還沒有相應(yīng)的實(shí)現(xiàn)。在 Bundleless 場景下,因?yàn)槲覀兊拿總€(gè)組件都是獨(dú)立加載的,所以要集成 react-refresh,我們只需要在瀏覽器請求返回時(shí)在文件的頂部和底部加上相應(yīng)的腳本即可完成集成。

??

??

 

要完整的實(shí)現(xiàn) HotModuleReplace 會比上面畫得更加復(fù)雜,還需要有一套依賴分析機(jī)制來判斷當(dāng)一個(gè)文件發(fā)生變更之后要替換哪些文件以及是否需要 reload。在 Bundleless 的場景下,因?yàn)椴辉傩枰虬鼮橐粋€(gè)完整的 bundle,同時(shí)我們也能更加靈活地對單個(gè)文件進(jìn)行修改,這一塊相關(guān)的實(shí)現(xiàn)會更加容易。

以下是在 Vite 中的相關(guān)實(shí)現(xiàn):

??

??

 

如何優(yōu)化大量請求導(dǎo)致頁面加載慢

Bundleless 的模式不再打包,提升了啟動的速度,但對于一些有較多外部依賴或者自身文件數(shù)量較多的模塊,需要發(fā)起大量請求才能獲取到全部的資源,這個(gè)會降低開發(fā)過程中頁面加載的時(shí)間。比如下面是直接在瀏覽器中 import lodash-es 會并發(fā)出大量的請求:

??

??

 

在這一塊上我們可以做相應(yīng)的優(yōu)化,將外部的依賴提前打包成單個(gè)文件來減少在開發(fā)過程中由于外部依賴過多而發(fā)起過多的網(wǎng)絡(luò)請求。

在 Vite 的啟動流程中有一個(gè) vite optimize 的過程會自動將 package.json 中的 depenencies 借助 Rollup 打包成 ES6 Module。

??

??

 

提前打包帶來的好處除了能夠提升頁面的加載速度,借助 @rollup/plugin-commonjs 我們能夠?qū)?commonjs 的外部依賴打包為 ESModule 的形式引入,進(jìn)一步擴(kuò)大 Bundleless 的適用范圍。

四 在供應(yīng)鏈 POS 場景下落地實(shí)踐

我們團(tuán)隊(duì)負(fù)責(zé)的供應(yīng)鏈 POS 業(yè)務(wù)主要可分為面向建材家居的家裝行業(yè)和線下小店的零售行業(yè),在技術(shù)架構(gòu)上采用了各個(gè)域 bundle 獨(dú)立開發(fā),然后最終借助底層的 sdk 合并為一個(gè)大的 SPA 的形式。由于項(xiàng)目的復(fù)雜性,在日常開發(fā)過程中,有以下的一些痛點(diǎn):

  • 項(xiàng)目的啟動和耗時(shí)相對較長。
  • 改動后二次編譯時(shí)間長。
  • 缺少穩(wěn)定的 HMR 能力,開發(fā)過程中需要重復(fù)造場景。
  • debug 依賴 sourcemaps 能力,有時(shí)會出現(xiàn)不穩(wěn)定的情況。

基于以上的問題,借助 Vite 的相關(guān)實(shí)現(xiàn),我們對本地開發(fā)環(huán)境進(jìn)行了 Bundleless 的嘗試和落地,在實(shí)驗(yàn)的一些項(xiàng)目中對于本地的開發(fā)體驗(yàn)有了很大的提升。

在啟動以及修改生效的速度上帶來極大的提升

目前已實(shí)現(xiàn)單 bundle 維度的開發(fā),打包構(gòu)建速度:

??

??

 


 

 

Webpack


 

??

??

 

Vite Bundleless

從上面的可以看出,在啟動單個(gè) bundle 時(shí),Webpack 需要 10s 左右的時(shí)間,而基于 Bundleless 的 Vite 只需要 1s 左右,提升 10 倍。

??

??

 

整體的頁面加載時(shí)間在 4s 左右,仍然比 Webpack 的打包構(gòu)建時(shí)間要短,同時(shí)從上面的視頻中也可以看到 HMR 的速度達(dá)到了毫秒級的響應(yīng),實(shí)現(xiàn)了基本無感的保存即生效。

不依賴 sourcemap 調(diào)試單個(gè)文件

??

??

 

落地過程中遇到的問題和解決

在實(shí)際落地過程中,遇到的問題主要是相關(guān)模塊不符合 ESModule 規(guī)范以及一些寫法上的標(biāo)準(zhǔn)化:

  • 部分模塊沒有 ESModule 的打包。
  • less 依賴 node_modules 的寫法的規(guī)范。
  • jsx 文件后綴規(guī)范。
  • babel-runtime 的處理。

部分模塊沒有 ESModule 的打包

對于沒有 ESModule 打包輸出或者輸出的錯(cuò)誤的包,根據(jù)不同的類型使用不同的策略:

  • 內(nèi)部的包:通過升級腳手架,發(fā)布帶有 ESModule 的包的新版本。
  • 外部依賴:通過 issue、pull request 等形式,推動了 number-precision 等模塊的升級。
  • 同時(shí)有一些由于歷史原因無法打出 ESModule 的包可以借助 @rollup/plugin-commonjs 打包為 ESModule。

less 依賴 node_modules 的寫法的規(guī)范

@import '~@ali/pos-style-mixin/style/lst.less';  
// ~ 只在 webpack 中 less-loader 的支持,在原生的 less 中不支持

// 統(tǒng)一遷移為下面的模式
@import '@ali/pos-style-mixin/style/lst.less';

// 同時(shí)在原先的 webpack 構(gòu)建中的 less-laoder 中配置 lessOptions,用于最后的打包
/*
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
paths: [path.resolve(cwd, 'node_modules')],
}
}
}
*/

JSX 文件后綴規(guī)范

Vite 在運(yùn)行的過程中會依據(jù)文件不同的后綴名進(jìn)行對應(yīng)的編譯處理,而在 Webpack 模式下我們通常會將 JSX、JS 等文件都丟給 babel-loader 進(jìn)行處理,這使得有一些原本是 JSX 的文件沒有寫JSX 后綴。Vite 只會對 /\.(tsx?|jsx)$/ 的文件進(jìn)行 esbuild 編譯,對于純 JS 會直接跳過 esbuild 的過程。對于這種情況我們是逐步將錯(cuò)誤的原先沒有寫 JSX 的文件遷移為 JSX 文件。

babel-runtime 的處理

在使用了 babel-plugin-transform-runtime 之后,打包的輸出結(jié)果會是下面這樣:

??

??

 

上面所引用的 @babel/runtime/helpers/extends 是 commonjs 的格式無法直接使用,針對這個(gè)情況,有兩種解法:

1)針對內(nèi)部自己打包的模塊,可以在進(jìn)行 es6 打包時(shí)添加 useModules 配置,這樣打包出來的代碼就會是直接引用@babel/runtime/helpers/esm/extends<br />:

??

??

 

2)針對重新打包成本較高的模塊,可以通過 Vite 的插件機(jī)制進(jìn)行轉(zhuǎn)換,將 @babel/runtime/helpers 在運(yùn)行時(shí)替換為 @babel/runtime/helpers/esm 可以通過 alias 配置實(shí)現(xiàn):

??

??

 

以上是在 Vite 開發(fā)環(huán)境的遷移過程中遇到的一些問題和處理的分享,這一塊的更大范圍的落地還在進(jìn)行中。Bundleless 的落地不僅僅是為了適配 Vite 的開發(fā)模式,同時(shí)也是面向未來規(guī)范各個(gè)模塊代碼的過程,將我們的模塊進(jìn)行標(biāo)準(zhǔn)的 ESModule 化,在有新的工具和思想出現(xiàn)時(shí)可以用更低成本進(jìn)行落地。

五 直接使用 Bundleless 進(jìn)行部署的可行性

受限于網(wǎng)絡(luò)請求和瀏覽器的解析速度,對于較大型的應(yīng)用,bundle 在加載速度上還是能夠帶來較大的收益。V8 在 2018 年也給出了相關(guān)性能上的建議:在本地開發(fā)和小型的 Web 應(yīng)用中使用。在今天的場景下,隨著瀏覽器和網(wǎng)絡(luò)性能的不斷提升,結(jié)合 ServiceWorker 之類的緩存能力,網(wǎng)絡(luò)加載的影響和越來越小,對于一些不需要考慮兼容性問題的場景可以進(jìn)行內(nèi)部的嘗試,直接部署通過 ESModule 加載的代碼。

六 總結(jié)

本文主要分享了 Bundleless 架構(gòu)下,如何提升前端的研發(fā)效率、實(shí)現(xiàn)思路以及在具體業(yè)務(wù)場景下落地實(shí)踐。Bundleless 本質(zhì)上是將原先 Webpack 中模塊依賴解析的工作交給瀏覽器去執(zhí)行,使得在開發(fā)過程中代碼的轉(zhuǎn)換變少,極大地提升了開發(fā)過程中的構(gòu)建速度,同時(shí)也可以更好地利用瀏覽器的相關(guān)開發(fā)工具。

站在當(dāng)前的背景下,Web 各個(gè)領(lǐng)域 JavaScript/CSS/HTML 相關(guān)的標(biāo)準(zhǔn)都已成熟,同時(shí)瀏覽器內(nèi)核也趨于統(tǒng)一,前端工程化的核心重點(diǎn)已逐步遷移到研發(fā)提效上,而 Bundleless 的模式能夠帶來長效的啟動和 HMR 的速度,是未來的一大發(fā)展趨勢。隨著瀏覽器內(nèi)核和 Web 標(biāo)準(zhǔn)的不斷統(tǒng)一,前端的代碼可以不再打包直接運(yùn)行將成為可能,這將進(jìn)一步提高整體的研發(fā)效率。

最后非常感謝 ESModule、Vite、Snowpack 等標(biāo)準(zhǔn)和工具的出現(xiàn),讓前端的開發(fā)體驗(yàn)往前跨了一大步。

 

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2022-06-17 11:10:43

PandasPolarsPython

2020-08-25 08:03:59

測試Sharness結(jié)構(gòu)

2020-12-02 08:31:47

Elasticsear

2020-11-04 16:34:45

單元測試技術(shù)

2020-12-15 10:24:05

2012-07-03 09:38:42

前端

2018-09-05 10:43:47

MySQL診斷思路工具

2021-09-28 08:08:32

Snowpack前端工具

2021-10-12 09:52:30

Webpack 前端多進(jìn)程打包

2024-05-27 00:00:01

2018-03-20 16:20:29

LinuxChromiumLibreOffice

2021-01-27 11:36:34

代碼開發(fā)工具

2016-09-23 18:32:42

iTunesIOS 10蘋果

2022-02-10 14:23:16

WebpackJavaScript

2022-01-06 08:34:32

數(shù)據(jù)庫Spark查詢

2023-02-01 10:40:01

2021-12-16 22:02:28

webpack原理模塊化

2021-01-28 11:40:34

Dubbo異步配置

2021-06-28 05:59:17

Webpack 前端打包與工程化

2022-03-26 22:32:27

Windows 10機(jī)械硬盤SSD
點(diǎn)贊
收藏

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