webpack自动路由插件

由于之前的搭建的单页面架构自动路由这块,配置meta信息,需要单独写配置文件,而且只配置支持二级路由,三级路由需要手写配置生成。动态路由也是需要配置id。对使用者不太友好。现在就想使用webpack 插件的形式,自动生成路由文件,meta信息在每个vue页面里面去配置,支持多级路由,来优化路由这块的代码。(网上也有一些自动路由插件,但不太合适接入到之前写的单页面架构中,于是就自己动手写了一个)

整体思路

1、写一个webpack插件,在代码运行时生成routes.js文件
2、代码运行时传入需要生成文件的目录,以及输出routes.js的目录路径
3、根据目录路径(system/user/index.vue)生成 嵌套树

system:{
    value:[],
    children:[]
}

4、根据嵌套树在递归遍历配置路由信息生成json数据
5、nodejs读取目录下vue文件里面的meta信息整合到路由Json中

[ { name: 'system',
    path: 'system',
    component: '@/views/autoRouter/system/_layout.vue',
    index: 1,
    meta:
     '{\n    title: \'系统管理\',\n    icon: \'form\',\n    permissionArray: [1, 2, 3],\n    sortIndex: 1,\n    newTime: \'2022-05-20\'\n  }',
    children: [ [Object], [Object], [Object] ] } ]

6、在将路由json数据递归匹配可生成routes.js 的字符串数据
7、使用nodejs 在输出的目录下写入routes.js 文件

代码实现

const fg = require('fast-glob') // 读取文件目录
const prettier = require('prettier') // 处理文件格式

1、定义一个自动路由插件类,在这个类里面定义一个方法实现目录路径输入,routes.js文件输出数据。在写入文件中

// 定义一个自动路由插件类
    const generate = () => {
      const code = generateRoutes(this.options)
      let to
      // 处理路由文件生成目录 默认与插件文件并级 如果配置routePath,那生成文件就是此路径下
      if (this.options.routePath) {
        to = path.join(process.cwd(), this.options.routePath)
      } else {
        to = path.join(__dirname, './routes.js')
      }
      // 以同步的方法检测目录是否存在。 如果目录存在 返回true,如果目录不存在 返回false 语法
      // 读取文件信息 如果没有改变直接返回
      if (fs.existsSync(to) &&
        fs.readFileSync(to, 'utf8').trim() === code.trim()) {
        return
      }
      fs.writeFileSync(to, code)
    }

2、将目录生成嵌套树

/**
 * 根据目录去生成路由
 * @param pages 解析路由文件目录
 * @param importPrefix import导入前缀目录
 * @param layout 想要生成嵌套路由,目录下需要layout文件,用于配置父节点的meta信息 另个作用是当路由大于二级路由时,_layout需要有<router-view/>,不然页面不会显示
 * @param common 过滤文件路径路径
 * @returns {*}
 */
function generateRoutes({ pages = 'src/views', importPrefix = '@/views/', layout = '_layout.vue', common = 'common' }) {
  // 指定文件不需要生成路由配置
  const patterns = ['**/*.vue', `!**/${layout}`, `!**/${common}/*.vue`]
  // 获取所有layout的文件路径
  const layoutPaths = fg.sync(`**/${layout}`, {
    cwd: pages,
    onlyFiles: true
  })
  // 获取所有需要路由配置的文件路径
  const paths = fg.sync(patterns, {
    cwd: pages,
    onlyFiles: true
  })
  const pathsArr = paths.map((p) => p.split('/'))
  const layoutPathsArr = layoutPaths.map((p) => p.split('/'))
  // 生成嵌套目录
  const map = {}
  layoutPathsArr.forEach((path) => {
    const dir = path.slice(0, path.length - 1)
    dir.unshift('rootPathLayoutName')
    setToMap(map, pathToMapPath(path.slice(0, path.length - 1)), path)
  })
  pathsArr.forEach((path) => {
    let dir = path
    if (path.indexOf('index.vue') > -1) {
      dir = path.slice(0, path.length - 1)
    }
    setToMap(map, pathToMapPath(dir), path)
  })

  //  将目录匹配生成路由json
  const routerStr = pathMapToMeta({
    children: map.children,
    routers: [],
    pages: pages,
    importPrefix: importPrefix
  })
  // 将json转换文件字符串
  return createRoutes(routerStr)
}
/**
 * 将目录生成嵌套树 将每个目录生成key:{value:[],childer:[]}
 * @param map {}
 * @param paths 不带.vue的路径
 * @param value 所有解析处理的层级目录数组
 */
function setToMap(map, paths, value) {
  const target = paths.reduce((item, key) => {
    if (!item.children) {
      item.children = new Map()
    }
    let child = item.children.get(key)
    if (!child) {
      child = {}
      item.children.set(key, child)
    }
    return child
  }, map)
  target.value = value
}

