帶你玩轉 Vite + Vue3 高低版本常用玩法

京東雲開發者發表於2023-12-06

一、首先來個 Vite 的通用簡介

Vite 是一種新型前端構建工具,在我們保險前端專案中已經推動並應用很久了,Vite 能夠顯著降低構建時間,提升前端開發效率。

它主要由兩部分組成:

Vite 還提供了強大的擴充套件性,可透過其 外掛 APIJavaScript API 進行擴充套件,並提供完整的型別支援。

二、Vite 的優勢,為什麼使用 Vite ?

當我們開始構建越來越大型的應用時,需要處理的 JavaScript 程式碼量也呈指數級增長。包含數千個模組的大型專案相當普遍。基於 JavaScript 開發的工具就會開始遇到效能瓶頸:通常需要很長時間(甚至是幾分鐘!)才能啟動開發伺服器,即使使用模組熱替換(HMR),檔案修改後的效果也需要幾秒鐘才能在瀏覽器中反映出來。如此迴圈往復,嚴重影響開發人員的效率。

當冷啟動開發伺服器時,基於打包器的方式啟動必須優先抓取並構建你的整個應用,然後才能提供服務,如果專案很大,導致的一個必然結果就是,服務啟動很慢,熱更新很慢,效率降低。Vite 只需要在瀏覽器請求原始碼時進行轉換並按需提供原始碼。根據情景動態匯入程式碼,即只在當前螢幕上實際使用時才會被處理,節省啟動及更新時間。

如下圖是新舊專案啟動時長對比,Vite 啟動速度,快了 30 多倍

常規的透過 webpack 啟動的服務,基於打包器啟動,整個重新構建,即使將構建內容放到快取中,更新速度也會隨著應用體積增長而直線下降:

Vite 在原生 ESM 基礎上執行 HMR,Vite 只需要精確地使已編輯的模組與其最近的 HMR 邊界之間的鏈失活(大多數時候只是模組本身),使得無論應用大小如何,HMR 始終能保持快速更新:

三、Vite 為我們開發提升了很大的效率,但是也會有一些小問題

1、使用 Vite + Vue3 +Element-plus 或者其他 UI 庫,按需載入使用時,多人開發,每人頁面重新 reload 了嗎🤓?

2、低版本 Vite(2.9以下)配置 Preview 的 proxy 時,是不是不生效🤓?

3、低版本 Vite 配合 unocss 設定樣式後,是不是熱更新不生效🤓?

4、部分依賴包,使用京東映象,是不是無法下載🤓?

如果你遇到了以上問題,那這邊文章可以幫助你解決困惑!

四、解決頁面 reload 問題:高低版本 Vite 解決方法不同

  • 原因:UI 庫配置按需載入,點選跳轉新頁面,導致重新 reload 頁面
  • 情況:高低版本 Vite 解決頁面 reload 方法不同

問題現象如下,上方圖片頁面會 reload會有一段時間白屏,下方圖片中檔案 preload:

1、Vite 低版本(v2.9以下)頁面 reload 解決方法;

  • 解決方法:提前進行依賴包的預載入,可透過如下兩種方法配置,推薦方法b(因為低版本有現成外掛);
  • 透過這些配置,就能夠使頁面 reload 在多人開發時只進行一次。

注意事項(低版本 Vite 的 bug,高版本已經修復):

  • 使用 unocss 時,熱更新會失效,需要手動重新整理頁面,如果不使用 unocss 影響不大

方法a、透過手動配置 vite.config.ts 檔案的 optimizeDeps 選項;

// vite.config.js
{
    ...
    optimizeDeps: {
        include: [
            'vue',
            'vue-router',
            'pinia',
            '@element-plus/icons-vue',
            'driver.js',
            'element-plus',
            'element-plus/es',
            'element-plus/es/components/alert/style/index',
            'element-plus/es/components/badge/style/index',
            'element-plus/es/components/breadcrumb-item/style/index',
            'element-plus/es/components/breadcrumb/style/index',
            'element-plus/es/components/button/style/index',
            'element-plus/es/components/card/style/index'
            ...
        ]
    }
}

