啰嗦一下,
vite
和vue3
已经出来很长一段时间了,一直没生产实践过,之前一直都是使用 vue2,通过这次实践,个人感觉还是喜欢使用那经典且深入人心的 vue2,或许这是众多顽固派不愿跟随技术更新的最后的倔强吧。不过 vue3 的<script setup>
语法糖挺好使的,可以减少许多代码,不用在 return 出去。
1. 项目开始的准备
2. 关于 vite 遇到的一些问题
vite 的最大感觉就是“快”,本地开发比 webpack 快很多,因为不用打包所有看模块再启动,浏览器原生支持 ES 模块,文件修改后即生效,即使修改
vite.config.js
配置文件,也可以监听到,自动重启生效。
2.1 关于打包成 ES5 代码
通过build.target
配置项指定构建目标,传统浏览器可以通过插件@vitejs/plugin-legacy
来支持,最低仅支持到es2015
,即es6
,不能打包成es5
的代码。打包出来的代码<script type=module></script>
有type=module
属性,可以直接在里面使用模块化调用。有知道怎么处理成 es5 的,可以分享出来给大家。
// vite.config.js
import { defineConfig } from "vite";
// https://github.com/vitejs/vite/tree/main/packages/plugin-legacy
import legacy from "@vitejs/plugin-legacy";
export default defineConfig({
plugins: [
legacy({
targets: ["ie >= 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
}),
],
build: {
target: "es2015", // 默认 "modules"
},
});
2.2 vite 中使用css
预处理器等
- vite 中使用 less/scss/sass/stylus 等 css 预处理器
vitejs.cn/guide/featu… 直接进行安装,不用像 webpack 那样安装 loader 和配置
npm install -D less
<style lang="less">
/* use less */
</style>
-
vite 中使用 postcss
vitejs.cn/config/#css… vite 中已集成了 postcss,无需再单独安装
-
vite 中使用 autoprefixer
import autoprefixer from "autoprefixer";
export default defineConfig({
css: {
postcss: {
plugins: [autoprefixer],
},
},
});
2.3 vite 打包将 js 和 css 文件夹分离
默认打包后,js 和 css 是全部混在一起的,对强迫症很难受
export default defineConfig({
build: {
rollupOptions: {
output: {
chunkFileNames: "static/js/[name]-[hash].js",
entryFileNames: "static/js/[name]-[hash].js",
assetFileNames: "static/[ext]/[name]-[hash].[ext]",
},
},
},
});
2.4 vite 根目录vite.config.js
配置文件异步配置
官方默认是支持的
export default defineConfig(async ({ command, mode }) => {
const data = await asyncFunction();
return {
// 构建模式所需的特有配置
};
});
2.5 vite 根目录vite.config.js
配置文件读取env
变量
在配置文件中直接使用import.meta.env.xxx
这样读取是报错的,不过有了上面的异步配置,读取env
变量就方便了,我们可以直接使用fs
直接获取
.env
文件
# PROXY_URL
VITE_PROXY_URL=http://xxxxxx/
# DBPATH_ENV
DBPATH_ENV=XXXX
vite.config.js
文件
const fs = require("fs");
const path = require("path");
const { promisify } = require("util");
const fsState = promisify(fs.stat);
const readFile = promisify(fs.readFile);
// 定义一个函数,读取.env文件中的内容
async function getEnvConfig(vite_env_key) {
const envFilePath = path.resolve(__dirname, "./.env");
const [notExistEnvFile, existEnvFile] = await fsState(envFilePath)
.then((data) => [null, data])
.catch((e) => [e, null]);
if (notExistEnvFile || existEnvFile.size <= 0) return;
const envContent = await readFile(envFilePath, "utf8");
if (!envContent) return;
const envContentArr = envContent
.split("\n")
.filter((str) => !str.startsWith("#")) // 过滤掉注释行
.filter(Boolean);
const resultKey = envContentArr.find((item) => item.includes(vite_env_key));
return resultKey ? resultKey.split("=")[1] : null;
}
const resolveConf = async () => {
// 读取 .env 文件中的VITE_PROXY_URL的值
const PROXY_URL = await getEnvConfig("VITE_PROXY_URL");
return {
server: {
host: "0.0.0.0",
port: 3000,
open: true,
proxy: {
"/api": {
target: PROXY_URL,
changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
};
};
export default defineConfig(resolveConf());
2.6 vite 项目index.html
中使用env
环境变量
如何像webpack
项目的html-webpack-plugin
那样使用<%= htmlWebpackPlugin.options.title %>
,这种方式动态注入变量,vite 中可以使用vite-plugin-html
来完成。
- 安装
npm i vite-plugin-html -D
- 使用(比如不同环境下动态注入高德地图的
key
)
vite.config.js
配置文件
import { minifyHtml, injectHtml } from "vite-plugin-html";
const AMAP_KEY = "xxx";
export default defineConfig({
plugins: [
injectHtml({
data: {
title: "hello",
// 高德地图
injectAMapScript: `<script src="https://webapi.amap.com/maps?v=1.4.15&key=${AMAP_KEY}"></script>`,
},
}),
],
});
index.html
中使用 ejs 模板引擎动态注入
<!DOCTYPE html>
<html lang="en">
<head>
<title><%- title %></title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<%- injectAMapScript %>
</body>
</html>
2.7 引用静态资源和 public 目录
- 引用静态资源
在 vue 文件中template
和style
中以相对路径和绝对路径两种方式引用静态资源
<template>
<!-- 相对路径 -->
<img src="./assets/img/test.png" />
<!-- 绝对路径 -->
<img src="/src/assets/img/test.png" />
</template>
<style scoped>
#app {
background-image: url("./assets/img/test.png");
}
</style>
- public 目录
- 放在
public
目录下的文件应使用绝对路径引用,例如public/icon.png
应该使用/icon.png
public
中的资源不应该被JavaScript
文件引用
<img src="/logo.png" />
2.8 打包生产环境移除 console 和 debugger
export default defineConfig({
build: {
// 生产环境移除console
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
},
});
2.9 异步 import 如何像 webpack webpackChunkName 那样支持自定义 chunkname
// 这里如何像webpack那样支持自定义自定义chunkname,打包后文件名是自定义的名字
const Login = () => import("@/views/Login");
未解决,知道的,麻烦分享一下
3. 关于 vue3 遇到的一些问题
3.1 深度选择器
之前的 '>>>' 已经被废弃,现在使用:deep()
伪类
<style scoped>
.a :deep(.b) {
/* ... */
}
</style>
3.2 vue-router4.x
路由 404 匹配
import { createRouter, createWebHistory } from "vue-router";
const Result = () => import("@/views/Result");
const router = createRouter({
history: createWebHistory(),
routes: [
{
// 注意这里,404页面匹配规则和以前不相同,要采用这种配置方式才行
path: "/:pathMatch(.*)*",
component: Result,
name: "Result",
},
],
});
export default router;
3.3 <script setup>
特性中使用vant3.x
组件
在<script setup>
中可以直接使用 Vant 组件,不需要进行组件注册
<template>
<Button />
</template>
<script setup>
import { Button } from "vant";
</script>
3.4 vite 启动后关于defineEmits
的警告
defineEmits
is a compiler macro and no longer needs to be importe
删掉defineEmits
的引用即可
- import { reactive, defineEmits } from 'vue';
+ import { reactive } from 'vue';
// 直接使用defineEmits
const emit = defineEmits(['test1', 'test2']);
emit('test1', 'hello1');
3.5 vue3 和echarts5
动态更新数据报错
barPolar.js:63 Uncaught TypeError: Cannot read properties of undefined (reading 'type')
- 造成报错的原因是 vue3 中使用 proxy 的方式监听响应式,charts 实例会被在 vue 内部转换成响应式对象
- 在初始化的时候可以使用
markRaw
指定为非响应式即可
<template>
<div ref="lineChartDomRef"></div>
</template>
<script setup>
import { markRaw, ref, onMounted } from "vue";
import * as echarts from "echarts";
const lineChartInsRef = ref();
const lineChartDomRef = ref();
onMounted(() => {
// 初始化时使用markRaw,后面使用lineChartInsRef.value实例更新时,就不会报错了
const option = {
// ...
};
lineChartInsRef.value = markRaw(echarts.init(lineChartDomRef.value));
lineChartInsRef.value.setOption(option);
window.addEventListener("resize", () => {
lineChartInsRef.value.resize();
});
});
</script>
3.6 Vue3.x 使用<script setup>
如何设置组件name
名称
其实可以写两个 script 标签,下面两个可以并存
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "Test",
});
</script>
<script setup>
// ...
</script>
3.7 vue3 中的 vue-router4.x 下的 keep-alive 写法
[Vue Router Warn]: <router-view> can no longer be used directly inside <transition> or <keep-alive>
之前这样写,会报警告
<keep-alive>
<RouterView />
</keep-alive>
根据提示,得这样写:
<template>
<router-view v-slot="{ Component }">
<!-- 缓存name名称为aaa和bbb的组件 -->
<keep-alive :include="['aaa', 'bbb']">
<component :is="Component" />
</keep-alive>
</router-view>
</template>
3.8 vue-router4 监听路由变化 onBeforeRouteUpdate
import { onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';
onBeforeRouteUpdate((to, from, next) => {
if (from.name === 'MachineList') {
...
}
next();
});
3.9 nginx 线上部署,刷新后页面 404
修改 nginx 配置
location / {
root /usr/share/nginx/dist; # 服务默认启动目录
index index.html index.htm; # 默认访问文件
+ try_files $uri /index.html; # 防止浏览器刷新后,页面404
client_max_body_size 100m;
}