3、递归处理将嵌套树生成路由json数据,并读取vue页面中的meta信息
注意:想要生成嵌套路由,目录下需要layout文件,用于配置父节点的meta信息 另个作用是当路由大于二级路由时,_layout需要有,不然页面不会显示

/**
 *
 * @param children 嵌套目录
 * @param routers 路由
 * @param pages 需要自动生成文件的目录
 * @param importPrefix import 引入页面文件的前缀目录
 * @returns {*[]}
 */
function pathMapToMeta({ children, routers = [], pages, importPrefix }) {
  Array.from(children.keys()).forEach((row) => {
    const item = children.get(row)
    // 配置参数
    const router = {
      name: row,
      path: (row.indexOf('_id') > -1 ? (row.indexOf('_id') === 0 ? row.replace('_', ':') : row.replace('_', '/:')) : row),
      component: '',
      index: 1,
      meta: `{ title: "${row}", icon: "form"}`,
      children: []
    }

    // 如果value有值,说明可以根据文件路径取meta信息
    if (item.value) {
      router.component = path.join(importPrefix, item.value.join('/'))
      const file = fs.readFileSync(path.join(pages, item.value.join('/')), 'utf8')
      const metaArr = file.match(/\meta: {[^\{]+\}/g) || file.match(/\meta:{[^\{]+\}/g)
      if (metaArr) {
        const metaStr = metaArr[0]
        // 匹配meta信息
        let meta = ''
        if (metaStr.indexOf('meta') > -1) {
          meta = metaStr.substring(metaStr.indexOf('{'), metaStr.indexOf('}') + 1)
        }
        router.meta = meta
        // 匹配排序
        if (metaStr.indexOf('sortIndex') > -1) {
          const sortIndexAll = metaStr.substring(metaStr.indexOf('sortIndex'), metaStr.indexOf('}'))
          const sortIndex = sortIndexAll.substring(sortIndexAll.indexOf(':') + 1, (sortIndexAll.indexOf(',') > -1 ? sortIndexAll.indexOf(',') : sortIndexAll.length - 1))
          router.index = Number(sortIndex)
        }
      }
    }
    // 如果有children 需要遍历循环匹配
    if (item.children) {
      router.children = pathMapToMeta({
        children: item.children,
        routers: router.children,
        pages: pages,
        importPrefix: importPrefix
      })
    }
    routers.push(router)
  })
  return routers
}

4、将路由json数据匹配生成routes.js 文件字符串数据


/**
 * 将路由json格式化成字符串
 * @param routers 路由json
 * @returns {*}
 */
function createRoutes(routers) {
  const code = routers.map(createRoute)
  return prettier.format(`import Layout from '@/layout'\nexport default [\n
    ${code}
  \n]`, {
    parser: 'babel',
    semi: false,
    singleQuote: true,
    trailingComma: 'none' // 处理最后一行不加,的问题
  })
}

/**
 * 一级路由转换 第一级的时候需要把component换成Layout 如果没有子节点,则需要将父节点复制一份成为子节点,component指向文件路径
 * @param map
 * @param children
 * @returns {string}
 */
function createRoute(map, children = {}) {
  if (map.children && map.children.length !== 0) {
    children = map.children.map(createRouteZJ)
    return `\n{
      path:'/${map.path}',
      name:'${map.name}',
      meta:${map.meta},
      index:${map.index},
      alwaysShow: false,
      component: Layout,
      children:[${children}]
    }`
  } else {
    // 如果只有一级目录,需要单独处理name 不然会报警告name相同
    children = `\n{
      path:'${map.path}',
      name:'${map.name}',
      meta:${map.meta},
      index:${map.index},
      alwaysShow: false,
      component:() => import('${map.component}')
    }`
    return `\n{
      path:'/${map.path}',
      name:'${map.name}p',
      meta:${map.meta},
      index:${map.index},
      alwaysShow: false,
      component: Layout,
      children:[${children}]
    }`
  }
}

/**
 * 二级及以上路由转换
 * @param map json里面的children  子节点里面的path不需要'/'
 * @param children
 * @returns {string}
 */
function createRouteZJ(map, children = {}) {
  if (map.children) {
    children = map.children.map(createRouteZJ)
  }
  return `\n{
  path:'${map.path}',
  name:'${map.name}',
  meta:${map.meta},
  index:${map.index},
  component:() => import('${map.component}'),
  children:[${children}]
  }`
}

使用方式

1、导入

npm install  ff-auto-router

2、修改vue.config.js

const autoRouter = require('ff-auto-router/lib/router-webpack-plugin')
configureWebpack(config) {
    config.plugins = [
      ...config.plugins,
      // eslint-disable-next-line new-cap
      new autoRouter({
        pages: 'src/views/autoRouter',
        importPrefix: '@/views/autoRouter',
        routePath: 'src/router/routes.js'
      })
    ]
}

pages 需要自动生成文件的目录
importPrefix import 引入页面文件的前缀目录
routePath 路由生成的文件目录,如果设置了则会在当前项目指定目录生成路由文件,否则可以从ff-auto-route导入 ff-auto-router/lib/routes

其中metavue-router配置的meta属性一致
写在每个组件的export default {}中

meta: {
    title: '系统管理',
    icon: 'form',
    permissionArray: [1, 2, 3],
    sortIndex: 1,
    newTime: '2022-05-20'
  }

3、修改src/router/index.js 文件,
这里的配置根据项目实际情况写。这样是基本写法可使用

import routes from './routes'
const createRouter = () => new Router({
  mode: 'history', // require service support
  base: process.env.BASE_URL,
  scrollBehavior: () => ({ y: 0 }),
  routes
})

展示效果

目录文件

{
    path: '/system',
    name: 'system',
    meta: {
      title: '系统管理',
      icon: 'form',
      permissionArray: [1, 2, 3],
      sortIndex: 1,
      newTime: '2022-05-20'
    },
    index: 1,
    alwaysShow: false,
    component: Layout,
    children: [
      {
        path: 'menu',
        name: 'menu',
        meta: {
          title: '菜单管理',
          icon: 'form',
          permissionArray: [1, 2, 3],
          sortIndex: 2,
          newTime: '2022-05-20'
        },
        index: 2,
        component: () => import('@/views/autoRouter/system/menu.vue'),
        children: []
      },
      {
        path: 'user',
        name: 'user',
        meta: {
          title: '用户管理',
          icon: 'form',
          permissionArray: [1, 2, 3],
          sortIndex: 1,
          newTime: '2022-05-20'
        },
        index: 1,
        component: () => import('@/views/autoRouter/system/user.vue'),
        children: []
      },
      {
        path: 'role/:id',
        name: 'role_id',
        meta: {
          title: '角色管理',
          icon: 'form',
          permissionArray: [1, 2, 3],
          sortIndex: 3,
          newTime: '2022-05-20'
        },
        index: 3,
        component: () => import('@/views/autoRouter/system/role_id/index.vue'),
        children: []
      }
    ]
  }

运行结果

代码可自行npm 下载查看
https://www.npmjs.com/package/ff-auto-router

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. 什么是WebpackWebpack是一个开源的JavaScript模块打包工具,它可以将许多分散的模块按照依赖关系进行打包,生成优化后的静态资源。Webpack的主要目的是实现前端资源的模块化、组件化以及自动化构建。 2. Webpack的工作原理是什么? Webpack的工作原理主要包括两个部分:一是定义入口文件(entry),即需要打包的JavaScript文件;二是确定输出结果的配置(output),即打包后的文件格式和输出径。Webpack会从入口文件开始,递归地查找并打包所有依赖的模块,最后生成优化后的静态资源。 3. Webpack有哪些常用的加载器(loader)? Webpack提供了多种加载器,用于处理不同类型的文件。常见的加载器有: - babel-loader:将ES6+代码转换为浏览器兼容的JavaScript代码。 - css-loader:解析CSS文件中的@import和url()语句,返回对应的CSS代码。 - file-loader:将文件输出到指定目录,通常用于处理图片、字体等资源文件。 - json-loader:处理JSON文件,将JSON字符串转换为JavaScript对象或数组。 - url-loader:将小于某个大小的URL转换为DataURL,以减小文件体积。 - vue-loader:用于处理Vue.js单文件组件(.vue文件)。 - sass-loader:用于处理Sass或SCSS文件,将其转换为CSS代码。 4. Webpack有哪些常用的件(plugin)? Webpack提供了许多件,用于优化构建过程。常见的件有: - clean-webpack-plugin:在每次构建前清理/dist文件夹。 - html-webpack-plugin:自动生成HTML文件,用于启动开发服务器。 - copy-webpack-plugin:将单个文件或多个文件复制到指定目录。 - optimise-css-assets-webpack-plugin:压缩和优化CSS代码。 - minimizer-webpack-plugin:压缩和优化JavaScript代码。 - terser-webpack-plugin:压缩和优化JavaScript代码。 - webpack-bundle-analyzer:分析打包后的资源文件,提供优化建议。 5. 如何配置Webpack的热更新(Hot Module Replacement, HMR)? 要启用Webpack的热更新功能,需要在项目的入口文件中引入`webpack/hot/dev-server`模块,并在`webpack.config.js`中配置`devServer`选项。以下是一个简单的配置示例: ```javascript const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { hotMiddleware } = require('react-hot-loader'); const config = { // ...其他配置项 devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, port: 9000, hot: true, // 开启热更新功能 historyApiFallback: true, // 使用HTML5 History API作为由模式 }, }; module.exports = config; ``` 6. 如何配置Webpack的多入口(Multiple Entry Points)? 要配置Webpack的多入口,需要在`entry`配置项中添加多个入口文件径数组。例如: ```javascript module.exports = { // ...其他配置项 entry: { main: './src/main.js', app: './src/app.js', }, }; ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值