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

我們從 UmiJS 遷移到了 Vite

開發(fā) 前端
我們從 UmiJS 遷移到 Vite 已經(jīng)上線半年多了。目前很多人對(duì) Vite 躍躍欲試,Vite 開發(fā)體驗(yàn)到底怎么樣,今天來(lái)敘敘遷移到 Vite 的親身經(jīng)歷。

[[437242]]

我們從 UmiJS 遷移到 Vite 已經(jīng)上線半年多了。遷移過(guò)程中也遇到了不少問(wèn)題,好在 Vite 足夠優(yōu)秀,繼承自 Rollup 的插件系統(tǒng),使我們有了自由發(fā)揮空間。目前很多人對(duì) Vite 躍躍欲試,Vite 開發(fā)體驗(yàn)到底怎么樣,今天來(lái)敘敘遷移到 Vite 的親身經(jīng)歷。

先說(shuō)結(jié)論,Vite 已經(jīng)很成熟,強(qiáng)烈建議有條件的可以從 webpack 遷移過(guò)來(lái)。

為什么要放棄 UmiJS

2019 年底,在 Webpack 橫行霸道,各種腳手架琳瑯滿目的時(shí)代選擇了 UmiJS。它配置少、功能多、文檔齊全、持續(xù)更新。一整套的解決方案,非常適合一個(gè)大部分非 React 技術(shù)棧的團(tuán)隊(duì)。經(jīng)過(guò)不斷地磨合,團(tuán)隊(duì)很快適應(yīng)了這種 React 開發(fā)模式,開發(fā)效率也是水漲船高。

凡事總有個(gè)原因,為什么要遷移。2021 年初,為適應(yīng)公司的發(fā)展,前端架構(gòu)也需要做調(diào)整與升級(jí)。在項(xiàng)目日益增長(zhǎng)的情況下,一次項(xiàng)目啟動(dòng)需要耗費(fèi)一分多鐘,熱更新也慢得基本無(wú)法使用。差點(diǎn)的機(jī)器配置啟動(dòng)項(xiàng)目要么好幾分鐘、要么內(nèi)存溢出。這種模式極大地降低了開發(fā)效率。無(wú)論是自定義修改內(nèi)部 webpack 插件、從各種角度如多核編譯、緩存等方式優(yōu)化,依然是杯水車薪。雖然 UmiJS 提供了 webpack5 插件,不過(guò)在當(dāng)時(shí)處于不可用的狀態(tài)。

我們主要的矛盾是:

  1. 啟動(dòng)時(shí)間長(zhǎng)
  2. 熱更新慢
  3. 太臃腫
  4. 框架 BUG 修復(fù)不及時(shí)
  5. 過(guò)度封裝,自定義插件難度大
  6. 約定式功能太單一

適應(yīng)業(yè)務(wù)的要求,我們也需要上微前端。UmiJS 也提供了微前端插件 “乾坤”。但依然解決不了根本開發(fā)體驗(yàn)問(wèn)題。因此,在基礎(chǔ)腳手架上,我們尋求更多的是可控性及透明性。(盡管UmiJS 現(xiàn)在已經(jīng)支持Module Federation 的打包提速方案)

為什么是 Vite

市面上的腳手架很多,陣營(yíng)卻很少,大部分是基于 webpack 的上層封裝。webpack 的缺點(diǎn)很明顯,當(dāng)冷啟動(dòng)開發(fā)服務(wù)器時(shí),基于打包器的方式啟動(dòng)必須優(yōu)先抓取并構(gòu)建你的整個(gè)應(yīng)用,然后才能提供服務(wù)。

在瀏覽器 ESM 支持得很普遍得今天,Vite 這種可以稱得上是下一代前端開發(fā)與構(gòu)建工具。在 Vite 中,HMR 是在原生 ESM 上執(zhí)行的。當(dāng)編輯一個(gè)文件時(shí),無(wú)論應(yīng)用大小如何,HMR 始終能保持快速更新。

Vite 這種方式在我們習(xí)慣 webpack 的陰影下顯得尤為驚艷,可以說(shuō) Vite 完美地解決了我們所有的痛點(diǎn)。不過(guò) Vite 也是剛發(fā)布 2.0 不久,踩過(guò)坑的人也是相當(dāng)少。我們便試試 Vite。

前期調(diào)研

遷移的必要條件是在原有的功能下找到替代方案,我們便統(tǒng)計(jì)用到了 UmiJS 中的 API 及特性

