菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
0
0

Vue 3.x + Typescript + Vite 踩坑指南

原创
05/13 14:22
阅读数 235

Vite 与 webpack 使用注意点

node.js 文件系统

浏览器环境文件操做api使用,webpack对应Vitevue

// webpack
require.context

// 对应 vite 二选一,详细使用说明看文档 https://vitejs.dev/guide/features.html#glob-import
import.meta.globEager
import.meta.glob

举个例子,当前项目须要读取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的文件系统模块,这里跟webpack环境基本一致。一样是当前项目的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。

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做为删除惟一键值的,因此咱们在定义路由的时候最好写上,且惟一,删除路由操做能够看代码片断:

removeRoutes 方法

由于改用了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是须要的。

scss变量在js或ts中使用

以前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模块

第三方插件库

像一些比较老的插件,是不支持ES模块的,也就是不支持import,只能使用require的这类插件,目前是不支持在vite中使用的,由于它只支持ES模块,或者本身手动去修改源码导出方式。

生产环境

最后须要注意的是,咱们在开发环境使用的原生ES模块并不会由于打包后转成以往的兼容模式,意思就是打包后仍是ES模块,而且只能用服务端形式来打开index.html,一些低版本的浏览器是不支持或者存在兼容性的,仔细看下构建后的index.html引用的js标签就明白了;若是追求兼容、稳定,建议仍是用vue 2.x+vue-cli…

发表评论

0/200
0 点赞
0 评论
收藏