vite構(gòu)建項(xiàng)目并支持微前端
得益于 esbuild 的超高性能,vite 在誕生之初就備受關(guān)注,且一直保持著活躍的開發(fā)迭代。截至目前,vite 已經(jīng)迭代到了 2.7.10 版本,各方面也基本具備了在生產(chǎn)使用的條件。這段時間,我在項(xiàng)目中嘗試了使用 vite 進(jìn)行打包構(gòu)建,本文就是這次構(gòu)建的過程記錄。
基礎(chǔ)配置
首先使用vite 官方腳手架生成項(xiàng)目。
yarn create vite vite-demo --template react-ts
上面這行命令使用
react-ts
模板創(chuàng)建了一個叫vite-demo
的項(xiàng)目。由于我在的團(tuán)隊日常使用 react 和 typescript 開發(fā)居多,因此選擇了react-ts
這個模板,vite 官方支持的模板還有很多,可以在 create-vite 中查看。
項(xiàng)目初始化完成以后,目錄結(jié)構(gòu)如下:
. |____index.html |____.gitignore |____package.json |____tsconfig.json |____vite.config.ts |____src | |____App.tsx | |____main.tsx | |____App.css | |____index.css | |____vite-env.d.ts | |____logo.svg | |____favicon.svg
其中 vite.config.ts 內(nèi)容如下:
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()] })
可以看出,vite 官方已經(jīng)做了比較完善的封裝,相較于之前版本,開發(fā)體驗(yàn)提升了很多。
按照指示安裝完依賴,啟動應(yīng)用以后,速度確實(shí)很快?,F(xiàn)在我們來做一些基本改造。
我通常使用 less 來寫樣式,vite 已經(jīng)做了很好的支持,在安裝完依賴以后,只需要直接在代碼中引用 xxx.less
即可。對于一個久經(jīng)考驗(yàn)的開發(fā)者來說,樣式還是要引入作用域的,通常使用 css modules。
安裝 less 預(yù)處理器,
yarn add --dev less
然后修改 vite.config.ts
文件,添加 css modules 配置:
export default defineConfig({ ... css: { modules: { localsConvention: 'camelCaseOnly', // 我們使用駝峰形式 }, }, ... })
添加完配置以后,只要將原來的 xxx.less
改成 xxx.module.less
即可,這點(diǎn)與 create-react-app 是一樣的。
這里推薦一個 vscode 插件 clinyong.vscode-css-modules 可以實(shí)現(xiàn)編碼時樣式類名的智能提示,同時點(diǎn)擊樣式類名可以跳轉(zhuǎn)到樣式定義的地方,非常好用。如果在編寫樣式時使用的是中劃線形式的命名方式,比如 .xxx-container
,那么需要額外配置這個 vscode 插件,如下
{ "cssModules.camelCase": true }
這樣可以實(shí)現(xiàn)編寫樣式時使用中劃線形式,在代碼中使用的還是駝峰式的。
由于我開發(fā)的是一個中后臺項(xiàng)目,使用了 antd 和 lodash,大家都知道,這兩個是按需加載大戶,以前我們使用 babel-plugin-import 來處理,vite 生態(tài)里也有很多類似的方案。我選用了 vite-plugin-imp 這個插件,修改 vite.config.ts
如下:
import vitePluginImp from 'vite-plugin-imp'; export default defineConfig({ ... plugins: [ ... vitePluginImp({ libList: [ { libName: 'lodash', libDirectory: '', camel2DashComponentName: false, }, { libName: 'antd', style(name) { // use less return `antd/es/${name}/style/index.js`; }, }, ], }), ], css: { ... preprocessorOptions: { less: { javascriptEnabled: true, }, }, }, });
antd 已經(jīng)默認(rèn)支持了 Tree Shaking,上面的配置最終只會處理樣式的按需加載。lodash 不支持 Tree Shaking,我們也可以使用 ESM 版本 lodash-es,這樣就可以不使用 vite-plugin-imp 了,配置如下:
export default defineConfig({ resolve: { alias: [{ find: /^lodash$/, replacement: 'lodash-es', }], }, });
通常,我們在開發(fā)前端項(xiàng)目時,需要一些代理來調(diào)用后端 API 接口,vite 配置如下:
export default defineConfig({ ... server: { proxy: { '/api_path/': { target: 'http://xxx.server.domain.com/', changeOrigin: true, }, }, }, });
代理底層都是基于 http-proxy 實(shí)現(xiàn),這里不做過多說明了。
現(xiàn)在可以愉快的開發(fā)代碼了。
支持微前端構(gòu)建
因?yàn)槲覀兊闹泻笈_應(yīng)用是使用微前端(qiankun)來管理的,上面的配置,打包完成后不能被 qiankun 識別,主要原因可以看看這里,我們需要做一些額外處理。
我們知道,使用 webpack 構(gòu)建微前端是,需要添加如下三個配置項(xiàng):
{ output: { libraryTarget: 'umd', library: `${APP_NAME}-[name]`, jsonpFunction: `webpackJsonp_${APP_NAME}`, } }
在 vite 中,可以直接通過設(shè)置 build.rollupOptions.format
為 umd
來設(shè)置 UMD 規(guī)范,但是實(shí)際構(gòu)建結(jié)果卻不能被 qiankun 識別,猜想是可能跟 vite 使用 html entry 有關(guān)系。
換一個思路,我們把當(dāng)前整個應(yīng)用當(dāng)做一個 library 來構(gòu)建,輸出為 UMD 規(guī)范,然后手動寫入一個 html 文件,加載這個輸出的 JS。
修改配置如下:
export default defineConfig({ ... build: { lib: { name, entry: path.resolve(__dirname, 'src/index.tsx'), formats: ['umd'], }, }, ... })
配置完成之后,執(zhí)行 yarn build
提示如下錯誤:
UMD and IIFE output formats are not supported for code-splitting builds.
因?yàn)槲覀兊膽?yīng)用中有路由,使用了按需加載。我們將 rollup 的 inlineDynamicImports
配置打開:
export default defineConfig({ ... build: { rollupOptions: { output: { inlineDynamicImports: true, }, }, }, ... })
這樣,構(gòu)建完成之后,dist 目錄下有兩個文件 style.css
和 xxx.umd.js
。
現(xiàn)在我們要生成 index.html
了。
因?yàn)?vite 在開發(fā)態(tài)直接使用 ES Modules,是不打包的,因此生成開發(fā)態(tài)的 index.html
和生產(chǎn)的 index.html
是不同的。
我們修改項(xiàng)目根目錄下的 index.html
為:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" rel="external nofollow" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite App</title> <!-- style placeholder --> </head> <body> <div id="root"></div> <!-- script placeholder --> </body> </html>
注意當(dāng)中的兩行注釋,我們會在開發(fā)態(tài)和生產(chǎn)構(gòu)建做不同的處理。
vite 插件 API 中有一個 transformindexhtml
可以定制開發(fā)態(tài)的 html 內(nèi)容,因此,我們開發(fā)態(tài)的配置如下:
// https://vitejs.dev/config/ export default defineConfig({ ... plugins: [ ... { name: 'dev html', apply: 'serve', transformIndexHtml(indexHtml: string) { return indexHtml .replace('<!-- style placeholder -->', '') .replace('<!-- script placeholder -->', '<script type="module" src="/src/index.tsx"></script>'); }, }, ... ], });
生產(chǎn)構(gòu)建需要借助于 @rollup/plugin-html
這個插件來實(shí)現(xiàn)定制 html 內(nèi)容。
import html from '@rollup/plugin-html'; import fs from 'fs'; const entryHtml = fs.readFileSync('./index.html', { encoding: 'utf-8' }); export default defineConfig({ ... plugins: [ ... { name: 'build html', apply: 'build', ...html({ template: () => { return entryHtml .replace( '<!-- style placeholder -->', '<link rel="stylesheet" type="text/css" href="style.css" rel="external nofollow" />', ) .replace( '<!-- script placeholder -->', `<script type="text/javascript" src="${name}.umd.js"></script>`, ); }, }), }, ... ], });
通過上面的配置,再次構(gòu)建,qiankun 可以加載這個子應(yīng)用了。
其他說明
1. 老舊瀏覽器的支持
由于我這次的項(xiàng)目是中后臺項(xiàng)目,對老舊瀏覽器的支持訴求不強(qiáng)烈,就沒有在項(xiàng)目中做處理。其實(shí) vite 官方也是給了解決方案的,就是 @vitejs/plugin-legacy 這個插件。
原理也非常簡單,就是通過 <script nomodule>
來實(shí)現(xiàn)在不支持 ES Modules 的瀏覽器執(zhí)行相關(guān)腳本,同時使用 SystemJS
來加載模塊。
2. 關(guān)于 TypeScript 的說明
腳手架初始化完成以后就可以用 TypeScript 開發(fā),這里格外說明一點(diǎn),就是需要開啟編譯器選項(xiàng) isolatedModules:true
,因?yàn)?vite 使用 esbuild 處理 ts 文件,只將 ts轉(zhuǎn)換成 js 而不做類型檢查(依賴編輯器處理類型檢查,比如 vscode)。因此,當(dāng)遇到一些純類型的導(dǎo)入導(dǎo)出時,會出錯,需要開啟 isolatedModules:true
來避免這個問題。如果因?yàn)橐恍┰驘o法開啟這個選項(xiàng),則可以使用 rollup-plugin-friendly-type-imports 這個包來處理,這個包的 README 里也說明了為什么會有這樣的問題。
3. 對接 CDN
基于上面的配置構(gòu)建出來的結(jié)果,瀏覽器在加載資源的時候,都是使用的根路徑(/
)加載,如果使用 CDN 的話會出現(xiàn)資源加載 404 的問題。
我們可以配置 base
來設(shè)置基礎(chǔ)路徑,類似于 webpack 的 PUBLIC_PATH
。
export default defineConfig({ base: '/some/public/path', })
4. 構(gòu)建出錯
4.1 找不到包
報錯信息為:
[plugin: vite:dep-scan] Failed to resolve entry for package "xxx"
通常是依賴包未在 package.json 正確配置 main、module 等字段,導(dǎo)致 vite 無法找到包的入口。
可以設(shè)置通過設(shè)置別名的方式,將其映射到正確的文件上。
export default defineConfig({ resolve: { alias: [{ find: /^SOME_PACKAGE_NAME$/, replacement: 'SOME_PACKAGE_NAME/dist/xxx.es.js', }], }, });
4.2 請求超時
報錯信息為:
net::ERR_ABORTED 408 (Request Timeout)
啟動開發(fā)服務(wù)器后,瀏覽器出現(xiàn)請求超時錯誤。是因?yàn)?vite 檢測到對依賴包的請求,且該依賴尚未被 vite 處理過,這時候會會觸發(fā)預(yù)構(gòu)建,導(dǎo)致請求超時以及頁面重載。
我們可以多刷新幾次等 vite 完成預(yù)構(gòu)建,也可以將依賴加入 optimizeDeps.include
來提前處理。
4.3 導(dǎo)入模塊出錯
報錯信息為:
Internal server error: Failed to resolve import "./chunk-7L3SPMWF.js" from "node_modules/.vite/antd.js?v=7bec0e27". Does the file exist?
可能是因?yàn)橐恍┮蕾嚢敵龅母袷?vite 還不支持,可以看看這個 issue。
這個錯誤只在開發(fā)服務(wù)器運(yùn)行處理過程中存在,待頁面正常展示后就不出現(xiàn)了,忽略這個錯誤之后,目前看也沒產(chǎn)生什么影響。
小結(jié)
總體來說,vite 已經(jīng)基本具備了生產(chǎn)使用的條件。如果是常規(guī)的應(yīng)用開發(fā),vite 的配置非常簡單,可以說是開箱即用。如果需要添加額外的配置也非常方便。
目前比較大的問題是周邊生態(tài)還不是特別成熟,很多已經(jīng)成熟的包對于 vite(ES Modules)的支持比較弱。同時,如果團(tuán)隊內(nèi)基建氛圍比較濃厚的話,自己開發(fā)的工具包也要考慮這方面的問題。
到此這篇關(guān)于vite構(gòu)建項(xiàng)目并支持微前端的文章就介紹到這了,更多相關(guān)vite構(gòu)建項(xiàng)目內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue單個元素綁定多個事件問題(例如點(diǎn)擊綁定多個事件方法)
這篇文章主要介紹了vue單個元素綁定多個事件問題(例如點(diǎn)擊綁定多個事件方法),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04Vue通過moment插件實(shí)現(xiàn)獲取當(dāng)前月的第一天和最后一天
這篇文章主要介紹了Vue 結(jié)合插件moment 實(shí)現(xiàn)獲取當(dāng)前月的第一天和最后一天,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-10-10vue項(xiàng)目中v-model父子組件通信的實(shí)現(xiàn)詳解
vue.js,是一個構(gòu)建數(shù)據(jù)驅(qū)動的 web 界面的庫。Vue.js 的目標(biāo)是通過盡可能簡單的 API 實(shí)現(xiàn)響應(yīng)的數(shù)據(jù)綁定和組合的視圖組件。下面這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目中v-model父子組件通信實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下。2017-12-12教你在vue項(xiàng)目中使用svg圖標(biāo)的方法
本文給大家介紹了在vue項(xiàng)目中使用svg圖標(biāo)的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2022-04-04