在微前端qiankun中使用Vite你踩坑了嗎?

哈嘍,我是樹(shù)醬。之前搭建的微前端體系已經(jīng)穩(wěn)步運(yùn)行將近兩年了,最近遇到一些童鞋反饋。之前據(jù)說(shuō)qiankun并不支持Vite打包的應(yīng)用,那是不是我就無(wú)法使用了?
是的,官方暫未有文檔表明已經(jīng)支持Vite。接下來(lái)我會(huì)從Vite聊起,然后一步步解析如何去解決在qiankun微前端體系中集成基于Vite構(gòu)建的子應(yīng)用.
1 為什么要用Vite?
在Vite沒(méi)有誕生之前,我們前端大多都是基于 webpack 構(gòu)建的,主要離不開(kāi)以下兩點(diǎn):
本地開(kāi)發(fā)(熱更新HMR) 打包上線
webpack的核心簡(jiǎn)單概括就是將各類資源打包整合在一起,形成bundle的能力。但是隨著項(xiàng)目的不斷迭代,慢慢演變成一個(gè)中大型項(xiàng)目,這時(shí)候你會(huì)發(fā)現(xiàn)打包時(shí)間太久了,換句話說(shuō)構(gòu)建效率變低了。

而在Bundle工具的演變過(guò)程中,我們見(jiàn)證了?webpack[1]、Rollup[2]?和?Parcel[3]?等工具,同時(shí)構(gòu)建效率也在逐步提升,如下圖所示??

而伴隨著瀏覽器對(duì)ES模塊(ESM)逐步支持兼容,是不是有更快的方式可以解決構(gòu)建問(wèn)題?
那就是基于瀏覽器支持的?ESM import特性實(shí)現(xiàn)的 bundless, 通過(guò)利用瀏覽器進(jìn)行模塊間依賴加載,而不需要在編譯時(shí)進(jìn)行。
換句話說(shuō)我們不再需要構(gòu)建一個(gè)完整的 Bundle(下文我們稱為:Bundless)。當(dāng)我們修改文件時(shí),瀏覽器只需要重新加載單個(gè)文件即可。

啊樂(lè)同學(xué):那有哪些 Bundless 解決方案 ?
(見(jiàn)下文)Vite就是其一,回顧下Vite的優(yōu)勢(shì):??
在開(kāi)發(fā)模式下:基于
esbuild預(yù)構(gòu)建依賴(減少HTTP請(qǐng)求) + 瀏覽器自主加載對(duì)應(yīng)的模塊,熱更新頁(yè)面!在生產(chǎn)模式下:基于
Rollup的打包,速度也有一定提升

你一旦體驗(yàn)到Vite的神速!你真的會(huì)停不下來(lái) ??
?? 飯后思考:
esbuild不是比Rollup更快嗎?生產(chǎn)模式下,為何不用esbuild構(gòu)建??? 參考答案[4]
如果是對(duì)于原生ESM不支持的瀏覽器,開(kāi)發(fā)模式咋處理??? 參考答案[5]
不是說(shuō)好bundless?為何還要用
esbuild預(yù)構(gòu)建依賴呢??? 參考答案[6]Bundless方案除了Vite之外,還有哪些??? 參考答案[7]
Vite 的目標(biāo)是要干掉 Webpack??? 參考答案[8]
2. 微前端框架qiankun與Vite
通過(guò)上文,我們了解到使用Vite的優(yōu)勢(shì)。那是否
qiankun支持基于vite構(gòu)建的子應(yīng)用集成呢?這里我們以vue3+vite的demo為例
會(huì)遇到以下兩個(gè)需要解決的問(wèn)題:
開(kāi)發(fā)模式:在開(kāi)發(fā)環(huán)境下,如果我們使用 vite 來(lái)構(gòu)建 vue3 子應(yīng)用,基于vite的構(gòu)建機(jī)制,會(huì)在子應(yīng)的
html的入口文件的script標(biāo)簽上攜帶type=module。而我們知道qiankun父應(yīng)用引入子應(yīng)用,本質(zhì)上是將html做為入口文件,并通過(guò)import-html-entry這個(gè)庫(kù)去加載子應(yīng)用所需要的資源列表Js、css,然后通過(guò)eval直接執(zhí)行,而基于vite構(gòu)建的js中import、export并沒(méi)有被轉(zhuǎn)碼,會(huì)導(dǎo)致直接報(bào)錯(cuò)(不允許在非 type=module 的 script 里面使用 import)生產(chǎn)模式:生產(chǎn)模式下,因?yàn)闆](méi)有諸如webpack中支持運(yùn)行時(shí)
publicPath,也就是__webpack_public_path__,換句話說(shuō)就是vite不支持運(yùn)行時(shí)publicPath,其主要作用是用來(lái)解決微應(yīng)用動(dòng)態(tài)載入的腳本、樣式、圖片等地址不正確的問(wèn)題。
?? 拓展閱讀:
一開(kāi)始import-html-entry會(huì)過(guò)濾掉type=module的文件,導(dǎo)致缺失js卻直接eval最終執(zhí)行出錯(cuò),后期這個(gè)問(wèn)題官方已經(jīng)支持??
Support of?type=module?and?nomodule?attribute in?import-html-entry[9]
目前qiankun官網(wǎng)文檔并沒(méi)有基于Vue3+Vite構(gòu)建的子應(yīng)用打包的文檔指引,但是我們可以在Github的Issue中找到一些解決方案,主要通過(guò)以下這兩種方式解決
2.1 只解決生產(chǎn)模式的集成
我們可以通過(guò)對(duì)子應(yīng)用vite配置的構(gòu)建配置改造來(lái)實(shí)現(xiàn)
首先修改Vite.config.js·中的build配置, 默認(rèn)Vite的輸出目標(biāo)target是module,需改為esnext

