preface

The rise of single page is inseparable from front-end routing. I remember when I first came into contact with the concept of SPA single page application in my childhood, I once doubted the reliability of this technology and was filled with a lot of puzzlement, such as: Is there no performance problem to write everything on one page? Does the page crash if something goes wrong?

Later, as I did one SPA project after another, I gradually dispelled such concerns. Now let’s study the soul of the order page, routing is how to achieve the logic of it.

The meaning of #(hash) in the URL

For example, vue-Rouer has two working modes: hash and history. Let’s take a look at #hash

The anchor

See # is the most easy to associate with the anchor point, just like reading the nuggets article, you can use the anchor point to realize the jump

This is probably the most essential use of #(hash)

change#Does not trigger web page reloading

Another important feature of # is that changing the content after # does not cause a page refresh

It is this feature that makes it possible to implement front-end routing using #, and we will make a bold assumption here that the hash implementation will listen for changes after # and dynamically render the corresponding component

History Api

From the perspective of hash, we can draw a simple conclusion that to realize front-end routing, the condition that the page cannot be refreshed when the PAGE URL changes must be met

We found in the History Api that pushState seems to satisfy this condition as well

Next we use code verification, code is also very simple, set a timer, after a period of time through this API to change the page URL, to see if the page refresh

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      setTimeout(() = > {
        history.pushState(null.""."/a");  // Core code
      }, 3000);
    </script>
  </body>
</html>
Copy the code

Notice that the following URL changes from 127.0.0.1/aa.html to 127.0.0.1/a without refreshing the page.

This also opens up the possibility of implementing front-end routing. Let’s take vue-Router as an example to learn how to realize the front-end routing of SPA.

Vue-router workflow

Vue plug-in

Vue-router is essentially a vUE plug-in

import Vue from "vue";
import VueRouter from "vue-router";
import Home from ".. /views/Home.vue";

Vue.use(VueRouter); // Plugins are used

const routes = [
  {
    path: "/".name: "Home".component: Home,
  },
  {
    path: "/about".name: "About".component: () = >
      import(/* webpackChunkName: "about" */ ".. /views/About.vue"),},];const router = new VueRouter({
  routes,
});

export default router;
Copy the code

Since it is a plug-in, we will first clarify the vue plug-in operation mechanism, first to clarify a few questions.

  • What did Vue.Use do?

    We can find the answer in the vue2.0 source code

    export function initUse(Vue: GlobalAPI) {
    Vue.use = function (plugin: Function | Object) {
      const installedPlugins =
        this._installedPlugins || (this._installedPlugins = []);
      if (installedPlugins.indexOf(plugin) > -1) {
        return this;
      }
      const args = toArray(arguments.1);
      args.unshift(this); // Set the first parameter to the vue instance
      // Core code
      if (typeof plugin.install === "function") {
        plugin.install.apply(plugin, args);
      } else if (typeof plugin === "function") {
        plugin.apply(null, args);
      }
      installedPlugins.push(plugin);
      return this;
    };
    }    
    Copy the code

    The main thing the vue.use() method does is call the plug-in’s install method and pass the vue instance to the plug-in for use. So we must expose an install method for vUE to call when we develop the plug-in

  • Why does vue-rouer need to be mounted in main.js when some plug-ins don’t

    This is usually the case when we use vue-Router

    //router/index.js
    import Vue from "vue";
    import VueRouter from "vue-router";
    Vue.use(VueRouter); // Call vue-router with the use method
    const routes = [];
    const router = new VueRouter({
      routes,
    });
    export default router;
    Copy the code

    Then mount it in main.js

    import Vue from "vue";
    import App from "./App.vue";
    import router from "./router";
    Vue.config.productionTip = false;
    new Vue({
     router  / / a mount
    }).$mount("#app");
    Copy the code

When using vue-Router for the first time, I wondered why I would mount it when I already called it using the use method. This is obviously superfluous

However, when I read about vue-Router on the VUE website, I felt there was more to it than that

When we look at the vue-Router source code, we find that adding some component options through global blending means getting the vue-Rouer configuration items through blending

// vue-router src/install.js
  Vue.mixin({
    beforeCreate () {
    // Determine whether it is the root component
      if (isDef(this.$options.router)) {
        this._routerRoot = this
        this._router = this.$options.router
        this._router.init(this)
        Vue.util.defineReactive(this.'_route'.this._router.history.current) // Set to responsive data
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this.this)
    },
    destroyed () {
      registerInstance(this)}})Copy the code

The question is, why blend in the vue-Router configuration? Vue. Use (vueRouter) is executed before new vue()… It’s too hard to describe in words. Let’s draw pictures

The router is also used in new Vue, but sometimes I think it is unnecessary to mount the router in new Vue. We can also pass the router as an argument, for example:

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import VueRouter from "vue-router";

Vue.use(VurRouter,router) // Call use in main.js, passing router as the second parameter
Copy the code

Vue-rotuer configuration items are also available in vue-Router, as shown below:

class VueRouter{}
VueRouter.install=(vue,option) = >{}
Copy the code

So why didn’t Vue-Rotuer do that? Welcome to comment in the comments

vue-routerOperation mechanism

The core function of vue-Router is to automatically render the corresponding component when the URL changes

A picture is worth a thousand words. Let’s summarize vue-Rouer’s main workflow flow chart

There may be some details involved, such as:

  1. <router-view>and<router-link>How to achieve;
  2. How components are rendered;
  3. How to render nested routines


and

are vue global components registered in vue-Rotuer.

is a simple note, which is actually a router-link. We will focus on how to render

and nested routines



It should be noted that component rendering is from outside to inside. Render the parent component first. If there is

in the parent component, render the corresponding child component until all components are rendered. Render is rendered by the H method of the render function. The following is vue-Rouer’s core code about router-View

Simple version ofvue-rotuerimplementation

Vue-rotuer source code is not long, but it is not easy to read it completely. I removed the core code and implemented a simple version of vue-Router, as shown below:

The main functions are:

  1. Implement the corresponding components according to the route rendering, and achieve nested rendering
  2. implementationthis.$rouer.push()Method, and other methods are freely extensible

The details are as follows:

The router – the link

function isActive(location) {
  return window.location.hash.slice(1) === location;
}
export default {
  functional: true.render(h, { props, slots }) {
    const active = isActive(props.to) ? "my-vue-router-active" : "";
    return h(
      "a",
      {
        attrs: {
          href: ` #${props.to}`.class: [`${active}`],
        },
      },
      slots().default[0].text ); }};Copy the code

The router – the view

export default {
  functional: true.render(h, { parent, data }) {
    let route = parent.$route;
    let matched = route.matched;
    data.routerView = true;
    let deep = 0;
    while (parent) {
      if (parent.$vnode && parent.$vnode.data.routerView) {
        deep++;
      }
      parent = parent.$parent;
    }
    let record = matched[deep];
    if(! record) {return h();
    }
    let component = record.component;
    returnh(component, data); }};Copy the code

Source address: simple-vue-router

Personal project: Webpack based automatic routing packaging multi-page application LYh-Pages, welcome everyone star oh, thank you!

The last

If there is help, welcome to praise attention oh! 😘