In the development of the VUE project, the need to create a route to the specified directory file configuration, if it is only a small project may be ok, but if it is a large project, this may seem boring and tedious, is there a way to simplify the route configuration? For example, the nuxt.js server automatically generates the routing configuration of the VUe-Router module based on the Pages directory structure. Next, I will show you how to automate routing in non-server rendering.

For the convenience of explanation, the following example content is based on vecli4 scaffolding. This article function source address: github.com/zhicaizhu12… .

Implementation approach

  • routingcomponentIt can be created automatically based on the directory structure.
  • Routing meta informationmetaAnd other routing information is used in the routing configuration fileCustom Blocks (custom-blocks)Contains user-defined route configuration information, for examplemeta, whether the route is loaded on demand and other information if the file does not contain changesThe custom blockIs not automatically generated routing configuration. In this article, the custom block isz-route, in which to customize the required routing information:
< z - the route > {" dynamic ": true," meta ": {" title" : "home page", "icon" : "el - icon - plus", "auth" : "homepage",... } } </z-route>Copy the code
  • Embedded routines by

    For nested routing, you can define a template file in the current directory of the file you want to configure as a child routing, which in this case is_layout.vue, there is only one template that defines nested routinesrouter-viewLabels, such as:
<! -- _layout. Vue - > < template > < div > < p > the parent page content < / p > < the router - the view > < / router - the view > < / div > < / template >Copy the code
  • Dynamic route configuration This section describes how to configure routes dynamically/user/:id?, can be created by_id.vueor_id/index.vueFile implementation, for example.
. |-- user |-- _id.vue ...Copy the code
  • Routing pathpath

    According to the file directory structure created under the specified project folder as the access path of the route, this article specifies isviewsFolders, such as file directories, are shown below.
|-- views
  |-- _layout.vue
  |-- homepage.vue
  |-- system
    |-- user
      |-- index.vue
      |-- _id.vue
Copy the code

Based on the above directories, the expected generated path is as follows

{
   path: '/'. children: [ {path: '/homepage'. }, {path: '/user'. children:[ {path: ':id? '. }]}]}Copy the code

Based on the above implementation ideas, we need to obtain and parse vUE file directory structure and file content information, and automatically generate the required routing configuration information according to the parsed information. Therefore, we need to use the function of Webpack plug-in and VUe-template-Compiler to parse VUE files. No matter adding, deleting or modifying files, routing information can be monitored and automatically updated. Next we’ll show you how to use WebPack to write a plug-in and get and generate a routing configuration file.

Function implementation

The WebPack plug-in consists of:

  • A JavaScript naming function.
  • Define one on the prototype of the plug-in functionapplyMethods.
  • Specifies an event hook bound to WebPack itself.
  • Handles specific data for webPack internal instances.
  • The callback provided by WebPack is invoked when the functionality is complete.
