基于 Webpack 項目接入 Vite 你可能需要注意的點
前言
在之前的 **如何優(yōu)化你的 vue-cli 項目?**[2] 一文中介紹基于 webpack 進行的一些優(yōu)化方法,本文的核心是基于一個 vue2 的項目(也就是上篇文章中的項目)來繼續(xù)介紹一下如何接入 vite,以及這個過程中需要關(guān)注的點。
之前的優(yōu)化是基于已有的 構(gòu)建工具 進行的處理,現(xiàn)在相當(dāng)于直接替換 構(gòu)建工具,主要用于提升開發(fā)時的整體編譯速度和效率,本身也是一個優(yōu)化點。
本篇文章其實是之前的一個規(guī)劃,但是一直沒有落實,現(xiàn)在算是補回來吧!!!
集成 vite
vue 項目如何使用 webpack
如果你觀察過你的 vue2 項目中的 package.json 文件,會發(fā)現(xiàn)其中和依賴相關(guān)的內(nèi)容竟然沒有 webpack,但是直接觀察 node_modules 目錄中的依賴,還是會發(fā)現(xiàn) webpack 也作為依賴被安裝了,那么 vue-cli 創(chuàng)建的項目其中的 webpack 到底在哪呢?
vue-cli-service 命令
其實,我們可以觀察一下我們是如何啟動項目的,也就是 package.json 文件中的 script 的腳本字段,如:
"scripts": {
"dev": "cross-env API_ENV=dev vue-cli-service serve --mode dev",
"build:test": "cross-env API_ENV=test vue-cli-service build",
"build:prod": "cross-env API_ENV=prod vue-cli-service build"
},
復(fù)制代碼
其中的 vue-cli-service 的命令就很顯眼了,如果你自己嘗試過用 node 開發(fā)過一些命令,那么就會知道像這樣的命令一定是在某個庫中使用 package.json 的 bin 屬性與對應(yīng)文件的 Shebang 來實現(xiàn)的。
這里的 vue-cli-service 命令實際上是在 node_modules\@vue\cli-service\package.json 和 node_modules\@vue\cli-service\bin\vue-cli-service.js 中聲明和定義的:

webpack 依賴
仔細觀察 @vue/cli-service 相關(guān)的 package.json 中的依賴,會發(fā)現(xiàn) webpack 是作為其的依賴:

到這我們就知道了,雖沒有顯式的直接依賴 webpack,但本質(zhì)上仍然是基于 webpack 的,感興趣的可以自行了解 @vue/cli-service 相關(guān)源碼。
項目集成 vite
知道了 vue2 的項目如何依賴于 webpack,那么接入 vite 時,我們就需要顯式的安裝依賴,不能像上述一樣直接使用.
安裝 vite 依賴
通過 npm install vite \-D 為項目添加相關(guān)依賴,或者你可以通過其他方式添加.
創(chuàng)建 vite 配置文件
在 vue.config.js 的根目錄下創(chuàng)建 vite.config.js 用于添加 vite 相關(guān)配置.
在 webpack 中通過 vue-loader 來處理 .vue 文件,而在 vite 不存在 loader 的概念,只有 plugin 的概念,因此需要通過 vite-plugin-vue2 這個插件來處理 .vue 文件
安裝對應(yīng)插件: npm install vite-plugin-vue2 -D在 vite.config.js中配置插件:import { defineConfig } from 'vite';
import { createVuePlugin } from 'vite-plugin-vue2';
export default defineConfig(() => {
return {
plugins: [createVuePlugin()],
};
});
復(fù)制代碼
設(shè)置 root 選項(不推薦)
在 vue2 項目中 webpack 的模板文件默認是 public\index.html,而 vite 默認配置文件是項目根目錄下 index.html,因此,需要通過 **`root`**[3] 屬性重新指定 vite 的模板文件路徑,如:
import { defineConfig } from 'vite';
import { createVuePlugin } from 'vite-plugin-vue2';
import { resolve } from 'path';
export default defineConfig(() => {
return {
root: resolve(__dirname, './public'),
plugins: [createVuePlugin()],
};
});
復(fù)制代碼
個人不推薦這種方式,建議還是將
index.html復(fù)制到根目錄下讓vite使用這個外部的模板文件,否則頁面在加載main.ts時會出現(xiàn)404的情況.
配置 vite 命令
這個非常簡單,直接在 package.json 配置對應(yīng)的 vite 命令即可,如下:
"scripts": {
"vite": "vite",
"dev": "cross-env API_ENV=dev vue-cli-service serve --mode dev",
"build:test": "cross-env API_ENV=test vue-cli-service build",
"build:prod": "cross-env API_ENV=prod vue-cli-service build"
},
復(fù)制代碼
基于 ESMoudle 設(shè)置 index.html 模板的入口文件
通過 npm run dev 啟動項目,頁面正常訪問,如下

