前端自动生成路由

2,637 阅读2分钟

前言

在大前端的工程化趋势下,自动化也成为前端不可分离的一部分,自动化为我们日常开发中提高了不少效率且能少码不少代码,单配置一个json文件就可以生成一系列想要的代码是开发者的终极目标,本章将基于react讲述如何通过json文件生成对应路由。

准备工作

在开始之前,先检查一下你的版本(我是基于react的,vue的也是一样):

  1. react >= 16
  2. npm <= 11 (题主的是10.15.3)
  3. gulp 3.9.1(3.+)

由于高版本的node会与我当前gulp的版本会有冲突,所以务必确保npm版本要低于11,可以使用nvm进行node的版本管 理确保你可以自由的切换node版本。

路由书写

一般我们书写react路由通过react-router-dom提供的方法来书写,并且配备一些懒加载等优化处理。

import React, { Suspense, lazy } from "react";
import { Route, Switch } from "react-router-dom";
const loading = true;
const router = (
    <Suspense fallback={loading}>
        <Switch>
          <Route
            path="/operator/list"
            exact
            component={lazy(() =>
              import("../view/operator/list/app")
            )}
          />
        </Switch>
    </Suspense>
)

如上,后续有新增路由,在后面继续新增即可,但当路由数目较多时候,我们所做的重复性操作就有些频繁。当重复操作过多的时候,就适合使用自动化让项目变得灵活一些。

自动生成路由

利用glup以及glup-rename来完成我们的自动化生成,实际上node也可以来完成,node作为前端工具来说是相当不错的,但我们这里主要叙述利用glup来完成。

在开始之前,首先我们要定义好必要的模板,比如主页、404页等等。

// 定义路由模板
const routerTemplates = `
import React, { Suspense, lazy } from 'react'
import { Route, Switch } from 'react-router-dom'

const Home = lazy(() => import('../view/home/app'))
const NoMatch = lazy(() => import('../view/error/404.jsx'))
const loading = true
{$$ImportComponent$}
const router = (
  <Suspense fallback={loading}>
  <Switch>
  {$$SubRoutes
    <Route path="/" exact$} component={Home}/>
    <Route path="*" component={NoMatch}/>

  </Switch >
  </Suspense>
)
export default router
`
// 定义生成路由占位
const subRouteTemplate = `
    <Route path="{$$route$}" exact component={lazy(() => import('../view{$$path$}/app'))}/>
`
// 定义要解析的json
const MENUS_PATH = './src/static/app/js/route/menus.json'
// 定义输出目录
const MUNUS_DEST_PATH = './src/static/app/js/route/'

定义好模板后,我们利用through2来处理流,以及占位符来替换掉我们想要的路由。

const through = require('through2')

function generateRoutes() {
  const subLoader = []
  const subRoutes = []
  return through.obj((file, enc, cb) => {
    const list = JSON.parse(file.contents.toString())
    list.map((menu) => {
      menu.items.map((item) => {
        item.routes.map((route) => {
          subRoutes.push(
            subRouteTemplate
              .replace('{$$route$}', route.route)
              .replace('{$$name$}', route.name)
              .replace('{$$path$}', route.path)
          )
        })
      })
    })

    const final = routerTemplates
      .replace('{$$ImportComponent$}', subLoader.join(''))
      .replace('{$$SubRoutes$}', subRoutes.join(''))
    file.contents = Buffer.from(final)
    return cb(null, file)
  })
}

最后我们利用glup来定义任务,即可通过命令执行自动生成路由。

gulp.task('generate-routes', () => {
  return gulp
    .src(MENUS_PATH)
    .pipe(generateRoutes())
    .pipe(rename('routes.jsx'))
    .pipe(gulp.dest(MUNUS_DEST_PATH))
})

完整代码:

/* eslint-disable array-callback-return */
const gulp = require('gulp')
const through = require('through2')
const rename = require('gulp-rename')

const MENUS_PATH = './src/static/app/js/route/menus.json'
const MUNUS_DEST_PATH = './src/static/app/js/route/'

const routerTemplates = `
import React, { Suspense, lazy } from 'react'
import { Route, Switch } from 'react-router-dom'

const Home = lazy(() => import('../view/home/app'))
const NoMatch = lazy(() => import('../view/error/404.jsx'))
const loading = true
{$$ImportComponent$}
const router = (
  <Suspense fallback={loading}>
  <Switch>
  {$$SubRoutes
    <Route path="/" exact$} component={Home}/>
    <Route path="*" component={NoMatch}/>

  </Switch >
  </Suspense>
)
export default router
`

const subRouteTemplate = `
    <Route path="{$$route$}" exact component={lazy(() => import('../view{$$path$}/app'))}/>
`
function generateRoutes() {
  const subLoader = []
  const subRoutes = []
  return through.obj((file, enc, cb) => {
    const list = JSON.parse(file.contents.toString())
    list.map((menu) => {
      menu.items.map((item) => {
        item.routes.map((route) => {
          subRoutes.push(
            subRouteTemplate
              .replace('{$$route$}', route.route)
              .replace('{$$name$}', route.name)
              .replace('{$$path$}', route.path)
          )
        })
      })
    })

    const final = routerTemplates
      .replace('{$$ImportComponent$}', subLoader.join(''))
      .replace('{$$SubRoutes$}', subRoutes.join(''))
    // eslint-disable-next-line no-param-reassign
    file.contents = Buffer.from(final)
    return cb(null, file)
  })
}

gulp.task('generate-routes', () => {
  return gulp
    .src(MENUS_PATH)
    .pipe(generateRoutes())
    .pipe(rename('routes.jsx'))
    .pipe(gulp.dest(MUNUS_DEST_PATH))
})

配置路由的json文件:

[
  {
    "text": "管理系统",
    "key": "mng",
    "items": [
        {
            "key": "operator",
            "text": "运营商",
            "routes": [
                  {
                    "title": "商品管理",
                    "name": "product_mng",
                    "route": "/operator/product/list",
                    "path": "/operator/product/product_mng/list"
                  }
            ]
        }
    ]
  }
]

之后在控制台执行命令generate-routes即可生成路由。

最后

类似的方法也不止这一种,也可以使用node的读写流进行操作,webpack也有提供获取上下文的api,如require.context等。

如果文章对你有帮助,还望不吝三连~