The basic approach to plug-in writing

Recommend you first take a look at the official plugin to use and development methods vuejs.bootcss.com/guide/plugi…

Demand analysis

Let’s seevue-routerUsing steps of

To initialize the project using the official Vue scaffolding, let’s take a look at the following two files:

src/router/index.js

import Vue from "vue";
import VueRouter from "vue-router";

import Home from ".. /views/Home.vue";

Vue.use(VueRouter);
// Vue.use(VueRouter);

const routes = [
  {
    path: "/".name: "Home".component: Home,
  },
  {
    path: "/about".name: "About".// route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () = >
      import(/* webpackChunkName: "about" */ ".. /views/About.vue"),},];const router = new VueRouter({
  // mode: "hash",
  routes,
});

export default router;
Copy the code

src/main.js

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

Vue.config.productionTip = false;

new Vue({
  router,
  render: (h) = > h(App),
}).$mount("#app");
Copy the code

Vue-router call sequence

  1. use

    Vue.use(VueRouter) Note that ⚠️ : vue.use () essentially calls the install method inside the plug-in and passes in the Vue instance as an argument

  1. New Indicates a Router instance

    const router = new VueRouter({
      // Instantiate the parameters passed to the router
      mode: 'history'.base: process.env.BASE_URL,
      routes
    })
    Copy the code
  2. New Vue(), which places the instance in the configuration item of the Vue

    new Vue({
      router, // Note that the router instance is also passed in
      render: h= > h(App)
    }).$mount('#app')
    Copy the code
  3. Use the routing components
    ,
    or use this.$router in the component

So what does vue-Router do internally?

  1. Mount $router globally
  2. Two components are implemented and declared:<router-view/>,<router-link></router-link>

Implementation approach

So first let’s see how do we get$routerMount to components

let Vue; // Save the vue constructor to avoid packing it
VueRouter.install = function (_Vue) {
  Vue = _Vue;
  console.log("options", Vue.$options);
  Vue.mixin({
    beforeCreate() {
      console.log("inner".this);
      console.log(" this.$options.router".this.$options.router);
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router; }}});console.log("end");
};
Copy the code

Vue. Use (Router) is not instantiated for the first time, so Vue.$option is not available (ps: Router) Option = new Vue(); router = new Vue();

2. We can make a global blend during use, obtain the router instance in the configuration item of the Vue root instance at an appropriate point in time, and mount it. The router can be mounted to the prototype of the new Vue() root instance when the mixin configuration item this.$options already contains the router. This attribute is available to all subsequent VueCompont extensions from Vue instances via this.$router

Why define a local VUE

Let me explain the first line of code again;

Since we’re going to use an instance of Vue in the Router class, the simplest idea is of course to import Vue directly;

But this way when we package the release, we include the vue package;

Is it strange that a plugin for vue includes a vue package? So we create a local variable. When vue.use () is passed in, the first argument is an instance of Vue, and we assign this instance to our local variable to accomplish our purpose:

The vue instance can be called without being packaged;

So this is one of the reasons why we have to use many plug-ins first (another reason is to mount the instance);


For example: vuex, you have to use before new Store (); Used to hang instances and save vue instances

How do you implement those two routing components

How does the routing component work first

<div id="app">
  <div id="nav">
    <! -- a tag controls the jump -->
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </div>
  <! -- Route exit -->
  <router-view />
</div>

Copy the code

As can be seen from the above, clicking router-link is equivalent to clicking a tag, and then the href attribute of a tag controls the change of page routing. Listen for route changes and output different templates in router-view.

There are two ways to change routes

Hash mode:

The hash of the URL is used to simulate a complete URL. The hash of the URL will be displayed in the network path, but will not be included in the HTTP request. It has no impact on the back end. http://localhost:8080/#/home http://localhost:8080/#/user Hash: hashChange

The history mode:

Embellished hash mode. The path does not contain “#”. ** history API based on Html5 404; ** history API based on Html5 404; ** History API based on Html5 404; ** History API based on Html5 404; ** History API based on Html5 404; The history model example: http://localhost:8080/home http://localhost:8080/user history to monitor routing: The popState, pushState() history mode monitors the route in two cases, one isto directly listen to the popState event refresh page component, this is only suitable for the first visit to the web page, the browser forward and backward case. Because history.pushState() or history.replacEstate () do not trigger a popState event. The other option is to change the URL through router.push and router.replace. In this case, there are actually two steps inside the two apis:

  1. Page component updates (that is, updating the router’s current property)
  2. The underlying call history. PushState/history. ReplaceState change url

Let’s implement a simple hash route change

If you don’t know how to register a global component, you’d better look at the following component infrastructure

Let’s take a look at router-link

// Register and implement two components
Vue.component("router-link", {
  // There is a mandatory to value, the href inside the a tag, used to change the URL
  props: {
    to: {
      required: true,}},render(h) {
    return h(
      "a",
      {
        attrs: { href: "#" + this.to },
      },
      // Default slot
      this.$slots.default ); }});Copy the code

Let’s take a look at router-view

class VueRouter {
  constructor(options) {
    // Accept the parameters passed in
    this.$options = options;
    const initial = "/";
    // Change current to responsive data,
    // When modifying curent in the hashchange callback,
    // The current router-view render function will be rerendered
    Vue.util.defineReactive(this."current", initial);
    // Listen for route changes
    window.addEventListener("hashchange".() = > {
      // Get the hash in the current URL
      this.current = window.location.hash.slice(1);
    });
  }
}

VueRouter.install = function (_Vue) {
  Vue = _Vue;
  Vue.component("router-view", {
    render(h) {
      // Get the component of the current route and render it
      const { current, $options } = this.$router;
      Note that the routes we pass in is a routing table, as shown in Figure 1
      // So here we find the item that matches the current route and render the component directly
      const route = $options.routes.find((item) = > {
        return item.path === current;
      });
      let component = route ? route.component : null;

      returnh(component); }}); }Copy the code

FIG. 1

Full demo code

// What are we trying to achieve
// 1
// 2

// Save the vue constructor to avoid packing it
let Vue;
class VueRouter {
  constructor(options) {
    this.$options = options;
    const initial = "/";
    Vue.util.defineReactive(this."current", initial);
    this.current = "/";
    window.addEventListener("hashchange".() = > {
      // Get the hash in the current URL
      this.current = window.location.hash.slice(1); }); }}// Argument 1 is passed in when vue.use () is called,
VueRouter.install = function (_Vue) {
  Vue = _Vue;
  console.log("options".this);

  // global mixin
  // Purpose: Delay the following logic until the router is created and attached to the option
  Vue.mixin({
    // it is executed when each component creates an instance
    beforeCreate() {
      / / this. $options. The router; That is, the router instance placed in the new Vue
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router; }}});// Register and implement two components
  Vue.component("router-link", {
    props: {
      to: {
        required: true,}},render(h) {
      return h(
        "a",
        {
          attrs: { href: "#" + this.to },
        },
        this.$slots.default ); }}); Vue.component("router-view", {
    render(h) {
      // Get the component of the current route and render it
      const { current, $options } = this.$router;
      const route = $options.routes.find((item) = > {
        return item.path === current;
      });
      let component = route ? route.component : null;

      returnh(component); }}); };export default VueRouter;
Copy the code