Vue-Router principle realization

Article description: This article is the notes and experience of the front-end training camp of Regao. If there is something wrong, I hope you can point out and teach, thank you!

View-Router

1. Hash mode and History mode

The difference between Hash and History mode

No matter which mode is the implementation of client-side routing, that is, when the routing path changes, no request is sent to the server. It is js that receives the path change and renders different content according to different addresses. If server-side content is needed, it will send an ajax request to get it.

The difference in manifestation:

  • Hash mode
    https://music.163.com/#/playlist?id=3102961863

  • History mode
    https://music.163.com/playlist/3102961863, to use this mode well, server configuration support is required

The difference in principle:

  • Hash mode is based on the anchor point and the onhashchange event; the value of the anchor point is used as the routing address. When the address changes, the onhashchange event is triggered. In the onhashchange event, we record the current routing address and find the component corresponding to the path and then restart Rendering. That is, the content presented on the page is determined according to the path.
  • History mode is based on History API in HTML5. If you want to be compatible with browsers above ie9, you must use hash mode.
    1.
    history.pushState() is only supported after IE10. When history.push() is called, the path will change and a request will be made to the server;
    when history.pushState() is called, no request will be made to the server, only changes will be made The address in the path bar of the browser, and the address is recorded in the history. In other words, you can use pushState() to implement client-side routing. However, it needs to be used after IE10.
    2.history.replaceState()

Use of History mode

  • In a single-page application, if the server does not have an address like http://www.testurl.com/login, it will return that the page cannot be found
  • In addition to static resources, the server should return the index.html of the single-page application

History under nginx
1. Download the compressed package of nginx on the official website
2. Unzip the compressed package to the root directory of the hard disk: D:\nginx-1.17.10, no Chinese can appear in the directory
3. Open the command line and switch to D:\nginx -1.17.10
4. Start nginx

  $ start nginx

5. Open the conf configuration file in the nignx directory.
Insert picture description here
6. Restart after modification:

nginx -s reload

7. Enter the page to visit localhost, you can visit, which solves the problem of history mode in vue-router.

VueRouter implementation principle
VueRouter is a front-end router. When the path is switched, the current path is judged on the browser side and the component corresponding to the current path is loaded.

Hash mode:

  • The content after # in the URL is used as the path address
  • Listen for hashchange events
  • Find the corresponding component to re-render according to the current routing address

History mode

  • Change the browser's address bar through the history.pushState() method, just change the address bar, and record the current address in the browsing history, and will not actually jump to the specified path, that is, the browser will not send like the server request
  • Listen to the popstate event, you can monitor the changes in the history of the browser operation, record the changed address in the popstate processing function, click the browser’s forward or back button, or call the history.back(), forward() methods. Will trigger the popatate event
  • When the address is changed, find the corresponding component based on the current routing address and re-render

VueRouter class diagram: from top to bottom are class names, properties, and methods. _ Is a static method, and + is a public method.
options: Record the objects passed in in the constructor.
routeMap: An object used to record the correspondence between routing addresses and components, and parse routing rules to
data: objects in routeMap , where the current attribute records the current routing address. The purpose of setting this data is to require a responsive object. After the routing address changes, the corresponding component must be automatically updated.

Insert picture description here
Project directory:
Insert picture description here
There are about four steps, first look at the official Vue-router usage (router/index.js):
Insert picture description here

In main.js:
Insert picture description here
implement vuerouter in history mode, the first step:
create a VueRouter folder, then create a new file index.js, create an instance of VueRouter, Vue uses the use (plugins) method to inject plugins into Vue, the use method The install method injected into the plug-in VueRouter will be automatically detected. If there is, the install method will be executed. VueRouter, Vuex including some components are implemented through plug-ins. If it is in the browser environment, the use method will be automatically called in index.js. If it is based on the node environment, you need to call it manually.
_install method: the
beforeCreate hook operation must be mixed into the Vue instance (it will be called during the life cycle of Vue)

// 定义一个全局变量
let _Vue = null

export default class VueRouter {
    
    
  static install (Vue) {
    
    
    // 1.判断当前插件是否已被安装
    // 给install方法增加一个installed属性
    if (VueRouter.install.installed) {
    
    
      return
    }
    // 记录插件被安装
    VueRouter.install.installed = true
    // 2.把Vue构造函数记录到全局变量(我们要在VueRouter的实例方法中类使用Vue的构造函数,比如创建组件的时候使用Vue.component)
    _Vue = Vue
    // 混入,给所有的Vue实例设置选项
    _Vue.mixin({
    
    
      beforeCreate () {
    
    
        if (this.$options.router) {
    
    
          // 3.把创建Vue实例时候传入的router对象注入到所有Vue实例上
          _Vue.prototype.$router = this.$options.router// 获取到Vue实例才能写
          // 当插件注册完成的时候调用初始化方法
          this.$options.router.init()
        }
      }
    })
  }
}

