本文整理 vue2 项目接入 vite2 需要注意的事项。
首先我们需要在项目中安装 vite
;其次,要支持 vue2 还需要安装 vite-plugin-vue2
。
npm install vite vite-plugin-vue2 -D
然后,需要新建一个 vite.config.js
文件替换掉 vue.config.js
文件。其基础内容如下:
import { defineConfig } from 'vite';
import { createVuePlugin } from 'vite-plugin-vue2';
export default defineConfig({
plugins: [
createVuePlugin(),
],
});
修改 package.json
的运行指令:
"scripts": {
"serve": "vite",
"build": "vite build",
"build:dev": "vite build --mode dev",
"build:rel": "vite build --mode release",
"build:pro": "vite build --mode production"
}
这里其实就是把 vue-cli-service
替换为 vite
。
然后,还需要将 public
目录下的 index.html
文件移动到最外层目录下(如果不想移动需要配置 root),然后引入 main.js
文件。
<body>
<div id="app">div>
<script type="module" src="/src/main.js">script>
body>
接下来开始增加常用功能。
我们在导入 vue
、js
等文件时往往喜欢省略类型后缀,这需要配置 extensions
export default defineConfig({
resolve: {
extensions: ['.vue', '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'],
},
});
import path from 'path';
const resolve = dir => path.resolve(__dirname, dir);
export default defineConfig({
resolve: {
alias: {
'@': resolve('src'),
},
},
});
这一功能模块需要注意的地方就比较多了。
实际业务中,往往需要配置以下几个环境文件
.env.development // 开发环境
.env.dev // 测试环境
.env.release // 预发布环境
.env.production // 线上环境
以开发环境为例,其大致内容如下:
NODE_ENV=development
VUE_APP_ENV=development
...
NODE_ENV
变量将模式设置为 development,VUE_APP_ENV
为自定义变量。
关于这块内容可参考:Vue CLI 模式和环境变量
接下来就要开始说说接入 vite 前后的差异了。
接入前:前缀为 VUE_APP_
接入后:前缀为 VITE_
若不想修改现有代码环境变量的前缀,可以在 vite.config.js 中配置
export default defineConfig({
envPrefix: 'VUE_APP_',
});
接入前:通过 process.env.VUE_APP_ENV
可以获取到值。
接入后:通过 import.meta.env.VUE_APP_ENV
获取。
接入前:在 vue.config.js 中访问,通过 process.env.VUE_APP_ENV
就能获取。
接入后:在 vite.config.js 中访问,不能通过 import.meta.env.VUE_APP_ENV
获取。
那如何解决 vite 的这个问题呢?两种方式:
loadEnv
获取dotenv
插件(1)loadEnv
获取
import { defineConfig, loadEnv } from 'vite';
function getDefineConfig(env) {
defineConfig({
// ...
});
}
export default ({ mode }) => {
const env = loadEnv(mode, process.cwd()).VUE_APP_ENV;
return getDefineConfig(env);
}
注:这里的 mode
就是 env 文件中的 NODE_ENV
,即模式。process.cwd()
指当前执行 node 命令时候的文件夹地址,这和 __dirname
(被执行的 js 文件的地址)有所不同。
这里我们根据不同环境下的 VUE_APP_ENV 值加载不同的配置。需要注意的是 defineConfig
应只执行一次,否则后一次执行的配置将覆盖前一次执行的配置,而与你最终的 return 无关,比如这样:
function getDefineConfig(env) {
let devConfig = defineConfig({
// ...
});
let proConfig = defineConfig({
// ...
});
// 不管你 env 的值是啥,最终生效的都会是 proConfig
return env === 'development' ? devConfig : proConfig;
}
(2)使用 dotenv
插件
先执行安装
npm install dotenv -D
配置如下:
export default ({ mode }) => {
require('dotenv').config({ path: `./.env.${mode}` });
return getDefineConfig(process.env.VUE_APP_ENV);
};
这样,我们就能通过 process.env.{configName}
的方式访问了(业务代码中仍然不能这样访问)。不过这种方式需要多安装一个插件,所以推荐使用第一种方法。
vite 使用 ESM
(import 方式导入)的模块化方案,不支持 CommonJS
(require 方式导入) 方案,所以我们不能在业务代码中使用 require
。
往往我们需要使用 htmlWebpackPlugin
插入一些内容,比如 CDN、线上代码错误监控等。
DOCTYPE html>
<html lang="zh">
<head>
<% if(htmlWebpackPlugin.options.env==="production") { %>
<script type="text/javascript">
// 错误监控相关代码
script>
<% } %>
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>" type="text/javascript">script>
<% } %>
head>
<body>
<div id="app">div>
body>
html>
在 vite 下要实现代码挂载的功能该怎么改呢?这里我整理了两种方式:
vite-plugin-html
插件vite-plugin-html-config
插件(1)vite-plugin-html
插件
安装好插件后,配置:
import { injectHtml } from 'vite-plugin-html';
export default defineConfig({
plugins: [
injectHtml({
injectData: {
cdn: [
js: [
'https://xx/CDN/js/jquery.js',
// ...
],
],
env: '', // 这个值应该是动态的, 这里为了简化 demo, 未做
},
}),
],
});
在 html 中使用
DOCTYPE html>
<html lang="zh">
<head>
<% if(env==="production") { %>
<script type="text/javascript">
// 错误监控相关代码
script>
<% } %>
<% for (var i in cdn&&cdn.js) { %>
<script src="<%= cdn.js[i] %>" type="text/javascript">script>
<% } %>
head>
<body>
<div id="app">div>
body>
html>
通过比较,可以发现,这里省略了 htmlWebpackPlugin.options.
前缀。
相关 ejs
语法可见:github ejs。
当我们要根据环境加载 js 文件时,可这样写:
<%- VUE_APP_TITLE=="production" ? '<script type="text/javascript" src="/webfunny.min.js">script>' : '' %>
需要注意的是使用的是 <%-
开头。
(2)vite-plugin-html-config
插件
安装好插件后,配置:
import htmlConfig from 'vite-plugin-html-config';
const htmlPluginConfig = htmlConfig({
links: [
{
rel: 'stylesheet',
href: 'https://xx/CDN/css/element-ui2.13.0/index.css',
},
],
headScripts: [
`console.log('hi')` // 假设这是错误监控相关代码
],
scripts: [
{
src: 'https://xx/CDN/js/jquery.js',
},
],
});
export default defineConfig({
plugins: [
htmlPluginConfig,
],
});
通过此配置,我们就不用在 html 上做操作了,它会自动将代码挂载到对应位置。
更多配置见:github vite-plugin-html-config
可以根据各自情况选择使用方式,但我个人更偏向方式一。
cdn 这块我们还得做配置,否则打包后的文件仍然包含 cdn 相关的代码。
需要安装 rollup-plugin-external-globals
,@rollup/plugin-commonjs
插件
npm install rollup-plugin-external-globals @rollup/plugin-commonjs -D
安装好后做如下配置:
import commonjs from '@rollup/plugin-commonjs';
import externalGlobals from 'rollup-plugin-external-globals';
const externals = {
vue: 'Vue',
'vue-router': 'VueRouter',
axios: 'axios',
vuex: 'Vuex',
'element-ui': 'ELEMENT',
lodash: '_',
echarts: 'echarts',
'v-charts': 'VeIndex',
qs: 'Qs',
};
export default defineConfig({
build: {
rollupOptions: {
external: ['vue', 'vue-router', 'axios', 'vuex', 'element-ui', 'lodash', 'echarts', 'v-charts', 'qs'],
plugins: [
commonjs(), // 转换 CJS -> ESM, 通过 cdn 引入的没法转
externalGlobals(externals),
],
},
},
});
注:这里的 vue
是 import xx from yy
中的 yy 包名。Vue
是文件导出的全局变量名字,查看源码或者参考作者文档可以获得。
webpack 中可以通过 require.context
导入模块
let routes = [];
const files = require.context('./modules', false, /(.).js/);
files.keys().forEach(key => {
const fileData = files(key).default;
routes[fileData.index] = fileData.routers;
});
vite 也提供了类似功能(详见:Glob 导入),有两种方式:
import.meta.globEager
,直接引入所有的模块import.meta.glob
,动态导入let routes = [];
// 方式一
const files = import.meta.globEager('./modules/*.js');
for (const key in files) {
const fileData = files[key].default;
routes[fileData.index] = fileData.routers;
}
// 方式二
const files2 = import.meta.glob('./modules/*.js');
for (const key in files2) {
files2[key]().then(res => {
const fileData = res.default;
routes[fileData.index] = fileData.routers;
});
}
js 引入图片:
import imgUrl from '@/assets/img/logo.png';
css 引入图片:
.logo {
background: url('@/assets/img/logo.png') no-repeat;
}
使用 vite-plugin-compression
做 gzip 压缩。
import compressPlugin from "vite-plugin-compression";
export default defineConfig({
plugins: [
compressPlugin({
filter: /\.(js|css)$/i, // 压缩文件类型
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz',
}),
],
});
具体配置可见 github vite-plugin-compression。
格式和以前没有区别,可以直接将 vue-cli 的 proxy
直接复制过来即可。
export default defineConfig({
server: {
host: '0.0.0.0',
port: 8080,
proxy: {
'testapi': {
target: 'https://api.xx.com',
changeOrigin: true,
pathRewrite: {
[`^/testapi`]: `/testapi`,
},
},
},
},
});
warning: "@charset" must be the first rule in the file
警告。解决方法一:
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
charset: false,
},
},
},
});
方法一不行?再试试方法二:
export default defineConfig({
css: {
postcss: {
plugins: [
{
postcssPlugin: 'internal:charset-removal',
AtRule: {
charset: atRule => {
if (atRule.name === 'charset') {
atRule.remove();
}
},
},
},
],
},
},
});
这个是打包后预览项目时控制台出现的错误,出现这个问题的原因是:Vite 不会重写从外部文件导入的内容,我们需要使用支持 ESM 编译的 CDN
。
这里我们需要用到 https://esm.sh/ 这个网站,然后修改我们的 cdn 配置:
import { injectHtml } from 'vite-plugin-html';
export default defineConfig({
plugins: [
injectHtml({
injectData: {
cdn: [
js: [
'https://esm.sh/[email protected]',
// ...
],
],
},
}),
],
});
https://esm.sh/[email protected]
返回的内容如下:
/* esm.sh - [email protected] */
export * from "https://cdn.esm.sh/v64/[email protected]/es2021/vue.js";
export { default } from "https://cdn.esm.sh/v64/[email protected]/es2021/vue.js";
另外,我们在挂载