最近在迁移开源项目 vue-admin 到最新技术上的时候,遇到了一些技术隐形的问题,毕竟是最新的技术点,难免有些疑难杂症,所以分享给有需要的朋友
vue-router
vue-router 4.x
之后剔除了路由路径匹配,什么意思呢?看个代码片段
import { createRouter, createWebHashHistory } from "vue-router";
const base = [
{
path: "https://github.com/Hansen-hjs/vue-admin",
name: "baidu",
component: () => import("../views/404.vue"), // 这里一定要给个组件(虽然不会显示),不然会卡死
meta: {
icon: "star",
title: "跳转外部链接"
}
}
]
const router = createRouter({
history: createWebHashHistory(),
routes: base
})
复制代码
这个时候会警告并卡死,因为现在不能匹配path
为非/
开头的路径了,这时候需要在外链前面加个/
即可,然后对应的获取路由的时候做对应的的处理即可,像这样:
const base = [
{
path: "/https://github.com/Hansen-hjs/vue-admin",
...more
}
]
复制代码
同时vue-router 4.x
加入以往没有的新api
:removeRoute
现在可以轻松的做退出登陆删除之前动态拼接的路由了,不过这个api
是以路由定义中name
作为删除唯一键值的,所以我们在定义路由的时候最好写上,且唯一,删除路由操作可以看代码片段
因为改用了Composition API
,所以路由的使用方式变了,不过需要注意的是:useRoute
和useRouter
这两个hooks
函数必选要写在顶层,如果是写在代码运行之后的函数中,是获取不到的,看下面代码:
import { useRouter, useRoute } from "vue-router";
import { defineComponent } from "vue";
export default defineComponent({
setup() {
const route = useRoute();
const router = useRouter();
function getRouterInfo() {
// const route = useRoute(); // 如果写在这里,是获取不到对象的
// const router = useRouter(); // 如果写在这里,是获取不到对象的
console.log(route, router);
}
return {
getRouterInfo
}
}
})
复制代码
Vite 与 webpack 使用注意点
node.js 文件系统
以往在webpack
环境中,是可以在任意地方使用import path from "path"
或者import fs from "fs"
来做一些文件处理的。现在Vite
环境有些特殊的区分,就是在浏览器运行的文件,包括任何.js
、.vue
或者.ts
,是不能正常使用import path from "path"
或者import fs from "fs"
等一些node.js
模块的,必需要改用Vite
环境特有的import.meta.globEager
和import.meta.glob
作为文件处理api
使用。举个例子,当前项目需要读取src/icons/svg/
目录下的所有svg
名称,那么就要这样写:
<template>
<div v-for="item of svgIcons" :key="item">
<svg-icon :name="item" />
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
const svgFileReg = /(?<=(svg\/)).*?(?=(.svg))/;
/** 获取所有`svg`名称 */
function getSvgNames() {
const svgInfo = import.meta.globEager("../../icons/svg/*.svg");
const svgs = Object.keys(svgInfo);
const names = svgs.map(value => {
const res = value.match(svgFileReg)![0];
return res;
});
return names;
}
export default defineComponent({
name: "Icons",
setup() {
return {
svgIcons: getSvgNames()
}
}
})
</script>
复制代码
说完浏览器运行文件,还有一个就是在vite.config.ts
文件中,import.meta.globEager
和import.meta.glob
这个两个api
就用不了了,只能用node.js
的文件系统模块,也就是上说的那些import fs from fs
。同样是当前项目的svg
组件,这里要单独写一个svg
的加载插件(vite插件),那么要像这样:
import { readFileSync, readdirSync } from "fs";
// svg-sprite-loader 这个貌似在 vite 中用不了
// 该文件只能作为`vite.config.ts`导入使用
// 其他地方导入会报错,因为浏览器环境不支持`fs`模块
/** `id`前缀 */
let idPerfix = "";
const svgTitle = /<svg([^>+].*?)>/;
const clearHeightWidth = /(width|height)="([^>+].*?)"/g;
const hasViewBox = /(viewBox="[^>+].*?")/g;
const clearReturn = /(\r)|(\n)/g;
/**
* 查找`svg`文件
* @param dir 文件目录
*/
function findSvgFile(dir: string): Array<string> {
const svgRes = []
const dirents = readdirSync(dir, {
withFileTypes: true
})
for (const dirent of dirents) {
if (dirent.isDirectory()) {
svgRes.push(...findSvgFile(dir + dirent.name + "/"));
} else {
const svg = readFileSync(dir + dirent.name).toString().replace(clearReturn, "").replace(svgTitle, (value, group) => {
// console.log(++i)
// console.log(dirent.name)
let width = 0;
let height = 0;
let content = group.replace(clearHeightWidth, (val1: string, val2: string, val3: number) => {
if (val2 === "width") {
width = val3;
} else if (val2 === "height") {
height = val3;
}
return "";
}
)
if (!hasViewBox.test(group)) {
content += `viewBox="0 0 ${width} ${height}"`;
}
return `<symbol id="${idPerfix}-${dirent.name.replace(".svg", "")}" ${content}>`;
}).replace("</svg>", "</symbol>");
svgRes.push(svg);
}
}
return svgRes;
}
/**
* `svg`打包器
* @param path 资源路径
* @param perfix 后缀名(标签`id`前缀)
*/
export function svgBuilder(path: string, perfix = "icon") {
if (path.trim() === "") return;
idPerfix = perfix;
const res = findSvgFile(path);
// console.log(res.length)
return {
name: "svg-transform",
transformIndexHtml(html: string) {
return html.replace("<body>",
`<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
${res.join("")}
</svg>`)
}
}
}
复制代码
最后在vite.config.ts
文件中使用:
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import vueJsx from "@vitejs/plugin-vue-jsx";
import { svgBuilder } from "./src/icons/loader"; // 这里是上面写的`svg`加载插件
export default defineConfig({
plugins: [vue(), vueJsx(), svgBuilder("./src/icons/svg/")],
})
复制代码
npm run build 报错
这个问题比较诡异,npm run dev
连警告都没有,npm run build
打包居然报错了,后面摸索了一下,原来在tsconfig.json
中,需要在include
的所有路径前面加个/
,我佛,webpack
环境表示没有出现过这类问题。像这样:
{
...more,
// 这里所有的路径前面都要加上 / 猜测应该是 vite 在处理文件的时候,用的是严格路径校验
"include": ["/src/**/*.ts", "/src/**/*.d.ts", "/src/**/*.tsx", "/src/**/*.vue"]
}
复制代码
但是呢,在所有路径前面加上/
之后又导致在开发中无法正常配置ts
的一些类型检测,所以又得把前面的/
给手动删掉,等npm run build
的时候再加上去,不知道这是不是vite
的一个bug
,已经向作者提问了…
打包后依然是使用最新的ES模块
最后需要注意的是,我们在开发环境使用的原生ES模块
并不会因为打包后转成以往的兼容模式,意思就是只能用服务器形式来打开,并且不支持IE
(好像是废话),仔细看下构建后的index.html
引用的js
标签就明白了,如果追求兼容、稳定,建议还是用vue 2.x
+vue-cli
…