方法b、使用現成的外掛 vite-plugin-optimize-persistvite-plugin-package-config (推薦原因: 自動會生成預載入配置檔案 optimizeDeps),通用配置如下:

😂遺憾的是高版本(v2.9及以上失效)配置無效,有興趣的話,可以試著修改下原始碼。

// vite.config.ts
// 兩個外掛組合使用,會自動生成 optimizeDeps 配置,預設生成到 package.json 檔案中,可透過 PkgConfig() 修改生成目錄
import OptimizationPersist from 'vite-plugin-optimize-persist'
import PkgConfig from 'vite-plugin-package-config'

export default {
  ...
  plugins: [
    ...
    PkgConfig(),
    OptimizationPersist()
  ]
}

2、Vite 高版本(v4+)頁面 reload 解決方法:

  • 解決方法:上述兩個外掛已被棄用,只能自己配置 optimizeDeps 選項,提前進行依賴包的預載入;

全量配置案例如下,備註:如果專案中使用,根據專案需要進行相應依賴包的配置,否則會出現首屏載入過慢的問題,類似我們常規使用的 webpack 一樣;

// vite.config.js

{
    ...
    optimizeDeps:{
         include: [
            '@element-plus/icons-vue',
            '@tinymce/tinymce-vue',
            '@vueuse/core',
            '@vueuse/shared',
            'axios',
            'clipboard',
            'codemirror',
            'codemirror/addon/lint/json-lint',
            'codemirror/addon/lint/lint',
            'codemirror/mode/css/css',
            'codemirror/mode/htmlmixed/htmlmixed',
            'codemirror/mode/javascript/javascript',
            'driver.js',
            'element-plus',
            'element-plus/es',
            'element-plus/es/components/alert/style/index',
            'element-plus/es/components/badge/style/index',
            'element-plus/es/components/breadcrumb-item/style/index',
            'element-plus/es/components/breadcrumb/style/index',
            'element-plus/es/components/button/style/index',
            'element-plus/es/components/card/style/index',
            'element-plus/es/components/cascader/style/index',
            'element-plus/es/components/check-tag/style/index',
            'element-plus/es/components/checkbox-group/style/index',
            'element-plus/es/components/checkbox/style/index',
            'element-plus/es/components/col/style/index',
            'element-plus/es/components/color-picker/style/index',
            'element-plus/es/components/config-provider/style/index',
            'element-plus/es/components/date-picker/style/index',
            'element-plus/es/components/dialog/style/index',
            'element-plus/es/components/divider/style/index',
            'element-plus/es/components/drawer/style/index',
            'element-plus/es/components/dropdown-item/style/index',
            'element-plus/es/components/dropdown-menu/style/index',
            'element-plus/es/components/dropdown/style/index',
            'element-plus/es/components/empty/style/index',
            'element-plus/es/components/form-item/style/index',
            'element-plus/es/components/form/style/index',
            'element-plus/es/components/icon/style/index',
            'element-plus/es/components/input-number/style/index',
            'element-plus/es/components/input/style/index',
            'element-plus/es/components/link/style/index',
            'element-plus/es/components/loading/style/index',
            'element-plus/es/components/menu-item/style/index',
            'element-plus/es/components/menu/style/index',
            'element-plus/es/components/message-box/style/index',
            'element-plus/es/components/message/style/index',
            'element-plus/es/components/notification/style/index',
            'element-plus/es/components/option/style/index',
            'element-plus/es/components/pagination/style/index',
            'element-plus/es/components/popover/style/index',
            'element-plus/es/components/progress/style/index',
            'element-plus/es/components/radio-group/style/index',
            'element-plus/es/components/radio/style/index',
            'element-plus/es/components/row/style/index',
            'element-plus/es/components/scrollbar/style/index',
            'element-plus/es/components/select/style/index',
            'element-plus/es/components/space/style/index',
            'element-plus/es/components/step/style/index',
            'element-plus/es/components/steps/style/index',
            'element-plus/es/components/sub-menu/style/index',
            'element-plus/es/components/switch/style/index',
            'element-plus/es/components/tab-pane/style/index',
            'element-plus/es/components/table-column/style/index',
            'element-plus/es/components/table/style/index',
            'element-plus/es/components/tabs/style/index',
            'element-plus/es/components/tag/style/index',
            'element-plus/es/components/timeline-item/style/index',
            'element-plus/es/components/timeline/style/index',
            'element-plus/es/components/tooltip/style/index',
            'element-plus/es/components/transfer/style/index',
            'element-plus/es/components/upload/style/index',
            'element-plus/es/components/popconfirm/style/index',
            'element-plus/es/components/backtop/style/index',
            'element-plus/es/components/affix/style/index',
            'element-plus/es/components/statistic/style/index',
            'element-plus/es/components/tree/style/index',
            'element-plus/es/components/tree-v2/style/index',
            'element-plus/es/components/tree-select/style/index',
            'element-plus/es/components/table-v2/style/index',
            'element-plus/es/components/skeleton/style/index',
            'element-plus/es/components/skeleton-item/style/index',
            'element-plus/es/components/collapse/style/index',
            'element-plus/es/components/collapse-item/style/index',
            'element-plus/es/components/collapse-transition/style/index',
            'element-plus/es/components/carousel/style/index',
            'element-plus/es/components/carousel-item/style/index',
            'element-plus/es/components/calendar/style/index',
            'element-plus/es/components/badge/style/index',
            'element-plus/es/components/avatar/style/index',
            'element-plus/es/components/aside/style/index',
            'element-plus/es/locale/lang/zh-cn',
            'fuse.js',
            'js-base64',
            'js-cookie',
            'js-file-download',
            'nprogress',
            'pako',
            'path-browserify',
            'path-to-regexp',
            'pinia',
            'prismjs',
            'qs',
            'screenfull',
            'sortablejs',
            'vue',
            'vue-json-pretty',
            'vue-router',
            'vuedraggable' 
         ]
    }
}

