Automatic route generation and code cutting for vue project function menu configuration

Keywords: Javascript Vue Attribute Webpack

Function menu configuration automatically generates routing and code cutting

Menu is routing, shared configuration

Appointment

The views directory stores the business components; the business modules are stored in folders, and the main routing components are stored in the directory with the routing name named by index.

views * * business components
 └ -- base business group directory, corresponding to the menu group in router directory
    ├ - about business functions
    Main component of index.vue service routing page
    │└ -... Other.vue related block components
    ├── welcome
    │   └── index.vue
    └ -- sub route component of index.vue, available when sub route is enabled in menu

To configure

Each configuration file corresponds to a set of menus, and the file name corresponds to the subdirectory under views. The configuration structure is as follows:

// base.ts
const menu: IMenu = {
  name: 'base',             // Menu name, corresponding to the subdirectory under views directory
  title: 'Essential information',         // menu title
  isPath: true,             // Whether to access the parent path or not. By default, the directory under the menu is the first level route. If this option is enabled, index.vue under the directory will be read as the sub route component. When the file does not exist, the component will be generated automatically.
  routes: [                 // Menu directory
    {
      name: 'about',        // Menu name, corresponding route name
      title: 'about',        // Menu title, corresponding to routing meta information
      filename: 'about',    // Component file name, default is name
      path: 'about',        // The access path is configured in the same way as Vue router, and the default is name.
      ...RouterConfig       // Routing configuration of other Vue router
    },
    {
      name: 'welcome',
      title: 'Welcome'
    }
  ]
}

export default menu

Realization

Menu routing

menuToRoute method converts menu configuration to route configuration

parameter

  • Menu: a single menu configuration object or an array of configuration objects
  • isRoot: whether it is a root route or not; the root route path in the route configuration needs to start with /
// menuToRoute.ts
export default function formatRoute(menu: IMenu | IMenu[], isRoot?: boolean) {
  let _routes: RouteConfig[] = []
  if (Array.isArray(menu)) {
    for (const item of menu) {
      _routes = _routes.concat(formatRoute(item, isRoot))
    }
    return _routes
  }

  const { name: menuName, isPath, routes } = menu
  const children = routes.map(route => {
    const { name, filename = name, path, title, meta, ...config } = route
    return {
      name,
      path: (!isPath && isRoot ? '/' : '') + (path || name),
      component: () =>
        // Use the chunks method to package the code in blocks according to the menu. See the following instructions for details
        chunks(menuName, filename).then(e => {
          // Component name is required when using keep alive dynamic cache include attribute
          // Add 'v -' prefix to avoid "do not use built in or reserved HTML elements as component ID"
          ;(e.default.options || e.default).name = 'v-' + name
          return e
        }),
      meta: title ? { title, ...meta } : meta
      ...config,
    }
  })
  _routes = isPath
    ? [
        {
          path: (isRoot ? '/' : '') + menuName,
          // If the secondary route is used, read the index under the menu directory as the sub route component, and no default component can be found.
          component: () => chunks(menuName, 'index').catch(() => subView),
          children
        }
      ]
    : children

  return _routes
}

Used in combination with other routing configurations:

// @/router/routes.ts
import Home from '@/views/Home.vue'
/* Import all menu configuration arrays */
import menu from './menu/'
import menuToRoute from './menuToRoute'

const ISROOT = true
const routes = [
  {
    path: '/',
    name: 'home',
    component: Home
  },
  ...menuToRoute(menu, ISROOT)
]

export default routes

Code cutting

The component corresponding to the route uses the import() method of webpack( View official documents )Lazy loading, you can modify the chunk method in the menuToRoute.ts file to cut and package the code according to the requirements.

In general small projects, you can directly use import ('@ / views / ${name} / index'). This method packages the index files of each directory as the entry by default, and the related referenced files. In combination with the previous convention that the main routing component is named after index, you can avoid packaging other files that are not referenced.

/**
 * menuToRoute.ts
 * Code cutting and packaging
 * @param type Menu group (folder) name
 * @param name Route name or route corresponding file name
 */
function chunks(type: string, name: string) {
  switch (type) {
    case 'guide':
      // If no filename is specified for the route under the guide menu, the md file will be imported by default. Because md is not the default import type, the extension needs to be added.
      if (!/(^index)|(\.\w+$)/.test(name)) {
        name += '.md'
      }
      return import(/* webpackChunkName: 'guide' */ `@/views/guide/${name}`)

    case 'example':
      // Package all vue and tsx files under views/example /
      return import(
        /* webpackChunkName: 'example',webpackMode: "lazy-once",webpackInclude: /\.(vue|tsx)$/ */
        `@/views/example/${name}`
      )
    default:
      // Package the default import types of all index files in views directory (including subdirectories) in blocks
      return import( `@/views/${name}/index` )
  }
}

Menu application and authority control

Example of NavMenu navigation component applied to elment UI:

  <el-menu :default-active="$route.name" @select="$router.push({ name:$event })">
    <el-menu-item index="home">
      <template slot="title">home page</template>
    </el-menu-item>
    <el-submenu :index="menu.name" v-for="menu of menuList" :key="menu.name">
      <template slot="title">{{ menu.title }}</template>
      <el-menu-item :index="item.name" v-for="item of menu.routes" :key="item.name">
        {{ item.title }}
      </el-menu-item>
    </el-submenu>
  </el-menu>

Permissions filtering

  • In the background, the multi-level function list is established according to the menu structure name, such as menu - > Directory - > tab - > button.
  • Assign different function permissions to roles
  • Each user can specify multiple roles
  • User login returns to user role group for permission consolidation
/**
 * @param menuList List of all permission information configured in the background
 * @param roleList User multiple role information
 **/
function buildUserRule (menuList, roleList) {
  // Merge role permission list
  const menuIdList = [].concat(roleList.map(role=>role.menuIdList))
  // Filter generate user permissions
  const rules = menuList.filter(item => menuIdList.includes(item.id))
  // Generate the directory tree structure through the corresponding relationship between id and parentId in permission information
  return formatTree(rules)
}

Store the generated permission tree structure in the store. The recommended format is as follows:

  {
    Menu name: {/ / corresponding menu group
      Directory name: {/ / corresponding route
        tab Name: {/ / multiple tabs in the routing page
          edit: true / / operation button
          delete: true
        }
      }
    }
  }

This format is convenient for permission filtering when the menu is displayed and for permission adaptation when entering a route.

Original article, reprint please declare, welcome to discuss together! @nicefan.cn

Posted by robh76 on Sat, 26 Oct 2019 12:22:47 -0700