Skip to content

Latest commit

 

History

History
177 lines (163 loc) · 5.94 KB

route.md

File metadata and controls

177 lines (163 loc) · 5.94 KB

vue3+vite 动态路由

根目录 router.ts

import {createRouter, createWebHashHistory, RouterView} from "vue-router"
let files = import.meta.glob("./views/**/*.{vue,jsx,tsx}", {})
const pages = import.meta.glob("./views/**/page.json", {eager:true, import:'default'})
const filesKeys = Object.keys(files)
const metaMaps = ROUTES_META
let routes:any = []
// 平铺路由
for(const [key, component] of Object.entries(files)){
    const path = key.replace(/^\.\/views|\.(vue|jsx|tsx)$/img,'')
    const name = path.split('/').filter(e=>e).join("-")
    const fileName = /(?:\/?([^\/]+\.(vue|jsx|tsx)$))/.exec(key)[1]
    const pageJson = key.replace(fileName,'page.json')
    const directory = key.replace('/'+fileName, '')
    const layoutRegs = directory.split('/').map((e, i, arr)=>arr.slice(0, i+1).join('/')).map(e=> new RegExp(`^${e}\/layout\\.(vue|jsx|tsx)`, 'img'))
    const layout = filesKeys.find(e=>layoutRegs.some(ee=>ee.test(e)))
    // 过滤Alert
    if(!/\/alert\//.test(key)){
        console.log()
        routes.push({
            component,
            name,
            path:path.toLowerCase(),
            key,
            fileName,
            layout,
            directory,
            meta:Object.assign(metaMaps[key] || {}, (pages[pageJson] || {})[fileName] || {})
        })
    }
}
const pathToTree = (input, reg) => {
    const currInput = input.map(e=>e.replace(/^\.\/views\//,''))
    const output = [];
    for (let i = 0; i < currInput.length; i++) {
        const key = currInput[i]
        const chain = key.replace(reg, '').split("/");
        const chainJoin = chain.join("-").toLowerCase();
        const suffix = key.match(reg)[1]
        let currentNode:any = output;
        for (let j = 0; j < chain.length; j++) {
            const wantedNode = chain.slice(0, j+1).join('-').toLowerCase();
            const lastNode = currentNode;
            let k = 0
            for (; k < currentNode.length; k++) {
                if (currentNode[k].name == wantedNode) {
                    currentNode = currentNode[k].children;
                    break;
                }
            }
            if (lastNode == currentNode) {
                const directory = chainJoin !== wantedNode
                const path = `./views/${chain.slice(0, j+1).join('/')}${directory ? '' : `.${suffix}`}`
                let layoutComponent:any = RouterView
                if(directory){
                    const layoutPath = input.find(e=>new RegExp(`^${path}\/layout\\.(vue|jsx|tsx)`, 'img').test(e))
                    if(layoutPath){
                        layoutComponent = files[layoutPath]
                    }
                }
                const pageJsonPath = path.replace(new RegExp(`${chain[j]}\\.${suffix}$`,'i'), 'page.json')
                const newNode = {
                    key,
                    name: wantedNode,
                    children: [],
                    directory,
                    suffix:directory ? null : suffix,
                    filePath:path,
                    path:chain[j].toLowerCase(),
                    component:directory ? layoutComponent : files[path],
                    meta:Object.assign(metaMaps[path] || {},(pages[pageJsonPath] || {})[`${chain[j]}.${suffix}`] || {})
                };
                currentNode[k] = newNode
                currentNode = newNode.children;
            } else {
                delete currentNode.children
            }
        }
    }
    return output.map(e=>({
        ...e,
        path:`/${e.path}`
    }));
}

// 嵌套路由
routes = routes.filter(e=>!e.layout).concat(pathToTree(routes.filter(e=>e.layout).map(e=>e.key),/\.(vue|tsx|jsx)$/))
export default createRouter({
    history:createWebHashHistory(),
    routes
})

根目录vite.config.ts

import {readFileSync} from 'fs'
import * as sfc from 'vue/compiler-sfc'
import glob from 'fast-glob'
const ROUTES_META = function ():Plugin{
    let routeModuleId = null
    return {
        name:"ROUTES_META",
        enforce:'pre',
        load(id){
            if(/route\.ts$/.test(id)){
                routeModuleId = id
                const map = glob.sync("**/*.vue", {absolute:true}).reduce((res, id)=>{
                    const code = readFileSync(id, 'utf-8')
                    const descriptor = sfc.parse(`${code.match(/<script([^>])*>/)?.[0] || '<script>'}console.log(1)<script/>`).descriptor
                    return {
                        ...res,
                        [id.replace(process.cwd(),'.')]:descriptor?.scriptSetup?.attrs
                    }
                },{})
                return readFileSync(id, 'utf-8').replace('ROUTES_META', JSON.stringify(map))
            }
        },
        handleHotUpdate(cxt){
            cxt.server.reloadModule(cxt.server.moduleGraph.getModuleById(routeModuleId))
        }
    }
}

declare global{
    const ROUTES_META:Record<string, any>
}


export default defineConfig({
    plugins:[
        ROUTES_META()
    ]
})

核心代码

import {RouteRecordRaw, RouterView} from "vue-router"
const files = import.meta.glob('./views/**/*.vue')
const keys = Object.keys(files)
const routes = []
for(const u of keys){
    const urls = u.replace(/^\/|(\.+\/)+views\/|\.\D+$/g,'').split('/')
    let currDir = routes
    for (const [k,name] of Object.entries(urls)){
        const index = Number(k)
        const currNames = urls.slice(0, index+1).join("-")
        let child = currDir.find(e=>e.name === currNames)
        const isDir = index+1 !== urls.length
        const layoutPath = `./views/${urls.slice(0, index+1).join('/')}/layout.vue`
        if(!child){
            child = {
                isDir,
                path:name,
                name:currNames,
                children:[],
                currNames,
                layoutPath,
                component:isDir ? files[layoutPath] || RouterView : files[u]
            } as RouteRecordRaw
            currDir.push(child)
        }
        currDir = child.children
    }
}
console.log(routes)