Vue 2 項(xiàng)目如何快速接入 Vite 作為開發(fā)工具
當(dāng)前版本 [email protected]
一. 適合什么項(xiàng)目遷移
使用 vue2 的系統(tǒng) 內(nèi)部系統(tǒng) - 無需大型流量場(chǎng)景:因?yàn)?vite 更迭較快,導(dǎo)致系統(tǒng)需要定期改動(dòng)基礎(chǔ)功能,造成不穩(wěn)定 非 ssr 系統(tǒng) - ssr 還有很多問題,暫且等社區(qū)豐富起來 定期有人維護(hù)的系統(tǒng) 對(duì)開發(fā)有痛點(diǎn)而想要改進(jìn):比如打包慢,冷啟動(dòng)慢,HMR 更新慢。。。。 vite 生產(chǎn)環(huán)境用 rollup,但是改造成本大,提效不高,風(fēng)險(xiǎn)大,暫不建議使用。【本人愚見,大佬輕噴】
二.遷移步驟
將會(huì)以內(nèi)部系統(tǒng)作為案例改造, 開發(fā)用 vite,生產(chǎn)依舊保持 webpack。
簡(jiǎn)單了解 vite 特性。有問題優(yōu)先看vite 官網(wǎng)排查是否有更新或解決方案!! npm i [email protected] [email protected] [email protected] -D package.json 添加一個(gè) script -- "vite": "NODE_ENV=development vite" 關(guān)鍵在于配置 vite.config.js【默認(rèn)叫做這個(gè)文件名,你可配置成其他的。。】
import { defineConfig } from 'vite';
import path from 'path';
import fs from 'fs';
import { createVuePlugin } from 'vite-plugin-vue2';
import { injectHtml, minifyHtml } from 'vite-plugin-html';
import { cjs2esmVitePlugin } from 'cjs2esmodule'
import dotenv from 'dotenv'
const config = require('./config')
try {
// 根據(jù)環(huán)境變量加載環(huán)境變量文件
const file = dotenv.parse(fs.readFileSync(`./config/.env.${process.env.NODE_ENV}`), {
debug: true
})
console.log(file)
// 根據(jù)獲取的 key 給對(duì)應(yīng)的環(huán)境變量賦值
for (const key in file) {
process.env[key] = file[key]
}
} catch (e) {
console.error(e)
}
const API_LOCATION = process.env.API_LOCATION || '/api'
function resolve(dir) {
return path.join(__dirname, './', dir)
}
export default defineConfig({
root: './', // 項(xiàng)目根目錄(index.html 文件所在的位置)可以是一個(gè)絕對(duì)路徑,或者一個(gè)相對(duì)于該配置文件本身的相對(duì)路徑。
publicDir: 'public', // 作為靜態(tài)資源服務(wù)的文件夾.該值可以是文件系統(tǒng)的絕對(duì)路徑,也可以是相對(duì)于項(xiàng)目的根目錄的相對(duì)路徑。
base: './', // 公共基礎(chǔ)路徑。改值可以是絕對(duì)路徑或空字符串
mode: 'development',
optimizeDeps: { // 要預(yù)構(gòu)建的第三方依賴
include: []
},
resolve: {
alias: {
// 'vue': 'vue/dist/vue.esm.js', // 如果是模板解析的 - 使用這個(gè) vue:內(nèi)部為正則表達(dá)式 vue 結(jié)尾的
'vendor': resolve('src/vendor'),
'@': resolve('src'),
'~@': resolve('src'),
'~component': resolve('src/components'),
'~config': resolve('config'),
}
},
plugins: [
cjs2esmVitePlugin(), // 將 commonjs 轉(zhuǎn)化為 es module: 有報(bào)錯(cuò)
createVuePlugin({
jsx: true,
jsxOptions: {
injectH: false,
},
}),
minifyHtml(), // 壓縮 HTML
injectHtml({ // 入口文件 index.html 的模板注入
injectData: { // 模板注入的數(shù)據(jù)
htmlWebpackPlugin: {
options: {
isVite: true,
shotcut: '/static/img/favicon.png',
}
},
title: 'HMO 運(yùn)營(yíng)后臺(tái)',
},
}),
],
define: {
'process.env': process.env
},
server: {
host: 'liang.myweb.com',
open: true, // 是否自動(dòng)打開瀏覽器
port: process.env.PORT || config.dev.port,
proxy: {
[API_LOCATION]: {
target: 'http://127.0.0.1:8001',
rewrite: (path) => path.replace(API_LOCATION, '')
}
}
},
});
三.常用問題【踩坑日記??】
1. vite 目前要求入口文件必須是根目錄下的 index.html,如果之前的 webpack 入口文件同名,需要更改。
解決方案:vite.config.js:
import { injectHtml } from 'vite-plugin-html';
export default defineConfig({
plugins:[
injectHtml({ // 入口文件 index.html 的模板注入
injectData: { // 模板注入的數(shù)據(jù)
htmlWebpackPlugin: { // 取和 webpack 插件同名的對(duì)象 key,即可
options: {
isVite: true,
shotcut: '/static/img/favicon.png',
}
},
title: 'HMO 運(yùn)營(yíng)后臺(tái)'
},
})
]
})
webpack.xxx.js
new HtmlWebpackPlugin({
template: 'index.html',
inject: true,
isVite: false // 添加標(biāo)識(shí)
})
根目錄入口文件 index.html - ejs 模板
<% if (htmlWebpackPlugin.options.isVite) { %>
<script type="module" src="/src/main.js"></script>
<%}%>
2. 新版本報(bào) xx 錯(cuò):可切換舊版本,如 [email protected]
3.沒有導(dǎo)出命名?
Uncaught SyntaxError: The requested module '/config/index.js' does not provide an export named 'default'Uncaught SyntaxError: The requested module '/config/index.js' does not provide an export named 'default'
錯(cuò)誤原因:瀏覽器僅支持 esm,不支持 cjs
vite.config.js
import { cjs2esmVitePlugin } from 'cjs2esmodule'
export default defineConfig({
plugins: [
cjs2esmVitePlugin(), // 將 commonjs 轉(zhuǎn)化為 es module
]
})
如果有 require.xx 的按需加載寫法還可以修改成 import 的,案例如下:
const subjectList = r => require.ensure( [], () => r(require('@/pages/xxx/subject/list.vue')), 'subject' );
// 改為:Vue 動(dòng)態(tài)組件 component: ()=>import()
const subjectList = () => import(/* webpackChunkName: "subject" */ '@/pages/xxx/subject/list.vue')
const arr = [
{
path: '/subject/list',
name: 'subject/list',
component: subjectList
meta: {...}
}
];
export default arr;
4. proxy 使用 http-proxy。完整選項(xiàng)詳見 此處.
案例:
proxy: {
'/rest': {
target: 'http://my.web.com/',
changeOrigin: true,
bypass: (req, res, proxyOption) => {
console.log(`當(dāng)前請(qǐng)求代理:${req.url} -> ${proxyOption.target}`);
},
},
}
5. ts 文件報(bào)錯(cuò)?
驗(yàn)證是否配置了 vite 的 ts 處理
"compilerOptions": {
"types": ["vite/client"]
}
6. 全局環(huán)境變量報(bào)錯(cuò)?
// const isProd = ENV === 'production'; // webpack - dev 環(huán)境變量
// const isProd = import.meta.env.PROD; // vite - dev 環(huán)境變量
// 可以避開上面????的,采用 NODE_ENV 來區(qū)分:
const isProd = process.env.NODE_ENV === 'production';
那么我們啟動(dòng)的時(shí)候:"dev": "NODE_ENV=development vite"
或者可以探索一下社區(qū)的 babel 插件:babel-preset-vite【包含以下兩個(gè)功能】babel-plugin-transform-vite-meta-envbabel-plugin-transform-vite-meta-glob
7. 看一些打印出來的日志&錯(cuò)誤等?
cli --debug,或者 vite.config.js 配置打印相關(guān)參數(shù)
8. 引入文件,比如.vue 的時(shí)候,不可以省略擴(kuò)展名?
是的!!!不是他們不會(huì)做,是他們不想做??,就是這么設(shè)計(jì)的,具體請(qǐng)戳這里, 尤大佬推特解釋然后加上 resolve.extensions: ['.vue'] 直接在控制臺(tái)報(bào)錯(cuò):所以沒用。。。
error: No loader is configured for ".vue"
害!老老實(shí)實(shí)加上擴(kuò)展名!【在線??】
方便的全局加上擴(kuò)展名方法如下:鏈接
9. less 文件找不到?
[vite] Internal server error: '~@/styles/var.less' wasn't found.
(1)確定已經(jīng)支持 less:npm install -D less(2)別忘了 resolve.alias 也加上一個(gè):'~@': resolve('src')
10. 如何支持 jsx?
vite.config.js
import { createVuePlugin } from 'vite-plugin-vue2';
createVuePlugin({
jsx: true, // 配置 jsx
jsxOptions: {
injectH: false,
},
})
Vue.component('my-component',{
render () {
return (<div>my template</div>)
}
})
11. 根據(jù)環(huán)境變量配置代理?
(1)cross-env 來跨平臺(tái)設(shè)置環(huán)境變量
1. 安裝 cross-env
npm i cross-env -D
(2)加載環(huán)境變量文件。它能將環(huán)境變量中的變量從 .env 文件加載到 process.env 中
2. 安裝 dotenv
npm i dotenv -D
(3)config/.env.development 配置變量
NODE_ENV = development
API_LOCATION = /api
LOGOUT_PC_LOCATION = http://user.myweb.com/login
CRM_ADDRESS = http://crm.myweb.com
(4)配置 vite.config.ts
try {
// 根據(jù)環(huán)境變量加載環(huán)境變量文件
const file = dotenv.parse(fs.readFileSync(`./config/.env.${process.env.NODE_ENV}`), {
debug: true
})
console.log(file)
// 根據(jù)獲取的 key 給對(duì)應(yīng)的環(huán)境變量賦值
for (const key in file) {
process.env[key] = file[key]
}
} catch (e) {
console.error(e)
}
const API_LOCATION = process.env.API_LOCATION || '/api'
..... 此處省略
export default defineConfig({
server: {
proxy: {
[API_LOCATION]: {
target: 'http://127.0.0.1:8001',
rewrite: (path) => path.replace(API_LOCATION, '') // 根據(jù)環(huán)境變量配置代理
}
}
}
})
(5)package.json 啟動(dòng) script
"vite": "cross-env NODE_ENV=development vite"
12. 環(huán)境變量報(bào)錯(cuò)?
原來 webpack 使用的環(huán)境變量 process.env,vite 沒有這個(gè),所以報(bào)錯(cuò)
Uncaught ReferenceError: process is not defined
vite 使用的時(shí)候import.meta.env, 但是我們老的代碼不想動(dòng)怎么辦?其實(shí) vite 也還是留了口子給我們定義全局變量[類型不能是 function]
export default defineConfig({
// ...
define: {
'process.env': {}
}
})
13. anything else?
..... bug 無止境,很多都是非通用問題,都是引入 vite 后發(fā)現(xiàn)的系統(tǒng)本身的一些問題,這里就不一一舉例了。后續(xù)會(huì)追蹤更多通用問題
內(nèi)推社群
我組建了一個(gè)氛圍特別好的騰訊內(nèi)推社群,如果你對(duì)加入騰訊感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行面試相關(guān)的答疑、聊聊面試的故事、并且在你準(zhǔn)備好的時(shí)候隨時(shí)幫你內(nèi)推。下方加 winty 好友回復(fù)「面試」即可。