然后在配置文件中引入 @rollup/plugin-html[10]

上圖省略部分方法,詳情請(qǐng)看本節(jié)末尾的Demo實(shí)例[11],代碼實(shí)現(xiàn)的目的是為了構(gòu)建html文件作為子應(yīng)用的入口,構(gòu)建結(jié)果如下所示??

其他環(huán)節(jié)跟基于Webpack的配置大致相同,這里不一一贅述
雖然這種方式針對(duì)生產(chǎn)模式可以實(shí)現(xiàn)集成,但是存在幾個(gè)局限性:
我們知道為了讓 qiankun?拿到子應(yīng)用export的生命周期函數(shù),所以需要將子應(yīng)用打包成?umd?格式,而vite的code-splitting(代碼分割)功能并不支持iife和umd兩種格式,這會(huì)導(dǎo)致路由無(wú)法實(shí)現(xiàn)懶加載。

因?yàn)関ite不支持運(yùn)行時(shí)publicPath,只能在打包時(shí)寫死
Base配置圖片最終會(huì)被打包成
base64
更詳細(xì)的Demo集成例子:?? ?app-vue-vite[12]
2.2 解決開(kāi)發(fā)模式 + 生產(chǎn)模式的集成
單獨(dú)解決生產(chǎn)模式的集成也不方便,畢竟很多時(shí)候需要我們?cè)诒镜丨h(huán)節(jié)進(jìn)行調(diào)試,那有什么方式可以同時(shí)讓Vite支持這兩種模式的集成呢?
Github上有一名開(kāi)源作者開(kāi)發(fā)了一款Vite插件叫vite-plugin-qiankun,通過(guò)這個(gè)插件可以在qiankun下走通這兩種模式。甚至保留了vite構(gòu)建模塊的優(yōu)勢(shì)
修改 Vite.config.js

修改子應(yīng)用的 main.ts,將生命周期mount、bootstrap、unmount等通過(guò)插件函數(shù)renderWithQiankun在其中暴露完成。其他配置與基于webpack構(gòu)建的子應(yīng)用相同

? 注意事項(xiàng):
qiankun官方是以 window.__POWERED_BY_QIANKUN__來(lái)判斷當(dāng)前是否為qiankun環(huán)境下,而該插件引用之后是通過(guò)qiankunWindow.__POWERED_BY_QIANKUN__來(lái)判斷
?? 局限性:
生產(chǎn)模式下依舊不支持 publicPath, 需要將vite.config.js中base配置寫死。導(dǎo)致多環(huán)境部署不便捷。無(wú)法像在webpack結(jié)合window.INJECTED_PUBLIC_PATH_BY_QIANKUN+publicpath來(lái)解決
更詳細(xì)的Demo集成例子:?? viteapp[13]
2.3 ?Vite對(duì)runtime publicpath的支持
目前在Vite官方文檔沒(méi)查閱到相關(guān)的配置,但在Github中找到一個(gè)插件vite-plugin-dynamic-publicpath[14]。如果你有更好的解決方案,也歡迎評(píng)論區(qū)留言
2.4 關(guān)于Vite的Dotenv配置
如果你從 vue-cli 切換到Vite 需要注意 Dotenv 命名的變化
vite前綴是?VITE_?,vue-cli?是?VUE_APP_獲取方式也不一樣,在 vite是通過(guò)import.meta.env,而在?vue-cli則是通過(guò)?process.env
3.最后
如果你有其他解決方式,歡迎在評(píng)論區(qū)留言,也可以加我微信,我們一起喝茶?? 討論
你好,我是?? 樹(shù)醬,請(qǐng)你喝杯?? 記得三連哦~
1.閱讀完記得點(diǎn)個(gè)贊哦,有?? 有動(dòng)力
2.關(guān)注公眾號(hào)前端那些趣事,陪你聊聊前端的趣事
3.文章收錄在Github frontendThings 感謝Star?
參考資料
webpack: https://webpack.js.org/
[2]Rollup: https://rollupjs.org/
[3]Parcel: https://parceljs.org/
[4]?? 參考答案: https://cn.vitejs.dev/guide/why.html#why-not-bundle-with-esbuild
[5]?? 參考答案: https://cn.vitejs.dev/guide/#browser-support
[6]?? 參考答案: https://cn.vitejs.dev/guide/dep-pre-bundling.html
[7]?? 參考答案: https://cn.vitejs.dev/guide/comparisons.html
[8]?? 參考答案: https://www.zhihu.com/question/477139054/answer/2156019180
[9]Support of?type=module?and?nomodule?attribute in?import-html-entry: https://github.com/umijs/qiankun/issues/507
[10]@rollup/plugin-html: https://github.com/rollup/plugins/tree/master/packages/html
[11]Demo實(shí)例: https://github.com/gongshun/qiankun-vue-demo/blob/feature/vite-child/app-vue-vite/vite.config.ts
[12]?? ?app-vue-vite: https://github.com/gongshun/qiankun-vue-demo/tree/feature/vite-child/app-vue-vite
[13]?? viteapp: https://github.com/tengmaoqing/vite-plugin-qiankun/tree/master/example/viteapp
[14]vite-plugin-dynamic-publicpath: https://github.com/jy0529/vite-plugin-dynamic-publicpath