此時,通過調(diào)試器查看網(wǎng)絡(luò)請求會發(fā)現(xiàn)其實只請求了如下的資源,而關(guān)鍵的 css 和 js 資源并沒有被加載:

這是因為原本項目中的 index.html 模板中是通過 webpack 基于入口文件進行打包構(gòu)建后自動注入 css、js 等關(guān)鍵資源,而在 vite 中模板 index.html 就是入口資源,意味著如果我們需要加載對應(yīng)的資源,得手動在 index.html 這個模板資源中基于 ES Module 指定入口資源,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
<meta content="yes" name="apple-mobile-web-app-capable" />
<meta content="black" name="apple-mobile-web-app-status-bar-style" />
<meta content="telephone=no" name="format-detection" />
<link rel="icon" href="./favicon.ico" />
<title>PC</title>
</head>
<body>
<noscript>
<strong>
We're sorry but esm-pc-fe doesn't work properly without JavaScript enabled. Please enable it to continue.
</strong>
</noscript>
<div id="app"></div>
<script src="../src/main.ts" type="module"></script>
</body>
</html>
復(fù)制代碼
兼容 webpack 和 vite 配置
vite 設(shè)置別名
基于以上修改之后,在重新啟動,實際上會出現(xiàn)如下錯誤:

本質(zhì)原因就是原本我們在 webpack 中設(shè)置的 別名 不能被 vite 識別,因此我們需要在 vite 中重新設(shè)置一下這些別名:
import { defineConfig } from 'vite';
import { createVuePlugin } from 'vite-plugin-vue2';
import { resolve } from 'path';
const pathResolve = (pathUrl) => resolve(__dirname, pathUrl);
export default defineConfig(() => {
return {
resolve: {
alias: {
'@': pathResolve('./src'),
'@build': pathResolve('./build'),
'@api': pathResolve('./src/api'),
'@utils': pathResolve('./src/utils'),
'@views': pathResolve('./src/views'),
},
},
plugins: [createVuePlugin()],
};
});
復(fù)制代碼
vite 處理 css 預(yù)處理器
基于以上修改后,重新啟動此時可能會報如下錯誤(正常情況下是不會的):

意思很明確,其實就是沒有不能直接處理 .less 文件,不過在 vite 中只需要安裝對應(yīng)的 css 預(yù)處理器就可以了,如此時直接 npm install less \-D 即可,不需要再額外配置類似 webpack 中的 loader,其實這個錯誤很少會出現(xiàn),因為無論你的項目使用什么 css 預(yù)處理器,都必須要得先進行安裝,意味著在 vite 中可以直接使用.
使用 import 代替 require
在 vite 中沒有 require 而在 webpack 中是可以使用的,這就意味著原本項目中使用的 require 的形式會在 vite 中拋出錯誤:

在組件中加載圖片
const bgImg = require('@/assets/images/logo/bg.jpeg');
const avatarImg = require('@/assets/images/avatar.png');
【轉(zhuǎn)換為如下形式】
import bgImg from '@/assets/images/logo/bg.jpeg';
import avatarImg from '@/assets/images/avatar.png';
復(fù)制代碼
加載 js 配置文件
const EnvBaseConf = require('@build/webpack.base.conf.js');
【轉(zhuǎn)換為如下形式】
import EnvBaseConf from '@build/webpack.base.conf.js';
復(fù)制代碼
使用 import.meta.env 代替 process.env
import.meta 是一個在 ES 模塊內(nèi)部可用的對象,它包含關(guān)于模塊運行環(huán)境的有用信息,可以在這個對象的 env 屬性上獲取當(dāng)前的環(huán)境信息,因為在 vite 中不在將對應(yīng)的環(huán)境信息掛載到 process.env 上,這就會導(dǎo)致最終取值結(jié)果為 undefined.
vite 處理 svg
通常在 webpack 中處理 svg 有三步:
自動加載對應(yīng)的 svg文件// svg.js
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
復(fù)制代碼在入口文件 main.ts中引入svg.js在 vue.config.js中為*.svg文件配置對應(yīng)的loaderconst svgRule = config.module.rule('svg');
// 清除已有的所有 loader
// 如果你不這樣做,接下來的 loader 會附加在該規(guī)則現(xiàn)有的 loader 之后
svgRule.uses.clear();
// 添加要替換的 loader
svgRule.use('svg-sprite-loader').loader('svg-sprite-loader').options({
symbolId: 'icon-[name]',
});
復(fù)制代碼
在 vite 中只需要配置 vite-plugin-svg-icons:
import { defineConfig } from 'vite';
import { createVuePlugin } from 'vite-plugin-vue2';
import viteSvgIcons from 'vite-plugin-svg-icons';
import { resolve } from 'path';
const pathResolve = (pathUrl) => resolve(process.cwd(), pathUrl);
export default defineConfig(() => {
return {
resolve: {
alias: {
'@': pathResolve('./src'),
'@build': pathResolve('./build'),
'@api': pathResolve('./src/api'),
'@utils': pathResolve('./src/utils'),
'@views': pathResolve('./src/views'),
},
},
plugins: [
createVuePlugin(),
viteSvgIcons({
iconDirs: [pathResolve('/src/assets/icons/svg')], // 指定要緩存的圖標文件夾
symbolId: 'icon-[name]', // 執(zhí)行 icon-name 的格式
}),
],
};
});
復(fù)制代碼
區(qū)分 webpack 和 vite 環(huán)境
有些配置相關(guān)的內(nèi)容還是沒有辦法很好的復(fù)用,此時可以通過訪問 import.meta.env 值的方式,判斷當(dāng)前環(huán)境是否屬于 vite 環(huán)境,以便于處理一些 webpack 和 vite 本身不兼容的地方,比如在加載文件資源時 webpack 中需要使用 require 加載,vite 中需要通過 import 加載等,當(dāng)然這個可以通過 import(xxx) 的方式來加載是可以兼容兩者的,這里只是提出個引子.
最后
以上就是大概需要注意的一些內(nèi)容,相信有了上述的處理經(jīng)驗,即使你遇到其他問題也可以很快的解決,實際上內(nèi)容不算多、也不算難,其實只要一步步根據(jù)出現(xiàn)的問題逐個解決即可.
簡單看一下接入 vite 前后的整體編譯速度:

注意:本地服務(wù)啟動方面
vite相比于webpack來說速度很快,其實它的快其實是基于esbuild實現(xiàn)的 預(yù)構(gòu)建,還不算是真正做編譯,只有頁面真正初始化加載時,才會對當(dāng)前頁面需要訪問的文件資源進行 按需編譯,所以如果要真正比對其實還得考慮它們 首屏加載的速度,不過vite的按需編譯也會使得它整體速度會更快一些.
關(guān)于本文
作者:熊的貓
https://juejin.cn/post/7129128214735093791
