I met

Front End time read an article on nuggets and learned about the require.context API. After doing some research on this API, I had an idea to duplicate nuxtJS routing rules with require.context. The routing code of NuxtJS is simply read, and it is found that gulp and global are used to monitor the changes of files under pages folder, and then compile into the corresponding route (not deep understanding, Context is used to import file modules directly from the XX folder.

require.context(path: string, deep? : boolean, filter? :RegExp, mode? :"sync" | "eager" | "weak" | "lazy" | "lazy-once")
Copy the code

This is how the API is used. Assume that there are @/pages/A/index.vue and @/pages/B/ b. vue in the pages folder of the project. So if you introduce it this way you get the following result.

const context = require.context("@/pages/".true./\.vue/)
===>
context = webpackContext(req) {
	var id = webpackContextResolve(req);
	return __webpack_require__(id);
}
Copy the code

You can get the file path if you call the context keys method.

const pages = context.keys()
===>
pages = ["./A/A.vue"."./B/B.vue"]
Copy the code

If you want to introduce the page module, you just need to

const pageModule = context("./A/A.vue")
===>
pageModule = Module {default: {... },__esModule: true.Symbol(Symbol.toStringTag): "Module"}
Copy the code

The pageModule, the pageModule, is written in the component of the vue-router route.

imagination

With this in mind, I came up with the nuxTJS route import method, but I felt. Assigning routing rules to folder nesting makes the whole file relationship very confusing. It’s not very neat, and if every file has an index. CSS in addition it would be a huge headache to maintain the project. So I’m going to flatten the page into one level. This is a neat way to wrap the routing of a page into a JSON file. In this way, the entire route relationship is also obvious in the JSON file.

implementation

When you have an idea, put it into practice. First read the VUE file module

const context = require.context("@/pages/".true./\.vue/)
Copy the code

Create the JSON file. In addition, the routing relationship is written and imported.

// routeHierarchyMap.json
{
  "/": {},
  "A": {},
  "B": {}}Copy the code

And then the route is parsed, and I’m thinking level by level, because the route might have multiple children. First, import the route into an object, whose key-value result is as follows:

const pageObj = {
  "/": Module,
  "A": Module,
  "B": Module
}
Copy the code
// routes indicates the route mapping of the JSON file
function getRouteFromObj(routes, parentsRoute) {
  const routesMap = Object.keys(routes);
  return routesMap.map((item) = > {
    const path = `${parentsRoute.replace(/ / / $/."")}/${item.replace(/ / / $/."")}`;
    return {
      path,
      component: () = > pageObj[item].component,
      children: getRouteFromObj(routes[item], path),
      name: "",}}); }Copy the code

call

import routeMapData from "routeHierarchyMap.json"
getRouteFromObj(routeMapData, "")
// Get the route as follows= = = > [{path: "/A".component: () = > Module,
    children: [].name: ""
  },
  {
    path: "/B".component: () = > Module,
    children: [].name: ""}]Copy the code

If there are nested relationships, for example:

// routeHierarchyMap.json
{
  "/": {
    "A": {
      "B": {}}}}Copy the code
// Get the route as follows[{path: "/".name: "Home".component: () = > Module,
    children: [{path: "/A".component: () = > Module,
        children: [{path: "/B".component: () = > Module,
            children: [].name: ""}].name: ""}}]]Copy the code

In addition, there may be some personalized routing rules, such as writing needLogin or name values in mate, etc. So add a __meta to the JSON of the route map to hold the personalized configuration as follows:

// routeHierarchyMap.json
{
  "/": {
    "__meta": {
      "name": "home"}},"A": {
    "__meta": {
      "name": "a"."meta": {
        "needLogin": true}}},"B": {
    "__meta": {
      "name": "b"}}}Copy the code

The function that resolves the route needs further optimization

