背景
我們代碼采用的是webpack的微前端架構鲫咽,但是由于代碼越來越多,巨石應用越來越大,本地啟動編譯時間需要10s分尸,所以決定升級本地啟動環(huán)境為vite锦聊,提升開發(fā)效率。但是由于是微前端架構寓落,升級過程中遇到了很多問題括丁,逐個分析解決。
核心原理
Vite 是一種現(xiàn)代化的前端開發(fā)構建工具伶选,它的核心原理和特性可以歸納為以下幾個方面:
基于 ES 模塊的開發(fā)模式:Vite 通過基于 ES 模塊的開發(fā)模式史飞,可以讓開發(fā)者在開發(fā)過程中以更加自然的方式編寫代碼。開發(fā)者可以通過 import/export 語句來引用其他模塊仰税,而無需使用其他工具將這些模塊打包在一起构资。
快速的冷啟動:Vite 采用了一種“按需編譯”的策略,只會在需要時編譯文件陨簇,這樣就避免了像其他打包工具一樣需要一次性編譯所有的文件吐绵,導致啟動時間較長的問題。因此河绽,Vite 的冷啟動速度非臣旱ィ快,可以顯著提高開發(fā)效率耙饰。
開發(fā)模式下的緩存:在Vite開發(fā)模式下纹笼,當一個模塊被第一次引用時,Vite會將該模塊的依賴關系分析并緩存到
.vite/deps
目錄下苟跪。這樣在下一次重新編譯時廷痘,Vite就可以直接使用緩存的依賴關系,Vite可以減少重復的依賴分析和編譯操作件已,從而加快編譯速度笋额。同時,由于緩存的依賴關系是按照模塊路徑組織的篷扩,因此Vite還可以檢測到依賴關系的變化兄猩,并在需要時自動更新緩存。輕量快速的熱重載HMR:Vite 采用了一種基于原生 ES 模塊系統(tǒng)的高效的熱更新機制(HMR)瞻惋,可以在開發(fā)過程中快速地更新代碼厦滤,并在瀏覽器中實時地查看更新效果,而無需刷新頁面歼狼。這也大大提高了開發(fā)效率掏导。
支持多種開發(fā)語言:Vite 不僅支持 JavaScript 和 TypeScript,還支持 Vue羽峰、React趟咆、Svelte 等多種前端開發(fā)框架添瓷,可以讓開發(fā)者在開發(fā)過程中更加靈活。
總之值纱,Vite 的核心原理是通過基于 ES 模塊的開發(fā)模式鳞贷、快速的冷啟動、高效的 HMR 以及對多種開發(fā)語言的支持虐唠,來提高前端開發(fā)的效率搀愧。
微前端改造
1、vite-plugin-federation 替換ModuleFederationPlugin
由于我們代碼使用的是微前端框架疆偿,主要使用webpack的ModuleFederationPlugin來實現(xiàn)各個模塊之間的獨立咱筛,升級為vite時我們首先采用的是使用針對vite的插件vite-plugin-federation 來進行整體升級改造。
vite-plugin-federation的使用方式與Webpack 5中的模塊聯(lián)邦相似杆故,通過配置來定義不同子應用程序之間的依賴關系和公共模塊迅箩,從而實現(xiàn)代碼的復用和分發(fā)。在實際應用中处铛,可以將一個大型的應用程序拆分成多個獨立的子應用程序饲趋,通過這種方式實現(xiàn)微服務架構和多人合作開發(fā)等需求。
但是在改造過程中發(fā)現(xiàn)撤蟆,該plugin實現(xiàn)的核心原理是各個模塊之間的remote和exposes需要先build奕塑,build的時候相當于使用rollup進行打包編譯,這樣就失去了vite本身最難得的快的屬性家肯。所以最后我們決定放棄使用該插件爵川。
2、采取本地開發(fā)模式摒棄微前端多個server模式息楔,準備通過alias將多個子模塊合并成一個子模塊。
核心原理:rewrite文件間/模塊間的引用路徑扒披,本地開發(fā)只需要單個vite server
1)alias方案初期嘗試過程中遇到兩個卡點
- 子模塊之間互相調(diào)用值依,通過commonLibs,commonApp這種別名進行引用碟案,我們需要識別出文件的真正路徑愿险,這個主要是通過原模塊中導出的remotes文件導出的exposes,能找到真正的路徑价说。
[圖片上傳失敗...(image-f35d07-1705649743183)
[圖片上傳失敗...(image-d89aea-1705649639438)]
解決方案:vite提供的resolve的alias配置中支持customResolver辆亏,其中的id和importer可以提供一些自定義的方案,id是代碼中要解析的from后邊的path鳖目,importer是該文件的目錄扮叨。所以我們可以先根據(jù)id去exposes中找到最終的路徑,這樣就可以解析這種子模塊的引用方式领迈。
[圖片上傳失敗...(image-cb3d7f-1705649639438)]
- 各個子模塊的app開頭的引用一般都指向自己的子模塊的src彻磁,我們改成一個server之后碍沐,需要識別app所屬的模塊,并指向?qū)膕rc位置衷蜓。
[圖片上傳失敗...(image-6fe9c-1705649639438)]
解決方案:從importer中提取出所屬模塊累提,然后把app替換成該子模塊下的src路徑
[圖片上傳失敗...(image-135c0e-1705649639438)]
2)遇到的問題和解決方案
vite模式下子模塊中的node_modules的引用會優(yōu)先取離他最近的node_modules中去取,所以由于該特性無需處理npm包的alias磁浇。
connected-react-router問題斋陪,啟動過程中報錯了,查一下原因是connected-react-router和react-router置吓、react-dom的兼容性問題无虚,由于connected-react-router缺少維護,和路由版本兼容要求高交洗,因此解決方案是下掉connected-react-router骑科,下掉connected-react-router的過程中需要注意幾個點,后續(xù)大家也不要用這種方式去使用了构拳。
[圖片上傳失敗...(image-6a206d-1705649639438)]
下線的過程中發(fā)現(xiàn)報了一個錯咆爽,是使用useHistory或者useLocation的時候,Context找不到置森。一開始以為是單例問題斗埂,嘗試了很多解決方案,只保留一個模塊的這個npm包還是不行凫海,排除單例問題呛凶,后來發(fā)現(xiàn)這個文件報錯位置在react-router里,但我們由于下掉connected-react-router的過程中行贪,BrowserRouter使用的是react-router-dom的漾稀,Context也是通過react-router-dom下發(fā)的,但我們代碼里使用的useHistory等是引入的react-router,因此改掉了這個引用就解決了問題建瘫。
[圖片上傳失敗...(image-fe816-1705649639438)]
- 下掉最外層的入口
[圖片上傳失敗...(image-8eb8de-1705649639438)]
- 下掉路由對history的注入和監(jiān)聽崭捍;注意要同時下掉state.router的取用邏輯。
[圖片上傳失敗...(image-f7212a-1705649639438)]
[圖片上傳失敗...(image-527cdb-1705649639438)]
[圖片上傳失敗...(image-b02b9c-1705649639438)]
- 下掉dispatch(puh(url))的使用啰脚;采用useHistory進行跳轉(zhuǎn)殷蛇。
[圖片上傳失敗...(image-351304-1705649639438)]
- alias副作用導致的單例問題。
vite中alias相對路徑會把文件打到有后綴的文件橄浓,我們alias的子模塊的替換由于exposes中披露了文件后綴粒梦,所以也會打到有后綴的文件,但是app的alias打到的是無后綴的文件荸实,這樣就會生成兩份匀们,對于使用context這樣全局數(shù)據(jù)的來說,就會由于無法保證單例導致context數(shù)據(jù)清空的bug泪勒。
這邊把用sharedInfo的context的引用路徑改成了相對路徑昼蛀,或者加上.js后綴即可宴猾。
還有app/utils/flags的引用也要加上后綴或者使用相對路徑。
[圖片上傳失敗...(image-ef7fe2-1705649639438)]
對大部分的app引用沒有影響叼旋,只是影響這種需要保證單實例的仇哆。
[圖片上傳失敗...(image-e5e100-1705649639438)]
[圖片上傳失敗...(image-353150-1705649639438)]
- 微前端模塊中除了alias引入的非單實例問題,也有一些模塊需要單實例夫植,以前維護在sharedInfo里去保證讹剔,如果有多個npm包的話,在vite中自身支持導出的是一個模塊详民,來保證單實例問題延欠,從vite的預構建輸出結(jié)果可以看出。
在 Vite 中沈跨,不同的 Node.js 模塊(node_modules
目錄下的不同包)可以共享一個單例模式的包由捎,這是通過 Vite 的模塊解析器和緩存機制來實現(xiàn)的。具體來說饿凛,Vite 會對每個模塊進行緩存狞玛,當?shù)谝淮握埱笠粋€模塊時,Vite 會解析該模塊及其依賴項涧窒,然后將其保存在內(nèi)存中心肪,下次請求該模塊時,會直接從緩存中讀取纠吴,而不是重新解析和構建硬鞍。
在 Vite 中,當多個模塊引用同一個依賴時戴已,Vite 會將這些模塊中的依賴項映射到同一個緩存中的實例固该,從而實現(xiàn)了單例模式。這樣做可以提高應用程序的性能糖儡,減少內(nèi)存占用和加載時間蹬音。
需要注意的是,Vite 的單例模式只是在開發(fā)模式下生效休玩。在生產(chǎn)模式下,由于 Vite 會對依賴項進行代碼拆分和壓縮劫狠,因此每個模塊都會引用自己的依賴項的獨立實例拴疤,這樣可以減小應用程序的大小并提高性能。
[圖片上傳失敗...(image-f4f425-1705649639438)]
[圖片上傳失敗...(image-b41d46-1705649639438)]
- mockService中間件
vite也支持啟動服務時做一些mock服務独泞,具體改造方式如下
[圖片上傳失敗...(image-eecaf4-1705649639438)]
- css注入變量
啟動過程中發(fā)生如下報錯呐矾,是因為less的一些全局變量沒有注入,增加vite配置注入即可懦砂。
[圖片上傳失敗...(image-3c3d00-1705649639438)]
[圖片上傳失敗...(image-a03728-1705649639438)]
- 靜態(tài)資源解析
代碼中引入了一些html或者docx結(jié)尾的靜態(tài)資源蜒犯,啟動過程中會報錯组橄,增加靜態(tài)資源的配置即可。
[圖片上傳失敗...(image-fcac48-1705649639438)]
[圖片上傳失敗...(image-396fbe-1705649639438)]
- 全局變量注入
可以通過define配置注入一些全局變量罚随。
[圖片上傳失敗...(image-8c14b4-1705649639438)]
- babel插件問題
react插件中支持babel的一些presets和plugin玉工,但是有一些會和react插件內(nèi)置的沖突,要注意避坑淘菩,比如之前我就把原來代碼庫中的babel配置直接抄過來傳入了react插件中遵班,結(jié)果就報錯了。比如presets中的@babel/preset-env和@babel/preset-react就會和react插件有沖突潮改。
[圖片上傳失敗...(image-e5d4a8-1705649639438)]
[圖片上傳失敗...(image-75e027-1705649639438)]
效果評估
現(xiàn)狀
[圖片上傳失敗...(image-f95bf0-1705649639438)]
改進后指標
冷啟動:8s左右
熱更新:保存即更新狭郑,幾乎實時更新,不需要刷新和等待時長汇在。