// a JavaScript naming function.
function MyExampleWebpackPlugin() {};// Define a 'apply' method on the prototype of the plug-in function.
MyExampleWebpackPlugin.prototype.apply = function(compiler) {
  // Specify an event hook to mount to webpack itself.
  compiler.plugin('webpacksEventHook'.function(compilation /* Handle specific data for webpack internal instances. * /, callback) {
    console.log("This is an example plugin!!!");

    // Call the webPack callback when the function is complete.
    callback();
  });
};
Copy the code

There are many wepack event hooks. If you need them, you can go to the official webpack document for reference. In this article, the code of automatic routing Webpack plug-in is as follows:

class AutoRoutingPlugin {
  constructor(private options: Options) { }

  apply(compiler: Compiler) {
    // Update the route configuration
    const generate = (a)= > {
      const code = generateRoutes(this.options)
      const to = this1. Options routePath? path.join(process.cwd(),this. The options. RoutePath) : path. The join (__dirname,'./routes.js')
      if (
        fs.existsSync(to) &&
        fs.readFileSync(to, 'utf8').trim() === code.trim()
      ) {
        return
      }
      fs.writeFileSync(to, code)
    }

    let watcher: any = null
    
    // After setting up the initial plug-in, execute the plug-in
    compiler.hooks.afterPlugins.tap(pluginName, () => {
      generate()
    })
    
    // execute before generating resources to the output directory
    compiler.hooks.emit.tap(pluginName, () => {
      const chokidar = require('chokidar')
      watcher = chokidar.watch(path.join(process.cwd(), this.options.pages || 'src/views'), {
        persistent: true,
      }).on('change', () => {
        generate()
      });
    })
    
    // Listening mode stops execution
    compiler.hooks.watchClose.tap(pluginName, () => {
      if (watcher) {
        watcher.close()
        watcher = null}}}})Copy the code

AfterPlugins are initialized, a route configuration file is automatically generated at first startup. Then, we listen for changes in the folder files that need to be configured before generating resources to the output directory. The generateRoutes method updates the route configuration file if a change is detected. The generateRoutes method generates the route configuration information and writes it to a file in the specified directory.

export function generateRoutes({
  pages = 'src/views',
  importPrefix = '@/views/',
  dynamic = true, //Whether chunkNamePrefix = needs to be loaded on demand' ',
  layout = '_layout.vue',
}: GenerateConfig) :string {
  // Specify that the file does not need to generate a routing configuration
  const patterns = ['**/*.vue'.`! * * /${layout}`]

  // Get file paths for all layouts
  const layoutPaths = fg.sync(` * * /${layout}`, {
    cwd: pages,
    onlyFiles: true,})// Get all file paths that need routing configuration
  const pagePaths = fg.sync(patterns, {
    cwd: pages,
    onlyFiles: true,})// Get route configuration information
  const metaList = resolveRoutePaths(
    layoutPaths,
    pagePaths,
    importPrefix,
    layout,
    (file) => {
      return fs.readFileSync(path.join(pages, file), 'utf8')})// Returns what needs to be written to the routing file
  return createRoutes(metaList, dynamic, chunkNamePrefix)
}
Copy the code

As you can see from the above code, we first need to get the template file and the path of the file to configure the route, and then the resolveRoutePaths method uses this information to get further information about the route. So what does the resolveRoutePaths method do?

export function resolveRoutePaths( layoutPaths: string[], paths: string[], importPrefix: string, layout: string, readFile: (path: string) => string ): PageMeta[] { const map: NestedMap<string[]> = {} // splitedLayouts = layoutpaths.map ((p) => p.split('/')) const hasRootLayout = splitedLayouts. Some (item => item.length === = 1) if (hasRootLayout) { ForEach ((path) => {let dir = path.slice(0, path.length-1) // Dir. Unshift (rootPathLayoutName) setToMap(map, pathToMapPath(dir), ForEach ((path) => {setToMap(map, pathToMapPath(path.slice(0, path.length - 1)), path) }) } const splitted = paths.map((p) => p.split('/')) splitted.forEach((path) => { if (hasRouteBlock(path, ReadFile)) {// Check whether there is a custom block, Let dir = path if (hasRootLayout) {// If there is a root template file, insert the template path dir.unshift(rootPathLayoutName)} // SetToMap (map, pathToMapPath(dir), path)}}) return pathMapToMeta(map, importPrefix, readFile, 0, importPrefix) Function getRouteBlock(path: string[], readFile: (path: String) => string) {const content = readFile(path.join('/')) const parsed = parseComponent(content, { pad: 'space', Return parsed.customblocks. Find ((b) => b.type === routeBlockName)} // Whether there is a custom block function hasRouteBlock(path: string[], readFile: (path: string) => string) { const routeBlock = getRouteBlock(path, readFile) return routeBlock && tryParseCustomBlock(routeBlock.content, path, Function pathMapToMeta(map: NestedMap<string[]>, importPrefix: string, readFile: (path: string) => string, parentDepth: number = 0, layout: string, ): PageMeta[] { if (map.value) { const path = map.value if (path[0] === rootPathLayoutName) { path.shift() } ... Const routeBlock = getRouteBlock(path, readFile) if (routeBlock) { Route = tryParseCustomBlock(routeblock. content, path, routeBlockName)}... return [meta] } ... }...Copy the code

There are no implementation details from the above code, but we can get a general idea. We first get the template file path information, then use setToMap to generate a mapping from this information, and then process the non-template files that need to be configured as routes. Similarly, the setToMap method generates a mapping relationship based on their path information. The getRouteBlock and tryParseCustomBlock methods parse the custom block information for each file. Finally, the mapping relationship and the custom block information are combined to generate the desired route configuration information. You can view the detailed implementation in z-auto-route.

The actual project uses the configuration

Add a Z-route label to the header of the VUE file to generate a route. The content in the header is in JSON format

< z - the route > {" dynamic ": false," meta ": {" title" : "root page layout"}} < / z - the route >Copy the code

Dynamic indicates whether the route is loaded on demand. If this parameter is not set, the dynamic parameter is used globally. Note:

  • If there is noz-routeTag indicates that the page does not generate routes
  • Only supported for nowmetaanddynamicTwo Settings.
  • If you needz-routeThe label is highlighted and can be setvs-codesettings.json
"vetur.grammar.customBlocks": {
  "z-route": "json"
}
Copy the code

Run the vscode command

Vetur: Generate grammar from vetur.grammar.customBlocks
Copy the code

Webpack configuration

Configure the content in the weppack configuration file. The following is the configuration information of vue.config.js

// vue.config.js
const ZAutoRoute = require('z-auto-route/lib/webpack-plugin')... configureWebpack:(config) = > {
    config.plugins = [
      ...config.plugins,
      new ZAutoRoute({
        pages: 'src/views'.// Route page file address, default is' SRC /views'
        importPrefix: '@/views/'.// import prefix directory to import page files. Default is '@/views/'.}}),]...Copy the code

Routing File Configuration

// Initialize route import Vue from'vue'
import VueRouter from 'vue-router'
import routes from 'z-auto-route'Vue.use(VueRouter) // Configure additional information based on the project, such as generating menu information based on the route //... const router = new VueRouter({ mode:'history',
  base: process.env.BASE_URL,
  routes,
})

export default router
Copy the code

Instance Project Directory

| - views | -- _layout. Vue / / global layout components | -- homepage. Vue / / homepage | | - system / / system management -- _layout. Vue / / embedded routines by | - role / / role management | - index. Vue | | - user / / the user management, the index | - _id / / user details | -- index. VueCopy the code

Generating a Routing Structure

import _layout from '@/views/_layout.vue'
function system__layout() {
  return import(
    /* webpackChunkName: "system-layout"* /'@/views/system/_layout.vue')}function system_role_index() {
  return import(
    /* webpackChunkName: "system-role-index"* /'@/views/system/role/index.vue')}function system_user_index() {
  return import(
    /* webpackChunkName: "system-user-index"* /'@/views/system/user/index.vue')}function system_user__id_index() {
  return import(
    /* webpackChunkName: "system-user-id-index"* /'@/views/system/user/_id/index.vue'
  )
}
import homepage from '@/views/homepage.vue'

export default [
  {
    name: 'layout',
    path: '/',
    component: _layout,
    meta: {
      title: 'Layout Components',
      hide: true
    },
    dynamic: false,
    children: [
      {
        name: 'system-layout',
        path: '/system',
        component: system__layout,
        meta: {
          title: 'System Administration'
        },
        sortIndex: 0,
        children: [
          {
            name: 'system-role',
            path: 'role',
            component: system_role_index,
            meta: {
              title: 'Role Management'
            }
          },
          {
            name: 'system-user',
            path: 'user',
            component: system_user_index,
            meta: {
              title: 'User Management'
            }
          },
          {
            name: 'system-user-id',
            path: 'user/:id',
            component: system_user__id_index,
            meta: {
              title: 'User Details',
              hide: true
            }
          }
        ]
      },
      {
        name: 'homepage',
        path: '/homepage',
        component: homepage,
        meta: {
          title: 'home'
        },
        dynamic: false,
        sortIndex: -1
      }
    ]
  }
]
Copy the code

Project renderings

Reference source

vue-auto-routing

conclusion

If there are any errors in this article, please correct them in the comments section. If the content of this article can improve the development efficiency of students in the project, please like and follow. Source address: github.com/zhicaizhu12… .