UmiJS 配置

  • alias - 配置別名(對(duì)應(yīng) resolve.alias)
  • base - 設(shè)置路由前綴(對(duì)應(yīng) base)
  • define - 用于提供給代碼中可用的變量(對(duì)應(yīng) define)
  • outputPath - 指定輸出路徑(對(duì)應(yīng) build.outDir)
  • hash - 配置是否讓生成的文件包含 hash 后綴 (Vite 自帶)
  • antd - 整合 antd 組件庫(kù) (無(wú)需框架提供,Vite 中可自己引用)
  • dva - 整合 dva 數(shù)據(jù)流(此庫(kù)已經(jīng)很久沒有更新了,在 hooks 時(shí)代使用顯得格格不入。我們沒有大量使用,重寫一個(gè)文件很輕松)
  • locale - 國(guó)際化插件,用于解決 i18n 問(wèn)題(需要自己實(shí)現(xiàn)國(guó)際化邏輯,都是基于 react-intl 封裝,在 Vite 中實(shí)現(xiàn)無(wú)壓力)
  • fastRefresh - 快速刷新(對(duì)應(yīng) @vitejs/plugin-react-refresh 插件)
  • dynamicImport - 是否啟用按需加載(路由級(jí)的按需加載,在 Vite 中用 React.lazy 封裝)
  • targets - 配置需要兼容的瀏覽器最低版本(對(duì)應(yīng) @vitejs/plugin-legacy 插件)
  • theme - 配置 less 變量(對(duì)應(yīng) css.preprocessorOptions.less.modifyVars 配置)
  • lessLoader - 設(shè)置 less-loader 配置項(xiàng)(與 theme 配置相同)
  • ignoreMomentLocale - 忽略 moment 的 locale 文件(可以通過(guò) alias 設(shè)置別名方式解決)
  • proxy - 配置代理能力(對(duì)應(yīng) server.proxy)
  • externals - 設(shè)置哪些模塊可以不被打包(對(duì)應(yīng) build.rollupOptions.external)
  • copy - 設(shè)置要復(fù)制到輸出目錄的文件或文件夾(對(duì)應(yīng) rollup-plugin-copy)
  • mock - 配置 mock 屬性(對(duì)應(yīng) vite-plugin-mock)
  • extraBabelPlugins - 配置額外的 babel 插件(對(duì)應(yīng) @rollup/plugin-babel)

通過(guò)配置分析,基本上所有的 UmiJS 配置都可以在 Vite 中找到替代方案。除了配置還有一些約定

