在微前端中加載 Vite 應(yīng)用
大廠技術(shù)??高級前端??Node進(jìn)階
點擊上方?程序員成長指北,關(guān)注公眾號
回復(fù)1,加入高級Node交流群
自 2018 年 5 月 Firefox 60 發(fā)布后,所有主瀏覽器均默認(rèn)支持 ES modules。借助 ES modules 的能力,代碼可以實現(xiàn)無需構(gòu)建直接運行。
隨著 Vite 和 Snowpack 等基于 ES modules 的構(gòu)建工具的產(chǎn)生,前端隨即掀起了 ES modules 新一輪熱潮。

天下武功,無招不破,唯快不破 ?- 李小龍
Vite、Snowpack 等基于 ES modules 的構(gòu)建工具帶來了開發(fā)的極致體驗,相比傳統(tǒng)的構(gòu)建工具,這些新型的構(gòu)建工具或多或少地帶來了以下優(yōu)勢:
由于無需打包的特性,服務(wù)器冷啟動時間超快
借助 ES modules 的能力,模塊化交給瀏覽器處理(雖然目前的階段存在一個預(yù)編譯的過程)。傳統(tǒng)構(gòu)建器需要打包依賴和源碼,才能構(gòu)建整個應(yīng)用,并提供服務(wù)。
項目大小不再成為限制項目熱更新速度的因素
傳統(tǒng)構(gòu)建器在代碼更改時,需要重新構(gòu)建并載入頁面,這樣帶來的的結(jié)果是:隨著項目體積增長,構(gòu)建耗時越長。基于 ES modules 的構(gòu)建器只進(jìn)行單文件編譯,單文件更新,時間復(fù)雜度保持 O(1).
Vite 得益于原生 ES modules 的能力,大幅提升了開發(fā)時體驗。相信未來,隨著社區(qū)生態(tài)(CDN 服務(wù)、Deno)、ESM 相關(guān)標(biāo)準(zhǔn)(import-maps、import.meta)的逐步完善,以及越來越多的技術(shù)方案解決 ES modules 在瀏覽器端的相關(guān)難題(依賴瀑布,資源碎片化),前端會開啟一個無構(gòu)建的新篇章。
同時在微前端領(lǐng)域,腳本資源的打包規(guī)范向來是百花齊放(比如 singleSPA 默認(rèn)支持 SystemJs 規(guī)范,icestark 默認(rèn)支持 UMD 規(guī)范)。未來腳本資源的打包規(guī)范必定是趨于統(tǒng)一的 ES modules 規(guī)范。正是基于這兩個原因,微前端支持 ES modules 應(yīng)用的加載就成了用戶強(qiáng)訴求。
加載 ES modules 微應(yīng)用
Vite 會默認(rèn)打包出符合標(biāo)準(zhǔn)的 ES modules 的腳本資源。ES modules 資源的加載方式如下:
<script src="index.js" type="module">script>然而,在 icestark 中需要依賴微應(yīng)用導(dǎo)出 生命周期函數(shù) 來渲染微應(yīng)用。使用 js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"` shouldRemove = true }}...
Vite 默認(rèn)使用 index.html 作為入口,在解析 index.html 的過程中,會生成一個虛擬的入口文件,將腳本資源通過 import 注入進(jìn)來,也就是最終的入口文件實際上類似于下面的代碼:
import './src/main.ts';import 'polyfill';
面對這個場景,我們想到了兩種解決方案:
借助 Vite Lib 模式,修改應(yīng)用入口:
// vite.config.tsexport default defineConfig({...??build:?{????lib:?{??????entry:?'./src/main.ts',??????formats:?['es'],??????fileName:?'index'????},????rollupOptions:?{??????preserveEntrySignatures:?'exports-only'????}??},})
這種方式有個明顯的問題是:Vite 以 Lib 模式構(gòu)建出的應(yīng)用,其產(chǎn)物并不是一個完整的前端應(yīng)用(缺少 index.html),無法滿足獨立運行的條件。
通過插件修改 Vite 的這一默認(rèn)行為
通過 vite-plugin-index-html 插件,結(jié)合 Vite 的解析能力,將入口修改為靜態(tài)資源的入口。
import htmlPlugin from 'vite-plugin-index-html';// vite.config.tsexport default defineConfig({plugins: [vue(), htmlPlugin({input: './src/main.ts'})]})
ice.js Vite 模式
同時,icestark 也支持 ice.js Vite 模式快速接入。安裝或升級 build-plugin-icestark 插件,在微應(yīng)用 build.json 中配置:
{+ "vite": true,"plugins": [[: ,}]]}
即可得到正確導(dǎo)出生命周期函數(shù)的微應(yīng)用。詳細(xì)用法可參見 使用 ice.js Vite 模式。
最終效果
漸進(jìn)升級
為了解決時間上,長尾應(yīng)用升級帶來的效率問題,微前端通常是大型架構(gòu)升級所選擇的中間態(tài)(或終態(tài))方案。因此在設(shè)計加載 ES modules 方案時,需要保持這一基準(zhǔn)原則。
框架應(yīng)用可以保持現(xiàn)有的構(gòu)建方式不變(仍然可以使用 webpack 等非原生 ES modules 構(gòu)建工具),亦無需對框架應(yīng)用做任何構(gòu)建上的改造。
因此,基本可以無痛嘗試 Vite 所帶來的快感,腳踏實地地,一點點地靠近遠(yuǎn)方。
二次加載的極致體驗
通過對 ES modules 原理的探尋,可以知道 ES modules 只執(zhí)行一次。換成實際例子,也就是說當(dāng)?shù)诙螆?zhí)行相同的加載腳本時:
// icestark 第二次執(zhí)行加載腳本const { mount, unmout } = import(esModule);
瀏覽器不會重復(fù)執(zhí)行 Construction -> Instantiation -> Evaluation 的流程,而是直接返回上次模塊執(zhí)行的結(jié)果。這會導(dǎo)致一些副作用的操作(比如在 Module Conext ?下插入樣式資源,腳本資源的行為,這給我們的微應(yīng)用二次加載帶來了額外的問題),同時也帶來了極快的二次加載效果。
建立在原生 ES modules 規(guī)范下的應(yīng)用不會在短時間內(nèi)快速鋪開,很多 To C,To 商戶的業(yè)務(wù)對瀏覽器的版本仍有限制。但是,icestark 在 2.x 快一年多的發(fā)展以來,仍希望覆蓋到多樣的開發(fā)場景,提供便捷、快速地業(yè)務(wù)升級。在支持傳統(tǒng) JS bundle、UMD 規(guī)范,本文分享了 icestark 在接入 ES modules 規(guī)范微應(yīng)用的一些嘗試,希望能給開發(fā)者帶來一些新的選擇和啟發(fā)。
引用
icestark - 面向大型系統(tǒng)的微前端解決方案
proposal-dynamic-import
Vite - Next Generation Frontend Tooling
ES modules: A cartoon deep-dive
What Happens When a Module Is Imported Twice?
我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。

???“分享、點贊、在看” 支持一波??