Step 2:
Create the initialization method constructor in VueRouter

// 初始化三个属性options,routeMap,data属性

  constructor (options) {
    
    
    // 记录传入的options
    this.options = options
    // 解析路由规则存储到routeMap里面
    // router-view这个组件会根据当前地址寻找routeMap中对应的组件,渲染到浏览器中
    this.routeMap = {
    
    }
    this.data = _Vue.observable({
    
    
      // 存储当前的路由地址,当路由变化时要自动加载组件
      // data要设置成响应式的对象,observable用来创建响应式对象,可以直接用着渲染函数或者计算属性里面
      current: '/' // 当前的路由地址,默认为 /
    })
  }

Step 3:
First, implement the createRouteMap() method, which converts the routing rules (routes) in the options passed in the constructor into key-value pairs and stores them in the routeMap. The key is the routing address, and the value corresponds to the address. s component. When the address changes, the corresponding component needs to be found and rendered to the browser.

createRouterMap () {
    
    
    // 遍历所有路由规则,然后解析成键值对的形式存储的routeMap这个对象里面来
    // 所有的路由规则都在options这个选项中,构造函数中传入了一个options,里面的某个属性叫routes
    // routes 这个数组存储了所有的路由规则,在构造函数中我们把options这个选项存储到了当前VueRouter的options属性里面
    // 当把routes 所有内容解析出来放到routerMap里面
    this.options.routes.forEach(route => {
    
    
      this.routeMap[route.path] = route.component
    })
  }

Step 4:
Implement the initComponents method. In this method, router-link and router-view components are created.
app.vue:

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/video">Video</router-link>
    </div>
    <router-view/>
  </div>
</template>

The router-link needs to accept the string type parameter to, which is the address of the hyperlink, and render the content of home into the a tag. The
router-view component is equivalent to a placeholder, and the router-view component should be based on the current The routing address is obtained from the current routing component to be rendered to the location of the router-view

initComponents (Vue) {
    
     // 传入Vue而不传入_Vue是为了减少该方法和外部的依赖
    Vue.component('router-link', {
    
    
      // 接受外部传入的参数
      props: {
    
    
        to: String
      },
      // 使用运行时版本的vue也就是不带编译器,此时不支持template选项
      // 编译器的作用只是把template转换为render函数
      // template:'<a :href = "to><slot></slot></a>'
      // 在运行时版本中直接写render函数
      render (h) {
    
    
        return h('a', {
    
    
          attrs: {
    
    
            href: this.to
          },
          on: {
    
    
            click: this.clickHandler// 给click注册clickHandler的点击事件
          }
        }, [this.$slots.default])
        // 要创建的元素对应的选择器;设置a标签属性;通过代码(获取默认插槽:this.$slots.default)的形式获取slot内容放到数组当中来。
      },
      methods: {
    
    
        clickHandler (e) {
    
     // 传入事件参数e
          // 调用pushstate方法改变浏览器的地址栏,不会向服务器发送请求
          history.pushState({
    
    }, '', this.to)
          this.$router.data.current = this.to
          e.preventDefault()// 阻止e的默认行为
        }
      }
    //   template: '<a :href = "to"><slot></slot></a>'
    })

    // 定义变量存储this,initcomponent中的this存储到self里面,self就是VueRouter的实例
    const self = this
    Vue.component('router-view', {
    
    
      render (h) {
    
    
        // 先找到当前路由的地址,再根据当前路由的地址去routermap对象中来找路由地址对应的组件
        // 然后调用h函数帮我们把这个找到的组件转换成虚拟dom直接返回
        // h函数还能直接把一个组件转换成虚拟dom
        const component = self.routeMap[self.data.current]
        return h(component)
      }
    })
  }

Creating the init method includes createRouterMap() and initComponents(), and the subsequent initEvent

init () {
    
    
    this.createRouterMap()
    this.initComponents(_Vue)
    this.initEvent()
  }

Step 5:
Implement initEvent(), which is an initialization method and needs to be placed in init().
After clicking the forward and backward methods of the browser, although the address bar changes, the content on the page is changed, that is, the component corresponding to the address is not loaded. We need to update the component corresponding to the page load address when the browser address changes. At this time, we need the popstate event, which will be triggered when the browser history changes.

initEvent () {
    
     // 注册popstate事件
    window.addEventListener('popstate', () => {
    
    
      // 把当前地址栏的地址取出来,把路径部分取出放到data.current里
      this.data.current = window.location.pathname
    })
  }

Guess you like

Origin blog.csdn.net/weixin_41962912/article/details/111645929