將 React 應用遷移至 Vite

前言

當下,在項目開發的過程中,對於大多數人來說,會使用 create react app 來創建 react 應用,它開箱即用,零配置,但隨着項目中代碼量的增加,你的項目構建時間也會隨之增加,開發服務啓動時間變得緩慢,代碼更改後熱更新也會變慢,可能會需要 2-5s 纔會在頁面上體現,那麼能否提高構建速度,減少開發者等待的時間呢?

如何提升構建速度?

我們可以將構建工具遷移到 Vite[1]。

Vite 是下一代前端構建工具,可以更快地構建應用程序。

Vite 的亮點

創建 vite 新項目

使用以下命令創建全新的 react 應用程序。

yarn create vite my-react-app --template react

CRA 爲什麼慢?

Create react app 使用 wabpack 來打包。

webpack 打包圖如上圖所示,它將整個項目的代碼打包在一起,然後才能啓動服務。

Vite 爲什麼快?

Vite 將會使用 esbuild[2] 預構建依賴 [3]。esbuild 使用 Go 編寫,並且相比 JavaScript 編寫的 babel 打包器預構建依賴快 10-100 倍。

依賴預構建

當你首次啓動 vite 時,你可能會注意到打印出了以下信息:

Pre-bundling dependencies: (正在預構建依賴:)
  react,
  react-dom
(this will be run only when your dependencies or config have changed)(這將只會在你的依賴或配置發生變化時執行)

依賴預構建的目的:

  1. Vite 必須先將作爲 CommonJS 或 UMD 發佈的依賴項轉換爲 ESM。

  2. 性能: Vite 將有許多內部模塊的 ESM 依賴關係轉換爲單個模塊,例如,lodash-es 有超過 600 個內置模塊!通過預構建 lodash-es 成爲一個模塊,我們就只需要一個 HTTP 請求了!

按需加載

Vite 通過在一開始將應用中代碼區分爲 依賴源碼 兩類,改進了開發服務器啓動時間。

如上圖所示,當瀏覽器請求時,Vite 只需要按需編譯當前屏幕使用的代碼。

緩存優化

Vite 是直接把轉換後的 ES module 的 JavaScript 代碼,給支持 ES module 的瀏覽器,讓瀏覽器自己去加載依賴,也就是把壓力丟給了瀏覽器,從而達到了項目啓動速度快的效果。在我們修改其中一個代碼模塊的時候,Vite 熱更新非常快,源碼中模塊的請求會根據 304 Not Modified 進行協商緩存,而依賴模塊請求則會通過 Cache-Control: max-age=31536000,immutable 進行強緩存,因此一旦被緩存它們將不需要再次請求。

時間對比

我拿一個 create react app 項目, 該項目包含 3 個路由頁面,14 個組件,遷移到 vite,也就是大家常說的 cra 來測試過,我們一起來對比下時間:

開發模式 CRA 12 s,vite 298 ms。Vite 開發環境是懶編譯模式, 298ms 只是啓動 devServer 的時間,不包含資源編譯,只有你用瀏覽器去請求頁面了,它纔會進行編譯,但是 CRA 是一次性把所有資源都編譯、打包了。

打包模式 CRA 15.66 s,vite 9.11 s

從 CRA 遷移到 Vite

那麼我是如何從老項目遷移到 vite 的呢?

"devDependencies": {
  "@vitejs/plugin-react": "1.1.1",
  "vite": "2.7.0"
},
"scripts": {
  // 開發階段啓動 Vite Dev Server
  "dev": "vite",
  // 生產環境打包
  "build": "tsc && vite build",
  // 生產環境打包完預覽產物
  "preview": "vite preview"
},
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";


export default ({ mode }) => {
  return defineConfig({
    plugins: [react()],
    define: {
      "process.env.NODE_ENV": `"${mode}"`,
    }
  })
}

可以看到配置文件中默認在 plugins 數組中配置了官方的 react 插件,來提供 React 項目編譯和熱更新的功能。

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
const { resolve } = require('path')

// https://vitejs.dev/config/
export default defineConfig({
  base: './',
  resolve: {
    alias: {
      '@components': resolve(__dirname, 'src', 'components'),
      '@utils': resolve(__dirname, 'src', 'utils'),
      '@config': resolve(__dirname, 'src', 'config'),
    },
  },
  plugins: [react()]
})
//修改前
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
//修改後
<link rel="icon" href="/favicon.ico" />
<div></div>
<script type="module" src="/src/index.jsx"></script>

REACT_APP_XXX  的環境變量,則切換爲  VITE_XXX,假如有一個  .env  文件:

// 修改前
REACT_APP_ENV = local
// 修改後
VITE_ENV = local

如果項目使用了 sass,則需要執行命令進行安裝:

$ yarn add -D sass

如果 scss 文件裏面引入了一些 node_modules 的 css 是使用 ~ 符號的,可以做出調整:

@import '~antd/dist/antd.css';

調整爲

@import 'antd/dist/antd.css';

可以參考 issue - Cannot import CSS from node_modules using "~" pattern[4]

若能夠成功啓動,那麼恭喜你,項目遷移成功了,若不能啓動,我們可以根據命令行中的提示,參照 Vite 文檔修改。

遇到問題

decorators not support

項目代碼中如果使用了裝飾器,比如 redux 提供的 connect 來綁定狀態,形如:

@connect(state => state.foo)
class Foo extends React.PureComponent {
	....
}

但是 decorators 語法居然不被 vite 支持,關於這個問題,也有一個 issus,目前沒有一個好的解決辦法,只好去掉 decorators,改用常規的函數綁定了。

無法按需加載樣式文件

如果項目組使用了 antd 組件庫,本來會使用 babel-plugin-import 來自動導入組件和樣式, vite 對 babel-plugin-import 支持不那麼好,那麼就要手動修改代碼。

// 修改前
import { Button } from 'antd';

// 修改後
import Button from 'antd/lib/button';
import 'antd/lib/button/style/index.css';

也可以使用 vite-plugin-imp

'default' is not exported

有時候三方依賴項加載會出錯,例如'default' is not exported 等,這裏可以參考 issues

在實際遷移過程中,還是難免遇到一些奇怪的問題,這都是嚐鮮的代價。

小結

Vite 充分利用了瀏覽器的加載機制和緩存機制等,大大提示了研發效率,vite 雖然快,但不是所有的項目 react 項目都可以遷移成功,若有些項目的中的 npm 包並不遵循 CommonJS 的規範,那就可能遷移失敗,或者需要寫一些自定義插件來適配,所以筆者認爲不要激進地直接重構一些已有的大型項目,打包不是目的,運行纔是,若有些項目 webpack 配置特別複雜,那麼我們可以升級到 webpack5,或者開啓 webpack5 懶編譯模式 [5] 等, 這樣既能提升部分構建效率,也可以保證項目能夠穩定運行。

以上就是本文全部內容,希望這篇文章對大家有所幫助,也可以參考我往期的文章或者在評論區交流你的想法和心得,歡迎一起探索前端。

[1]Vite: https://vitejs.dev/

[2]esbuild: https://esbuild.github.io/

[3] 預構建依賴: https://cn.vitejs.dev/guide/dep-pre-bundling.html

[4]Cannot import CSS from node_modules using '~' pattern: https://github.com/vitejs/vite/issues/382

[5] 開啓 webpack5 懶編譯模式: https://juejin.cn/post/7090372816784064526

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/J0HO2PP8eaxTnoxx601dIA