最近在遷移開源項目 vue-admin 到最新技術上的時候,遇到了一些技術隱形的問題,畢竟是最新的技術點,不免有些疑難雜症,因此分享給有須要的朋友javascript
預覽效果css
node.js 文件系統html
瀏覽器環境文件操做api
使用,webpack
對應Vite
vue
// webpack
require.context
// 對應 vite 二選一,詳細使用說明看文檔 https://vitejs.dev/guide/features.html#glob-import
import.meta.globEager
import.meta.glob
複製代碼
舉個例子,當前項目須要讀取src/icons/svg/
目錄下的全部svg
名稱,那麼就要這樣寫:java
<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
的文件系統模塊,這裏跟webpack
環境基本一致。一樣是當前項目的svg
組件,這裏要單獨寫一個svg
的加載插件(vite插件),那麼要像這樣:node
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
文件中使用:webpack
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 報錯git
這個問題比較詭異,npm run dev
連警告都沒有,npm run build
打包竟然報錯了,後面摸索了一下,原來在tsconfig.json
中,須要在include
的全部路徑前面加個/
,webpack
環境表示沒有出現過這類問題。像這樣:github
{
...more,
// 這裏全部的路徑前面都要加上 / 猜想應該是 vite 在處理文件的時候,路徑校驗規則不太同樣
"include": ["/src/**/*.ts", "/src/**/*.d.ts", "/src/**/*.tsx", "/src/**/*.vue"]
}
複製代碼
可是呢,在全部路徑前面加上/
以後又致使在開發中沒法正常配置ts
的一些類型檢測,因此又得把前面的/
給手動刪掉,等npm run build
的時候再加上去,不知道這是否是vite
的一個bug
。web
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
}
}
})
複製代碼
不肯定其餘庫的hooks
使用方式是否也是須要把聲明寫在頂層,但vue-router
是須要的。
以前webpack
環境中導出的方式依然不變,稍做變更的是文件命名,例如variables.scss
要做爲js/ts
中導入使用,只須要在名字後面加個.module
便可,像這樣:variables.module.scss
$--color-primary: #1890FF;
// The :export directive is the magic sauce for webpack
// https://mattferderer.com/use-sass-variables-in-typescript-and-javascript
:export {
theme: $--color-primary;
}
複製代碼
其餘非.scss
文件導入使用
import variables from "../styles/variables.module.scss";
console.log(variables) // 輸出 { theme: "#1890FF" }
複製代碼
注意事項
在main.ts
中引入帶有module.scss
後綴的文件做爲樣式引入使用,默認是不會加載到<style>
去的,因此須要在沒有module.scss
後綴的文件中@import ./xxx.module.scss
,而後再在main.ts
引入該文件
npm run build 報錯
目前還不肯定是什麼緣由,npm run dev
的時候正常使用,可是npm run build
就報錯,出現:
[vite:css-post] value.replace is not a function
複製代碼
因此我在項目中放棄xxx.module.scss
這種命名導入使用方式,而是採用直接暴力的解決方案:正常導入xxx.scss
以後,寫一個提取導出變量的工具函數,這樣就實現相同的功能了。
處理導出工具函數:
/** * 格式化`.scss`文件中導出的變量 * @param val */
function formatStyleModule(val: string) {
val = val.replace(/:export {/, ":export{").replace(/(\n|\t|\s)*/g, "");
const matchInfo = val.match(/:export{(\S*)}/);
if (matchInfo && matchInfo[1]) {
let match = matchInfo[1];
if (match[match.length - 1] == ";") {
match = match.slice(0, match.length - 1);
}
val = match.replace(/;/g, `","`).replace(/:/g, `":"`);
}
// console.log(`{"${val}"}`);
return JSON.parse(`{"${val}"}`);
}
複製代碼
使用示例:
import style from "../styles/variables.scss";
const variables = formatStyleModule(style);
console.log(variables);
複製代碼
第三方插件庫
像一些比較老的插件,是不支持ES模塊的,也就是不支持import
,只能使用require
的這類插件,目前是不支持在vite
中使用的,由於它只支持ES模塊,或者本身手動去修改源碼導出方式。
生產環境
最後須要注意的是,咱們在開發環境使用的原生ES模塊
並不會由於打包後轉成以往的兼容模式,意思就是打包後仍是ES模塊
,而且只能用服務端形式來打開index.html
,一些低版本的瀏覽器是不支持或者存在兼容性的,仔細看下構建後的index.html
引用的js
標籤就明白了;若是追求兼容、穩定,建議仍是用vue 2.x
+vue-cli
...