vue-router路由配置拆分和自动导入的实现

2,550 阅读7分钟

这是我在掘金分享的第一篇文章,那当然是得上点干货了!(逃~

前置

我想查看本篇本章的朋友肯定是会一定的前端知识的,且还用过 Vue 进行开发项目的(逃~

那么大家肯定会用到 vue-router 进行路由管理吧,我们最普通的项目肯定会有如下的文件夹和文件:

image.png

router

该文件夹就是我们经常在 Vue 项目中配置 vue-router 的文件夹了,再常用不过了~

index.js

该文件即我们的 vue-router 的路由配置文件了


那么你会不会因为在项目越写越大之后突然发现你的 index.js 文件写成了这样呢?

image.png

如上图所示,整个项目用到的路由都写在了一个 index.js 文件中,项目越大那么写的就越多。博主就是因为受不了在这个越写越大的文件中去翻找/修改某个路由而去慢慢查找,这是一件很痛苦的事情。

解决方案一

⭐️ 那么解决方案肯定是有的

我们可以将共同的路由进行配置拆分,比如博主的后台项目中有:

  1. 用户管理
  2. 权限管理
  3. 文章管理
  4. 外链管理
  5. ...

那么我们就可以将如上不同管理模块的路由进行拆分成不同的配置文件:

image.png

以上我们用到的所有模块的路由都进行拆分到了 modules/ 文件夹下,每个文件中只需要导出当前模块用到的路由配置即可:

image.png

那么这时候就可以让我们更便捷的找到自己需要的模块所在的地方了,不会像一开始将所有文件堆在 index.js 中那么的烦乱了。

我们最终只需要导入所有模块的内容汇总到 index.js 中即可:

image.png

是不是很精妙呢?!(我知道我在废话!逃~

虽然上面的拆分是可以让路由配置和管理更清晰和便捷了,但是我们会发现我们在 index.js 中会反复的进行 import ... from ... 导入所有配置的路由模块,模块一多那么这些的 import 语句也就越多!这是咋们聪明(懒惰)的程序员所不能容忍的!(内心:我就想写短的代码!!!)

当然接下来将分享最终的解决方案,大家不要走开!

解决方案二

上面说到了我们会在 index.js 中反复的写 import 语句去导入所有路由模块,这很烦!所以我们就要解决它!

那么如何解决呢?肯定是实现一个自动导入 modules 下的所有路由模块了!能用代码解决的事情怎么能劳烦咋们程序员自己手动写呢?(逃~

接下来将介绍如何使用 WebpackVite 在项目中配置自己的自动路由导入小插件~

Webpack

思路其实很简单,我们是不是只需要去遍历 modules/ 下的所有文件汇总起来再导出即可?那么我们可以写出如下代码:

// router/autoImport.js

/**
 * 使用 require.context 去获取一个特定的上下文,主要用来实现自动化导入模块
 * @param {String} param1 所以读取的文件的目录
 * @param {Boolean} param2 是否遍历子目录
 * @param {RegExp} param3 匹配文件的正则表达式
 * @return {Function} 返回一个函数,我们可以调用其 keys() 方法获取到所有匹配城中的文件名称组成的数组
 */
const moduleFiles = require.context('./modules', false, /\.js$/);

// 通过 reduce 去搜集所有的模块的导出内容
const configRoutes = moduleFiles.keys().reduce((routes, filepath) => {
  // 因为moduleFiles是一个函数,那么可以接受一个参数(string:文件的相对路径),调用其从而获取到对应路径下的模块的导出对象
  // 导出的对象中有一个属性:default,可以获取到默认导出的所有内容
  const value = moduleFiles(filepath).default;

  // 我们判断导出的是不是数组,是则进行拓展解构
  if (Array.isArray(value)) {
    routes.push(...value); 
  } else {
    // 否则直接加到routes中
    routes.push(value);
  }
  return routes;
}, []);

// 直接导出所有模块配置的路由
export default configRoutes;

明确一点:require.context 是 Webpack 提供的 API,所以直接用即可,如果有不会的可自行百度查阅

最后我们在 index.js 中导入该小插件所导出的所有配置路由放到默认的全局路由配置对象上即可:

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

// import articleControlRoutes from './modules/articleControl';
// import outLinkControlRoutes from './modules/outLinkControl';
// import permissionControlRoutes from './modules/permissionControl';
// import userControlRoutes from './modules/userControl';

import configRoutes from './autoImport';

const routes = [
  ...configRoutes
]

export const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router;

Vite

上面说了在 Webpack 中如何实现自己的路由模块自动导入的小功能,使用到了 require.context 来实现,且明确了是 Webpack 提供的 API,所以是不能在 Vite 中使用的。

不过万事没有绝对,既然 Webpack 有这种 API,那么我们尤大大写的 Vite 当然不甘落后啦~

Vite 为我们提供了两个导入的独特的 API

  1. import.meta.glob - 从文件系统导入多个模块

    const modules = import.meta.glob('./dir/*.js');
    

    以上代码会被 Vite 编译成:

    const modules = {
      './dir/foo.js': () => import('./dir/foo.js'),
      './dir/bar.js': () => import('./dir/bar.js')
    }
    

    我们可以看到其实是帮我们将 dir/ 目录下的所有以 .js 结尾的文件写成了匿名箭头函数动态让我们去调用导入的(即官方文档中说的:匹配到的文件默认是懒加载的,通过动态导入实现,并会在构建时分离为独立的 chunk

  2. import.meta.globEager - 直接引入所有的模块(例如依赖于这些模块中的副作用首先被应用)

    const modules = import.meta.globEager('./dir/*.js');
    

    以上代码会被 Vite 编译成:

    import * as __glob__0_0 from './dir/foo.js';
    import * as __glob__0_1 from './dir/bar.js';
    const modules = {
      './dir/foo.js': __glob__0_0,
      './dir/bar.js': __glob__0_1
    }
    

    globEager 函数其实就是帮我们省去了自己写 import 语句去导入需要的模块的操作,而且是 直接导入,并非 glob 给我们一个需要手动调用的动态导入的函数

以上两个 API 如果你是要动态的导入你想要的模块的话那还是推荐使用第一种方式,今天博主讲的动态导入路由文件模块均是全部导入的,所以采用的是第二个函数。但是也不能说第一种不能实现,只不过需要我们解决 import 是异步执行导入的这个问题,会相对麻烦一些,这里就不过多的叙述了。

我们采用 import.meta.globEager 去导入,其实跟 Webpack 中使用 require.context 差不多:

/**
 * @param {String} param1 需要遍历的目录路径
 * @return {Object} 返回一个key为文件相对路径,value为对应文件模块导出的内容的对象集合
 */
 const moduleFiles = import.meta.globEager('./modules/*.js');

 // 通过 reduce 去搜集所有的模块的导出内容
 const configRoutes = Object.keys(moduleFiles).reduce((routes, filepath) => {
   // 因为moduleFiles是一个函数,那么可以接受一个参数(string:文件的相对路径),调用其从而获取到对应路径下的模块的导出对象
   // 导出的对象中有一个属性:default,可以获取到默认导出的所有内容
   const value = moduleFiles[filepath].default;
 
   // 我们判断导出的是不是数组,是则进行拓展解构
   if (Array.isArray(value)) {
     routes.push(...value); 
   } else {
     // 否则直接加到routes中
     routes.push(value);
   }
   return routes;
 }, []);
 
 // 直接导出所有模块配置的路由
 export default configRoutes;

主要的逻辑基本没变,只是 import.meta.globEager 返回的是一个对象,我们用的是 Object.keys() 去遍历而已,最终还是将 configRoutes 导出。

最后

至此,我们终于可以实现了:

  1. 拆分 index.js 的路由为多个模块进行管理
  2. 自动导入拆分出的路由模块的内容并整合导出给我们直接导入使用

虽然啰嗦了很多,不过博主也是第一次发文,求点赞,(灬ꈍ ꈍ灬)。

如有疑问和见解的请评论哦~