preface

If you have written nuxt.js, you must also have a certain understanding of nuxT routing rules, create files in the Pages directory, that is, you can automatically build routes, this article to lead you to realize how to automatically build routes in vue. I’m using vue-CLI 3.X for initialization. The cli 2.X version is the same, but the initialized file directory is different.

Do the preparation

Understand the require. The context:

An important API in Webpack, it searches through a directory, useSubdirectories specify whether to search subdirectories, and regular expressions to match the file. Understandably, it can search a given directory, so we can use it to automatically build routes, and we can use it to build builds.

  • Directory: indicates the directory to be retrieved
  • UseSubdirectories: Whether to retrieve subdirectories
  • RegExp: a regular expression for matching files

Understand nuxt.js routing rules

Here is a brief talk, do not elaborate. For more information: nuxt.js website: zh.nuxtjs.org

  • Basic route: If the file is index.vue, path is null. That is, if the pages/user/index.vue file exists, the route path is ‘/user’. The name of a route consists of file directories and multi-level splicing, for example, pages/user/index.vue file, where name is user-index
  • Nested routing: If you want to use nested routing, you need to create a folder with the same name and a.vue file in the root directory. For example, there are pages/user/_id. Vue and pages/user.vue at the same level. Constitute a set routine by.
  • Dynamic routing: a dynamic routing file starts with an underscore _. If pages/user/_id. Vue is available, the path of the route is user/:id.

start

One: initial file directory

Create a project using vue create XXX. Select router when selecting configuration. After initialization, we rename the default views folder to Pages (corresponding to the routing directory of nuxt.js), create routes. Js in the Router folder, initialize routes in routes and export routes to router/index.js

# router/index/js

import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'Use (VueRouter) const router = new VueRouter({mode: new VueRouter)'history',
  base: process.env.BASE_URL,
  routes
})

export default router

Copy the code
# router/routes/js

const routes = []
export default routes
Copy the code

In this case, the file directories are as follows :(about.vue and home.vue are the default files after cli creation.)

Router /routes.js start writing automatic build logic.

1: use require.context

Write the following code based on the three parameters we talked about earlier and our directory structure

# router/routes.js
let files = require.context('.. /pages'.true, /\.vue$/) // Search for files according to the directory structureletConsole. log(files) console.log(filesKey) const routes = []export default routes

Copy the code

So let’s see what’s printed out, files is a method, but we’re not going to talk about what we’re going to do, fileskey we can see what’s printed out is just an array of file addresses. Given the address, you can build routes according to the rules

2: But the above is only basic route, we create dynamic route, nested route and dynamic nested route, for logical differentiation, we delete the original pages files, and add new files, directory structure is as follows:

# pages/index.vue

<template>
  <div class="index"> this is the default index, and the route is: /. Base route </div> </template> <script>export default {
  name: 'index'
}
</script>

Copy the code
# pages/group.vue

<template>
  <div class="group"<router-view></router-view> </div> </template> <script>export default {
  name: 'group'
}
</script>

Copy the code
# pages/group/user.vue

<template>
  <div class="user"<router-view></router-view> </div> </template> <script>export default {
  name: 'user'
}
</script>

Copy the code
# pages/group/user/_id.vue

<template>
  <div class="id"> group/user/:id page, dynamic routing <div> userID: {{$route.params.id}}</div>
  </div>
</template>
<script>
export default {
  name: 'id'
}
</script>

Copy the code

Now let’s see what the printed fileskey is.

If we look at fileskeys today, we may find that both basic and dynamic routes are very simple, just follow the rule replace. But nesting is tricky. According to the fileskey we printed before, there are still many questions:

  • From the printed data, how can you tell how many routing objects there are in the end? (Routes will have nested relationships)
  • How do we know if a route has children?
  • How do I know the depth of nesting? What is the maximum depth?
  • How do we know that a route is nested by another route?

3. Analyze problems and give answers. (The solution is just the idea, the specific code logic comes later)

  • From the printed data, how can you tell how many routing objects there are in the end? (Routes will have nested relationships)

A: We can create a map object according to the file name of the level 1 directory. The key in the object is the name of the level 1 directory file, and the value is an array. We can collect all the files in this directory into the array. (The value array of the.vue file object has only itself)

  • How do we know if a route has children?

A: If a file named name can be found in the array containing name.vue, name/XXX, then it must have the children property

  • How do I know the depth of nesting? What is the maximum depth?

The array in the map object holds the path to the file, where we can use the re to derive unnecessary relative paths to the. Vue, such as :group/user/_id. The length of the array cut by the character ‘/’ is the depth. Compare the cut length of each object in the array, and the maximum is the maximum depth.

  • How do we know that a route is nested by another route?

