最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue 3.x + Typescript + Vite 踩坑指南

    正文概述 掘金(黄景圣)   2021-05-09   1943

    最近在迁移开源项目 vue-admin 到最新技术上的时候,遇到了一些技术隐形的问题,毕竟是最新的技术点,难免有些疑难杂症,所以分享给有需要的朋友

    预览效果

    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.globEagerimport.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.globEagerimport.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,已经向作者提问了...

    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加入以往没有的新apiremoveRoute现在可以轻松的做退出登陆删除之前动态拼接的路由了,不过这个api是以路由定义中name作为删除唯一键值的,所以我们在定义路由的时候最好写上,且唯一,删除路由操作可以看代码片段

    removeRoutes 方法

    因为改用了Composition API,所以路由的使用方式变了,不过需要注意的是:useRouteuseRouter这两个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
            }
        }
    })
    

    scss 变量无法在 js 或 ts 中使用

    之前在webpack中,.scss文件中导出的变量,是可以在对应的文件中作为js模块导入使用,现在换成vite之后就不行了,即使装了sass-loader,来看下之前能正确使用的代码片段:

    $--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 elementVariables from "../styles/element-variables.scss";
    
    console.log(elementVariables) // 输出 { theme: "#1890FF" }
    

    详细翻了 vite文档 也是没有找到相关的配置,所以暂时用不了该功能。

    打包后依然是使用最新的ES模块

    最后需要注意的是,我们在开发环境使用的原生ES模块并不会因为打包后转成以往的兼容模式,意思就是只能用服务器形式来打开,并且不支持IE(好像是废话),仔细看下构建后的index.html引用的js标签就明白了,如果追求兼容、稳定,建议还是用vue 2.x+vue-cli...


    起源地下载网 » Vue 3.x + Typescript + Vite 踩坑指南

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元