五、解決 Vite 低版本(v2.9以下),preview 配置 proxy 不生效:

問題原因:低版本 Vite 的執行 preview 預覽時,使用的 proxy 是 server 中的 proxy 配置,官方文件中提供的 preview 的 proxy 無效,這是低版本 Vite 的一個 bug,高版本已經修復,github 上有相關的 issue,也可以透過原始碼檢視。

解決辦法:

  1. 在 vite.config.js 使用 server 中的 proxy 代替

  2. 使用 switchhost 進行代理配置

六、解決 Vite 低版本(v2.9以下),unocss 熱更新失效:

  1. 解決方法a:升級 Vite,從根本解決問題,以後專案升級比較方便,各種小問題容易規避,推薦升級

  2. 解決方法b:降低 unocss 的版本,可能會出現各種其他問題,不建議

針對這個問題,作者沒有降低 unocss 的版本,直接進行 Vite 升級,如果使用 unocss 強烈建議進行 Vite 升級;

不升級後期如果使用其他的依賴包,可能也會有各種其他問題出現

七、解決升級 Vite 專案後,部分依賴包,使用京東映象無法下載:

產生原因:各種依賴包之間互相依賴,京東映象的 npm 包可能有問題;

解決方法:非京東私有依賴包,使用 npm 官方映象下載,京東私有映象使用京東映象,初始化專案,生成 package-lock.json 檔案,多人合作時,以 package-lock.json 檔案為主。

總結:Vite 幫助我們提升開發效率,但是區分 Vite 是否需要使用最新版本,還是需要作為考慮依據:

如果你的專案不使用 unocss,同時對於 preview 下的 proxy 沒有特殊要求,那麼 Vite2.8.6 + Vue3 + elementPlus 可自動生成預載入檔案,省時省力,開發體驗好,強烈推薦;如果你的專案使用 unocss 或者對於 preview 下的 proxy 有特殊需求,強烈建議使用最新版本的 Vite,可透過手動配置 optimizeDeps 解決頁面重新整理的問題。

作者:京東保險 王升升

來源:京東雲開發者社群 轉載請註明來源

相關文章