Recently, WHEN I was migrating the open source project VUE-admin to the latest technology, I encountered some problems of invisible technology. After all, it is the latest technology point, and there are inevitably some difficult and complicated problems, so I would like to share them with friends who need them

preview

Note the use of Vite and Webpack

Node.js file system

Browser environment file operation API used, Webpack corresponding to Vite

// webpack
require.context

/ / the corresponding alternative vite, detailed instructions document at https://vitejs.dev/guide/features.html#glob-import
import.meta.globEager
import.meta.glob
Copy the code

For example, if the current project needs to read all SVG names in the SRC/ICONS/SVG/directory, it would be like this:

<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))/;

/** Get all 'SVG' names */
function getSvgNames() {
    const svgInfo = import.meta.globEager(".. /.. /icons/svg/*.svg");
    const svgs = Object.keys(svgInfo);
    const names = svgs.map(value= > {
        constres = value.match(svgFileReg)! [0];
        return res;
    });
    return names;
}

export default defineComponent({
    name: "Icons".setup() {

        return {
            svgIcons: getSvgNames()
        }
    }
})
</script>
Copy the code

GlobEager and import.meta. Glob are not used in the non-browser environment, i.e. in the viet.config. ts file, and only node.js file system module is used, which is basically the same as webpack environment. For the same SVG component of the current project, we need to write a separate SVG plug-in (vite plug-in), so it looks like this:

import { readFileSync, readdirSync } from "fs";

// svg-sprite-loader does not work in vite
// This file can only be imported as' vite.config.ts'
// An error will be reported when imported elsewhere, because the browser environment does not support the 'fs' module

/** 'id' prefix */
let idPerfix = "";

const svgTitle = /
      
       +].*?) >/
      ([^>;

const clearHeightWidth = /(width|height)="([^>+].*?) "/g;

const hasViewBox = /(viewBox="[^>+].*?" )/g;

const clearReturn = /(\r)|(\n)/g;

/** * Find 'SVG' file *@param Dir File directory */
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' packer *@param Path Resource path *@param Perfix suffix (label 'ID' prefix) */
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>`)}}}Copy the code

Finally use in the vite.config.ts file:

import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import vueJsx from "@vitejs/plugin-vue-jsx";
import { svgBuilder } from "./src/icons/loader"; // Here is the 'SVG' loading plug-in written above

export default defineConfig({
    plugins: [vue(), vueJsx(), svgBuilder("./src/icons/svg/")]})Copy the code

NPM run build error

NPM run dev does not even have a warning. NPM run dev does not even have a warning. In tsconfig.json, we need to add a/before all paths of include. Like this:

{
    ...more,
    // All paths must be preceded by /
    "include": ["/src/**/*.ts"."/src/**/*.d.ts"."/src/**/*.tsx"."/src/**/*.vue"]}Copy the code

[bug] [bug] [bug] [bug] [bug] [bug] [bug] [bug] [bug] [bug] [bug] [bug]

vue-router

Vue -router 4.x removes routing path matches. Look at a code snippet

import { createRouter, createWebHashHistory } from "vue-router";

const base = [
    {
        path: "https://github.com/Hansen-hjs/vue-admin".// This is what I wrote when I filled in the outer chain
        name: "baidu".component: () = > import(".. /views/404.vue"), // There must be a component (although it will not be displayed), otherwise it will freeze
        meta: {
            icon: "star".title: "Jump external link"}}]const router = createRouter({
    history: createWebHashHistory(),
    routes: base
})
Copy the code

The console will warn you that the browser is stuck because it cannot match a path starting with a non-/, so you need to add a/to the front of the chain, and then do the corresponding processing when the route is obtained, like this:

const base = [
    {
        path: "/https://github.com/Hansen-hjs/vue-admin". more } ]Copy the code

Vue -router 4.x added a new API that was not previously available: RemoveRoute now makes it easy to log out and delete routes that were previously dynamically concatenated. However, the API uses name as the unique key for deleting routes, so it is best to specify “name” as the unique key for deleting routes.

RemoveRoutes method

The use of routes has changed because of the change to the Composition API, but note that the hooks functions useRoute and useRouter must be on the top level, and cannot be retrieved if they are in the function after the code has run:

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(); // If you write here, you can't get the object
            // const router = useRouter(); // If you write here, you can't get the object

            console.log(route, router);
            
        }
        return {
            getRouterInfo
        }
    }
})
Copy the code

It’s not sure if hooks are used in other libraries that require declarations at the top level, but vue-router is.

SCSS variables are used in JS or TS

In the webPack environment, the export method is still the same, but the file name is slightly changed. For example, to import variables. SCSS as js/ts, you just need to add a. Module after the name, like this: varies.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;
}
Copy the code

Other non. SCSS file import is used

import variables from ".. /styles/variables.module.scss";

console.log(variables) {theme: "#1890FF"}
Copy the code

Matters needing attention

Import./xxx.module. SCSS files in main.ts as a style introduction. By default, they are not loaded to

NPM run build error

[root@localhost] [root@localhost] [root@localhost] [root@localhost]

[vite:css-post] value.replace is not a function
Copy the code

So I abandoned the named import of xxx.module. SCSS in the project, and adopted a direct violent solution: after importing XXX.scss normally, I wrote a utility function that extracted the exported variables, so that the same function could be achieved.

Handle export utility functions:

/** * Format exported variables * in '. SCSS 'files@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}"} `);
}
Copy the code

Example:

import style from ".. /styles/variables.scss";
const variables = formatStyleModule(style);
console.log(variables);
Copy the code

ES module

Third-party plug-in library

For example, some older plug-ins do not support ES module, that is, do not support import, can only use plug-ins such as require, currently is not supported in Vite, because it only supports ES module, or manually modify the source export method.

The production environment

Finally, it is important to note that the native ES modules we use in the development environment will not be converted to the old compatibility mode after packaging, meaning that the package will still be ES module, and can only be opened in server form, some of the older browsers do not support or have compatibility. Take a closer look at the JS tags referenced in the built index.html; 2. X +vue- CLI…