UmiJS 中 @/* 路徑,代替方式

  1. defineConfig({ 
  2.   resolve: { 
  3.     alias: { 
  4.       '@/': `${path.resolve(process.cwd(), 'src')}/`, 
  5.     }, 
  6.   }, 
  7. }); 

遷移

Review 現(xiàn)有的代碼,找出可能出問(wèn)題的點(diǎn)并統(tǒng)計(jì)。做前期準(zhǔn)備。跑起來(lái)優(yōu)先:

從頭 Vite 官方模板中創(chuàng)建一個(gè)項(xiàng)目,安裝所需依賴包。UmiJS 內(nèi)置封裝了 react-router、antd react-intl,這里我們需要手動(dòng)加上 BrowserRouter、ConfigProvider、LocaleProvider

  1. // App.tsx 
  2. exportdefaultfunction App() { 
  3.   return ( 
  4.     <AppProvider> 
  5.       <BrowserRouter> 
  6.         <ConfigProvider locale={currentLocale}> 
  7.           <LocaleProvider> 
  8.             <BasicLayout> 
  9.               <Routes /> 
  10.             </BasicLayout> 
  11.           </LocaleProvider> 
  12.         </ConfigProvider> 
  13.       </BrowserRouter> 
  14.     </AppProvider> 
  15.   ); 

根據(jù)之前約定式路由,添加相應(yīng)的路由配置

  1. exportconst basicRoutes = [ 
  2.   { 
  3.     path: '/'
  4.     exact: true
  5.     trunk: () =>import('@/pages/index'), 
  6.   }, 
  7.   { 
  8.     path: '/login'
  9.     exact: true
  10.     trunk: () =>import('@/pages/login'), 
  11.   }, 
  12.   { 
  13.     path: '/my-app'
  14.     trunk: () =>import('@/pages/my-app'), 
  15.   }, 
  16.   // ... 
  17. ]; 

路由渲染組件,通過(guò) React.lazy 實(shí)現(xiàn) UmiJS 中的 dynamicImport

  1. const routes = basicRoutes.map(({ trunk, ...config }) => { 
  2.   const Trunk = React.lazy(() => trunk()); 
  3.   return { 
  4.     ...config, 
  5.     component: ( 
  6.       <React.Suspense fallback={<Spinner />}> 
  7.         <Trunk /> 
  8.       </React.Suspense> 
  9.     ), 
  10.   }; 
  11. }); 
  12.  
  13. exportdefaultfunction Routes() { 
  14.   return ( 
  15.     <Switch> 
  16.       {routes.map((route) => ( 
  17.         <Route key={route.key || route.path} path={route.path} exact={route.exact} render={() => route.component} /> 
  18.       ))} 
  19.     </Switch> 
  20.   ); 

從原先的約定式路由遷移完成,項(xiàng)目中主要不兼容的地方就是從 umi 導(dǎo)入的成員

  1. import { useIntl, history, useLocation, useSelector } from'umi'

我們需要將所有 umi 中導(dǎo)入的變量,通過(guò)編輯器的正則替換批量修改替換。

  • 國(guó)際化的 useIntl 通過(guò)將語(yǔ)言文件和 react-intl 封裝,導(dǎo)出一個(gè)全局的 formatMessage 方法
  • 路由相關(guān)的 API 用 react-router-dom 導(dǎo)出替換
  • Redux 相關(guān)的,用 react-redux 導(dǎo)出替換
  • 查找項(xiàng)目中使用 require 的地方,替換為動(dòng)態(tài) import
  • 查找項(xiàng)目中使用 process.env.NODE_ENV,替換為 import.meta.env.DEV,因?yàn)樵?Vite 中不再有 node.js 相關(guān)的 API

將 antd 添加進(jìn)項(xiàng)目后,發(fā)現(xiàn) babel-plugin-import 對(duì)應(yīng)的 Vite 插件似乎有問(wèn)題,某些樣式在 dev 模式下缺失,打包后正常。排查發(fā)現(xiàn)是組件包里面引用了 antd,在 dev 模式下包名被“依賴預(yù)構(gòu)建” 混淆,導(dǎo)致插件無(wú)法正確插入 antd 的樣式。為此,我們自己寫了個(gè)插件,在 dev 模式下全量引入樣式,prod 才走插件。

很輕松,第一個(gè)頁(yè)面成功運(yùn)行。

由于遷移之后需要使用微前端,因此我們將公共配置通過(guò)外置插件統(tǒng)一管理。

  1. exportdefault defineConfig({ 
  2.   server: { 
  3.     // 每個(gè)項(xiàng)目配置不同的端口號(hào) 
  4.     port: 3001, 
  5.   }, 
  6.   plugins: [ 
  7.     reactRefresh(), 
  8.     // 公共配置插件 
  9.     baseConfigPlugin(), 
  10.     // AntD 插件 
  11.     antdPlugin(), 
  12.   ], 
  13. }); 

遷移后發(fā)現(xiàn) Vite 需要配置的其實(shí)很少,抽取的公共配置,封裝成 Vite 插件。

  1. import path from'path'
  2. import LessPluginImportNodeModules from'less-plugin-import-node-modules'
  3.  
  4. exportdefaultfunction vitePluginBaseConfig(config: CustomConfig): Plugin { 
  5.   return { 
  6.     enforce: 'post'
  7.     name'base-config'
  8.     config() { 
  9.       return { 
  10.         cacheDir: '.vite'
  11.         resolve: { 
  12.           alias: { 
  13.             '@/': `${path.resolve(process.cwd(), 'src')}/`, 
  14.             lodash: 'lodash-es'
  15.             'lodash.debounce''lodash-es/debounce'
  16.             'lodash.throttle''lodash-es/throttle'
  17.           }, 
  18.         }, 
  19.         server: { 
  20.           host: '0.0.0.0'
  21.         }, 
  22.         css: { 
  23.           preprocessorOptions: { 
  24.             less: { 
  25.               modifyVars: { 
  26.                 '@primary-color''#f99b0b'
  27.                 ...config.theme, 
  28.                 // 自定義 ant 前綴 
  29.                 '@ant-prefix': config.antPrefix || 'ant'
  30.               }, 
  31.               plugins: [new LessPluginImportNodeModules()], 
  32.               javascriptEnabled: true
  33.             }, 
  34.           }, 
  35.         }, 
  36.       }; 
  37.     }, 
  38.   }; 

遷移的整個(gè)過(guò)程沒有想象中那么繁雜,反而相對(duì)容易。幾乎常用的功能 Vite 都有方案支持,這也許是 Vite 的厲害之處吧。其實(shí)本質(zhì)上的復(fù)雜度在于業(yè)務(wù),項(xiàng)目的復(fù)雜度就是代碼量的體現(xiàn),通過(guò) IDE 的搜索替換,很快便完成了遷移并成功的運(yùn)行。

現(xiàn)在,我們所有的項(xiàng)目都基于 Vite,完全沒有了等待而摸魚的煩惱。

問(wèn)題/解決

轉(zhuǎn)換 less 文件 @import '~antd/es/style/themes/default.less' 中的 ~ 別名報(bào)錯(cuò)

配置 less 插件less-plugin-import-node-modules

SyntaxError: The requested module 'xxx' does not provide an export named 'default'

我們將公共組件作為獨(dú)立的 npm 包之后使用時(shí)遇到的錯(cuò)誤。本想著公共組件包自己不編譯,統(tǒng)一交給使用方編譯。所以導(dǎo)出了 TS 源文件。而這種情況常規(guī)下沒有問(wèn)題,Vite 一旦遇到 CommonJS 或 UMD 的包才導(dǎo)致無(wú)法解析。雖然可以將無(wú)法解析的包放入 optimizeDeps.include 。但是架不住包的數(shù)量多啊,還是將它 tsc 轉(zhuǎn)譯為 JS 文件再發(fā)布。

打包提速

首次打包發(fā)現(xiàn)需要 70 多秒,我們來(lái)優(yōu)化打包結(jié)構(gòu)

  • 通過(guò) build.minify 改為 esbuild(最新版 Vite 已經(jīng)默認(rèn) esbuild) 。Esbuild 比 terser 快 20-40 倍,壓縮率只差 1%-2%。開啟后降低到 30 多秒
  • babel-plugin-import 的類似 babel 插件嚴(yán)重拖后腿,總共不到 40 秒的時(shí)間,它就要占 10 秒。我們通過(guò)正則的方式做了個(gè)插件,完美解決
  • 通過(guò)分析 rollup 對(duì) @ant-design/icons 、lodash 包的 transform 數(shù)量非常多。我們將這些包也加入到剛剛做的插件中

通過(guò)一頓操作下來(lái),提速到 16 秒,先這樣吧。

為什么將 cacheDir 放在根目錄

cacheDir 作為存儲(chǔ)緩存文件的目錄。此目錄下會(huì)存儲(chǔ)預(yù)打包的依賴項(xiàng)或 vite 生成的某些緩存文件,使用緩存可以提高性能。在某些情況下需要聯(lián)調(diào) node_modules 里包,從而導(dǎo)致修改后未生效。這時(shí)需要使用 --force 命令行選項(xiàng)或手動(dòng)刪除目錄,放在根目錄便于刪除。

兼容性問(wèn)題

Vite 的兼容性可以通過(guò)官方的插件 @vitejs/plugin-legacy 解決。我們已經(jīng)放棄支持 IE 11,無(wú)限制在生產(chǎn)使用 ESM,羨慕嗎?

結(jié)語(yǔ)

如果你是新的項(xiàng)目,完全不必考慮 Webpack 了,Vite 及 rollup 的完全生態(tài)足夠支撐上生產(chǎn)。如果你是 Webpack 生態(tài)老項(xiàng)目,不忍體驗(yàn)上的折磨,滿足遷移條件的話,不妨試試 Vite,肯定會(huì)帶給你驚喜。

后面我會(huì)分享 Vite 和自己實(shí)現(xiàn)的微前端搭配組合,以及Vite 相關(guān)的插件,請(qǐng)持續(xù)關(guān)注。

 

責(zé)任編輯:姜華 來(lái)源: 前端星辰
相關(guān)推薦

2011-04-25 09:35:31

TwitterJava

2020-09-09 09:38:47

GoLangNodeJS編程語(yǔ)言

2023-12-07 07:14:36

WebpackVite

2022-11-01 08:55:55

編譯工具MavenSpring

2020-10-13 18:16:19

微服務(wù)架構(gòu)數(shù)據(jù)

2010-09-29 11:06:21

活動(dòng)目錄OpenLDAP

2010-07-20 09:48:33

2012-05-21 10:23:36

2013-06-21 13:49:08

MariaDB

2020-10-13 09:25:27

ESClickHouse搜索引擎

2020-03-12 08:00:34

MySQL遷移TiDB

2016-10-26 16:44:44

WatchfinderAWS云計(jì)算

2024-03-29 09:13:58

MochaNode.js測(cè)試

2011-06-24 10:10:35

SVN

2020-07-27 11:35:26

GitHub代碼開發(fā)者

2022-06-06 07:24:09

Caddy開源Ubuntu

2009-02-06 10:32:00

UnixLinux服務(wù)器

2013-05-03 09:49:38

MySQLMariaDB

2020-04-13 08:46:22

MongoDBES服務(wù)器

2020-01-18 09:35:03

微服務(wù)團(tuán)隊(duì)架構(gòu)
點(diǎn)贊
收藏

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