vite + Vue3 遇到的一些问题汇总

17,245 阅读2分钟

image.png

啰嗦一下,vitevue3已经出来很长一段时间了,一直没生产实践过,之前一直都是使用 vue2,通过这次实践,个人感觉还是喜欢使用那经典且深入人心的 vue2,或许这是众多顽固派不愿跟随技术更新的最后的倔强吧。不过 vue3 的<script setup>语法糖挺好使的,可以减少许多代码,不用在 return 出去。

1. 项目开始的准备

  1. 熟悉下vue3 文档vite 文档
  2. vsoce 编辑器安装Vue Language Features (Volar)拓展

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预处理器等

  1. vite 中使用 less/scss/sass/stylus 等 css 预处理器

    vitejs.cn/guide/featu… 直接进行安装,不用像 webpack 那样安装 loader 和配置

npm install -D less
<style lang="less">
/* use less */
</style>
  1. vite 中使用 postcss

    vitejs.cn/config/#css… vite 中已集成了 postcss,无需再单独安装

  2. vite 中使用 autoprefixer

import autoprefixer from "autoprefixer";

export default defineConfig({
  css: {
    postcss: {
      plugins: [autoprefixer],
    },
  },
});

2.3 vite 打包将 js 和 css 文件夹分离

默认打包后,js 和 css 是全部混在一起的,对强迫症很难受

github.com/vitejs/vite…

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配置文件异步配置

官方默认是支持的

cn.vitejs.dev/config/#asy…

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来完成。

  1. 安装
npm i vite-plugin-html -D
  1. 使用(比如不同环境下动态注入高德地图的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 目录

cn.vitejs.dev/config/#pub…

  1. 引用静态资源

在 vue 文件中templatestyle中以相对路径和绝对路径两种方式引用静态资源

<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>
  1. public 目录

    cn.vitejs.dev/guide/asset…

  • 放在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

github.com/vitejs/vite…

// 这里如何像webpack那样支持自定义自定义chunkname,打包后文件名是自定义的名字
const Login = () => import("@/views/Login");

未解决,知道的,麻烦分享一下

3. 关于 vue3 遇到的一些问题

3.1 深度选择器

v3.cn.vuejs.org/api/sfc-sty…

之前的 '>>>' 已经被废弃,现在使用:deep()伪类

<style scoped>
.a :deep(.b) {
  /* ... */
}
</style>

3.2 vue-router4.x路由 404 匹配

捕获所有路由或 404 Not found 路由

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组件

vant-contrib.gitee.io/vant/v3/#/z…

<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名称

forum.vuejs.org/t/vue3-2-sc…

其实可以写两个 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;
}

4. 参考资料

  1. vue3 文档
  2. vite 文档
  3. vue-router4 文档