// routes indicates the route mapping of the JSON file
function getRouteFromObj(routes, parentsRoute) {
  // Remove key from "__meta"
  const routesMap = Object.keys(routes).filter((item) = >item ! = ="__meta");
  return routesMap.map((item) = > {
    const path = `${parentsRoute.replace(/ / / $/."")}/${item.replace(/ / / $/."")}`;
    return {
      path,
      component: () = > pageObj[item].component,
      children: getRouteFromObj(routes[item], path),
      // Add other attributes. routes[item].__meta, } }); }Copy the code

Ok, so we have implemented import based on JSON route mapping, but so far, just a modification of the routes notation to simplify the import step, so how to make this automatic? If you rely on the above, I can think of automated production pages that rely on scripts and automated write route mapping. Just like hexo, to create a post I just need hexo create . In this case, when creating a page, write a template in the Pages folder through FS. Write the corresponding route in the RouteHierarchymap. json mapping file. It can be said that it is not very convenient and quite troublesome to use.

redesign

The disadvantage of relying on the route mapping file is that it is too cumbersome. It simplifies the original step by a small step, but increases the cost of its use, which is not a friendly measure for developers. Therefore, nuxt.js route construction rules are referred to to reflect the relationship of real routes through the relationship of file routes.

Context is used to import modules. Modules are used to import modules. Since this method is entirely dependent on file path resolution, personalized meta processing cannot be performed.

function createRoutes(routeConfig) {
  const { modules, redirect = "/" } = routeConfig;
  // Routes at the end
  const routes = [];

  const pages = modules.keys();
  for (const page of pages) {
    // Parse the route of each vue file
    addRoute(routes, page, modules);
  }
  / / out
  routes.push({
    path: "/:pathMatch(.*)*",
    redirect,
  });

  return routes;
}
Copy the code

A function that parses routes

function addRoute(routes, route, modules) {
  // Current file path
  const routeStr = route
    .replace(/ ^ \ \ / /."")
    .replace(/(\.\w+)$/."")
    .replace(/^index/."home")
    .replace(/\/index$/."");
  // Route name
  const name = routeStr.split("/").join("-").replace(/_/g."");
  const path = routeStr.startsWith("home")? routeStr.replace(/^(home\/|home)/."/")
    : "/" + routeStr;
  // Parent route -findparent is the function to find the parent route
  const target = findParent("/" + routeStr, name, routes);
  // Parent routing path
  consttargetFullPath = target? .meta? .fullPath ||"";
  // Routing path
  const finalPath = targetFullPath
    ? path.replace(String(targetFullPath), "")
    : path;
  // toProxy converts the routing object toProxy. Delete if children exist, delete the name field
  const targetRoute = toProxy({
    name,
    path: finalPath.replace(/_/g.":") | |"/".// moduleResolve route parsing function
    component: moduleResolve(modules(route)),
    meta: {
      originName: name,
      fullPath: path,
    },
  });
  if (target) {
    constchildren = target? .children ?? []; target.children = [...children, targetRoute];return;
  }
  routes.push(targetRoute);
}
Copy the code

Route resolution

function moduleResolve(module) {
  if (!module.default && module instanceof Promise) return () = > module;
  if (module.default && module.__esModule) return module.default;
  throw Error("Routing module resolution failed!");
}
Copy the code

To the Proxy

function toProxy(target) {
  return new Proxy(target, {
    set(target, p, value) {
      if (p === "children") delete target.name;
      return(target[p.toString()] = value); }}); }Copy the code

Search for the parent route

function findParent(route, name, routes, beforeTarget) {
  const target = routes.find(
    (item) = >
      route.startsWith(item.path) &&
      name.startsWith(String(item? .meta? .originName ||"")));constrouteRest = route.replace(target? .path ??""."");
  if (target && routeRest.split("/").length === 2) return target;
  if (target && routeRest)
    return findParent(routeRest, name, target.children || [], target);
  if (beforeTarget && routeRest) return beforeTarget;
  return target;
}
Copy the code

Routes parsed in this mode are as follows: Refer to Nuxtjs for routing mode

The basic routing

pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue
Copy the code
router: {
  routes: [{name: 'index'.path: '/'.component: 'pages/index.vue'
    },
    {
      name: 'user'.path: '/user'.component: 'pages/user/index.vue'
    },
    {
      name: 'user-one'.path: '/user/one'.component: 'pages/user/one.vue'}}]Copy the code

Dynamic routing

See Nuxtjs

Embedded routines by

See Nuxtjs

Dynamic nesting routines by

See Nuxtjs

conclusion

This is my study of require.context API and some thoughts. The former is a traversal and recursive generated route to a routing mapping object, and the latter is a parse generated route to a file path. Both perform generated routing at run time, which may have some impact on performance, and are not suitable for all scenarios.

Automatic register components, automatic register routes — lazy benefits (vUE, React) Reference: Nuxtjs

The role of require.context in webpack