require.context

你还可以使用 require.context() 方法来创建自己的(模块)上下文。

你可以给这个方法传 3 个参数:要搜索的文件夹目录,是否还应该搜索它的子目录,以及一个匹配文件的正则表达式。

webpack 会在构建的时候解析代码中的 require.context() 。

思路

  • 自定义一个菜单数组用来循环动态生成路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    /**
    * 文件创建的时候,如果当前的菜单下面还有子菜单,需要在该文件夹下面新建一个 index.vue 的文件,用来跳转路由
    * 菜单命名的格式如下
    */
    export const menus = [
    {
    key: "menu1",
    label: "一级菜单1",
    icon: "",
    },
    {
    key: "menu2",
    label: "一级菜单2",
    icon: "",
    },
    {
    key: "menu3",
    label: "一级菜单3",
    icon: "",
    children: [
    {
    key: "menu3_1",
    label: "一级菜单3_1",
    icon: "",
    children: [
    {
    key: "menu3_1_1",
    label: "一级菜单3_1_1",
    icon: "",
    },
    ],
    },
    {
    key: "menu3_2",
    label: "一级菜单3_2",
    icon: "",
    },
    ],
    },
    {
    key: "menu4",
    label: "未开发页面演示",
    icon: "",
    },
    ];

  • 核心代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    import Vue from "vue";
    import Router from "vue-router";

    Vue.use(Router);

    export const routes: any[] = [];

    const viewContext = require.context("./views", true, /\.vue$/);

    const cache: { [key: string]: object } = {};

    viewContext.keys().forEach((key) => cache[key] = viewContext(key).default);

    interface RouterObj {
    path: string;
    name: string;
    component?: object;

    [propName: string]: any;
    }

    interface Menu {
    key: string;
    label: string;
    icon?: string;

    [propName: string]: any;
    }

    const menu2router = (menu: Menu, parent: RouterObj, index: number, thisRoutes: any[]) => {
    let isFind = false;
    const route: RouterObj = {
    path: parent.path + "/" + menu.key,
    name: menu.label,
    };

    if (menu.children && menu.children.length > 0) {
    const routeFilePath = route.path + "/index.vue";
    let fileComponent = null;
    for (const key of Object.keys(cache)) {
    if (key.indexOf(routeFilePath) !== -1) {
    fileComponent = cache[key];
    break;
    }
    }
    thisRoutes[index] = {
    path: "/" + menu.key,
    name: menu.label,
    component: fileComponent,
    children: [],
    };
    for (let i = 0, j = menu.children.length; i < j; i++) {
    menu2router(menu.children[i], route, i, thisRoutes[index].children);
    }
    }

    const routePath = route.path + "/" + (menu.key || "index") + ".vue";
    for (const key of Object.keys(cache)) {
    if (key.indexOf(routePath) !== -1) {
    isFind = true;
    const obj: RouterObj = {
    path: route.path,
    name: route.name,
    component: cache[key],
    };
    thisRoutes.push(obj);
    break;
    }
    }
    if (!isFind) {
    thisRoutes.push({
    path: route.path,
    name: route.name,
    component: notFind,
    });
    }
    };

    export const getRouter = (menusArr: any[]) => {
    menusArr.forEach((menu: Menu, index: number) => {
    menu2router(menu, {path: "", name: ""}, index, routes);
    });

    routes.push({
    path: "/",
    redirect: routes[0].path,
    });

    return new Router({
    mode: "history",
    base: process.env.BASE_URL,
    routes,
    });
  • 遍历生成菜单
    以 iview 组件库中的菜单为例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    <template lang="pug">
    Menu(:active-name="activeName" theme="dark" width="auto")
    template(v-for="(menu, index) in menus")
    MenuItem(v-if="!menu.children" :name="menu.key" :to="'/' + menu.key") {{menu.label}}
    Submenu(v-else :name="menu.key")
    template(slot="title") {{menu.label}}
    template(v-for="(secondMenu, secondMenuIndex) in menu.children")
    MenuItem(v-if="!secondMenu.children" :name="secondMenu.key" :to="'/' + menu.key + '/' + secondMenu.key") {{secondMenu.label}}
    Submenu(v-else :name="secondMenu.key")
    template(slot="title") {{secondMenu.label}}
    MenuItem(v-for="(thirdMenu, thirdIndex) in secondMenu.children" :key="thirdIndex" :name="thirdMenu.key" :to="'/' + menu.key + '/' + secondMenu.key + '/' + thirdMenu.key") {{thirdMenu.label}}
    </template>

    <script lang="ts">
    import {Vue} from "vue-property-decorator";
    import {sessionMenus} from "@/check_session";

    export default class Menus extends Vue {
    activeName: string = "";
    menus: any[] = sessionMenus.menus;

    mounted() {
    const href = window.location.href;
    const hrefArr = href.split("/");
    this.activeName = hrefArr[hrefArr.length - 1];
    }
    }
    </script>

    <style lang="stylus" scoped>

    </style>

项目地址

基于 vue 脚手架实现自动遍历目录文件生成路由