A: The loop starts at 1 and ends at the maximum depth. The route is built layer by layer, and the route of a certain layer (e.g., layer 3) needs to get all of its parent layers if it wants to know who it is nested with (e.g., the current layer is: ./group/user/ id.vue, then all parent levels are group and user), then go to route level by level./group/user/ id.vue

4: Create getRouteItemName method

/** * get route name * @param {*} filetype: string (full file directory) */ const getRouteItemName = (file) => {letmatch = file.match(/\/(.+?) \.vue$/)[1] // Remove the relative path with.vuelet res = match.replace(/_/ig, ' ').replace(/\//ig, The '-') // Remove the underline and change/to -splicereturn res
}
Copy the code

5: Create getRouteItemPath, which replaces /index.vue with index.vue because there is a ‘/’ below the level, and the nested argument does not have a ‘/’

*/ const getRouteItemPath = (file) => {path * @param {*} file String */ const getRouteItemPath = (file) => {return file.replace('/index.vue'.' ').replace('index.vue'.' ').replace('vue'.' ').replace(/_/g, ':').replace(/\./g, ' ')}Copy the code

6: Create the hasFile method.

/** * Check whether there are other files in the directory, notice ((? !${name}).) This is because the directory to be queried may be name/name.vue, which may lead to misjudgment of whether there are children, so non-name should be matched. * @param {*} filetype: string (current directory path) * @param {*} nametype*/ const hasfile = (file, name = file) => new RegExp(file + '/((? !${name}).) `)Copy the code

7: Create the hasVue method.

/** * verify.vue file * @param {*} filetype: string (current directory path) */ const hasVue = (file) => new RegExp(file +)'.vue')
Copy the code

Create the createClassify method. Vue files or folders are classified as one level. The meaning of classification here is to collect all files in the same folder into an array for subsequent operations. The code is as follows:

const createClassify = () => {
  let map = filesKey.reduce((map, cur) => {
    letdislodge = cur.match(/\/(.+?) \.vue$/)[1] // Matches only strings with pure file nameslet key = dislodge.split('/') [0]; / / get a level 1 file name (map [key] | | (map [key] = [])). Push (cur)return map
  }, {})
  return getRoutes(map)
}
Copy the code

9: Create getRoutes method.

/** * Build route * @param {*} maptype: Object */ const getRoutes = (map) => {let res = []
  for (let key inMap) {// iterate over the objectletLevel = map[key] // Retrieve the corresponding valuelet text = level.join(The '@') // Concatenate hierarchical arrays with @, just for easy lookupletExpr1 = hasfile(key) // Check rule, whether there are subfilesletExpr2 = hasVue(key) // Verification rule, vUE file or notletRoute = {} // Initialize the routeif(text.match(expr1) && text.match(expr2)) {// There is a route with childrenletmax = Math.max(... level.map(v => v.match(/\/(.+?) .vue$/)[1].split('/').length)) // Find the deepest level in the directoryletI = 0 // Mark levelwhileForEach ((item) => {route level.forEach((item) => {letwipeOfVue = item.match(/\/(.+?) .vue$/)[1] // matches pure paths, removing relative paths and.vuelet classArray = wipeOfVue.split('/') // Cut for easy operationletLen = classarray. length // Depthif (len === i) {
            if(I === 1) {// Children route = {Component: files(item). Default, path: getRouteItemPath(item), children: []}}else {
              letfile = item.match(/(.+?) \.vue$/)[1] // Matches only the path before.vue in the directoryletName = classArray[len-1] // Obtain the file name in each pathletIteration = classArray.slice(0, len-1) // Intercepts the file pathletChildRoute = {Component: files(item). Default, path: getRouteItemPath(name)} // Searches for subfiles in the file directory. If there are subfiles, the children attribute exists. Match (hasfile(file, name)) && text.match(hasVue(file))? Childroute. children = [] : childroute. name = getRouteItemName(item) // Find the corresponding parent through the directorylet parent = iteration.reduce((map, current, index) => {
                let path = index === 0 ? getRouteItemPath(`/${current}.vue`) : getRouteItemPath(`${current}.vue`)
                returnmap.filter(v => v.path === path)[0].children }, [route]) parent && parent. Push (childRoute)}})} res.push(route) // Add route object}else{// No children, insert level.forEach(item => {route = {Component: files(item).default, name: GetRouteItemName (item), path: getRouteItemPath(item)} res.push(route) // Add route object})}}returnRes // Return the entire route object}Copy the code

10: Now let’s see the effect. (It’s hard to find a screen recording app, not advertising)

Three: the end. The above is all of the article, feel their implementation is not elegant enough, is thrown brick quote, in this, thank you can read to the end. Complete project code:Github.com/18692959234…

Author: Type a code for you

github: github.com/18692959234

Tony blog: websitejob. Xyz/page/pages