The principle route vue

Routes into front-end and back-end routing routing, initially it, there is a back-end routing, but each time the routing switch will need to refresh the page, sends a request, the server returns data, the user experience is not good, and later appeared in the front routing, it is characterized by the browser does not refresh

The front end is divided into two routes, hash mode and history mode

history routing principle

  1. For the first time into the project, have to resolve url, load the corresponding routing

  2. The project has been loaded, the need to switch routes. If you use a label switching, the need to prevent the default event, if the push method using js switch, direct manipulation:

    1. window.history.pushState: a record increase in the browsing history
    2. window.history.replaceState: the current browser address into the address after replaceState, the total length of constant browsing history
    3. onpopstate window object has a method to monitor changes in the history of the stack, as long as there is a change, it will trigger the event
    4. H5 above method provided in the browser does not refresh

history schematics .png

Code:

main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router: router,
  render: h => h(App)
}).$mount('#app')
router.js
import Vue from 'vue'
import VueRouter from './vue-router'
import Home from './views/Home.vue'

Vue.use(VueRouter)

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

const router = new VueRouter({
  routes
})

export default router
index.js vue-router folder
let Vue = null;

export default class Router{
  static install(_Vue){
    Vue = _Vue;
    
    Vue.mixin({
      beforeCreate(){
        if(this.$options.router){
          //根实例挂载了router
          // 路由初始化
          this.$options.router.init();
        }
        if(this.$root.$options.router){
          this.$router = this.$root.$options.router;
        }
      }
    })

  }

  constructor(options){
    this._options = options;
    this._routeMap = {};
    this._vm = new Vue({
      data: {
        currentPath : '/'
      }
    })
  }

  init(){
    // 解析路由表
    this.getRouteMap();
    // 注册组件
    this.initComponent();
    // 解析第一次的路由
    this.loadRoute();
    // 监听返回的事件,重新装载组件
    this.onBack();
  }

  getRouteMap(){
    this._options.routes.forEach(route=>{
      this._routeMap[route.path] = route;
    })
  }

  initComponent(){
    const self = this;
    Vue.component('router-view', {
      render(h){
        const path = self._vm.currentPath;
        const component = self._routeMap[path] && self._routeMap[path].component;
        return h(component);
      }
    });
    Vue.component('router-link', {
      props: {
        to: String
      },
      render(h){
        return h('a', {
          class: {
            'router-link-exact-active': self._vm.currentPath === this.to
          },
          attrs: {
            href: this.to
          },
          on: {
            // a标签点击事件
            click: (ev)=>{
              // 阻止默认事件
              ev.preventDefault();
              // 修改地址栏
              window.history.pushState({}, '', this.to);
              // 装载组件
              self._vm.currentPath = this.to;
            }
          }
        }, this.$slots.default);
      }
    });
  }


  loadRoute(){
    window.addEventListener('load', ()=>{
      //得到路由的地址
      let path = window.location.pathname;
      // 加载路由对应的组件
      this._vm.currentPath = path;
    })
  }

  onBack(){
    window.addEventListener('popstate', ()=>{
      //得到路由的地址
      let path = window.location.pathname;
      // 加载路由对应的组件
      this._vm.currentPath = path;
    })
  }


  push(path){
    // 修改地址栏
    window.history.pushState({}, '', path);
    // 加载路由对应的组件
    this._vm.currentPath = path;
  }

  back(){
    window.history.back();
  }

  forward(){
    window.history.forward();
  }

  go(n){
    window.history.go(n);
  }

}

hash routing principles

The address bar changes to routing switches, need to listen onhashchange
JS switch routing, actually modify the hash value of the address bar

  1. Changing the hash value following the url, it will not send the request to the server, and therefore will not refresh the page every time hash value changes, triggering hashchange events, so we can monitor this event to know the hash value of what happened (location .hash)
  2. Change does not trigger the page jump hash, hash link is a fragment of the current page, the page will scroll to the corresponding position, if not, then there is no effect

hash schematics .png

Code implementation:
main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router: router,
  render: h => h(App)
}).$mount('#app')

router.js

import Vue from 'vue'
import VueRouter from './vue-router'
import Home from './views/Home.vue'

Vue.use(VueRouter)

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

index.js vue-router folder

let Vue = null;

export default class Router{

  static install(_Vue){
    Vue = _Vue;

    Vue.mixin({
      beforeCreate(){
        if(this.$options.router){
          //只有vm实例,而且是配置了router的vm实例,会进来
          this.$options.router.init(); 
        }
        if(this.$root.$options.router){
          this.$router = this.$root.$options.router;
        }
      }
    });

  }


  constructor(options){
    this._options = options;
    
    this._routeMap = {};

    this._routeEach = [];

    this._vm = new Vue({
      data: {
        //保存当前路由所在的地址
        currentPath: '/'
      }
    });

  }

  init(){
    // 解析路由表
    this.getRouteMap();
    
    // 配置组件
    this.initComponent();
    
    // 加载路由
    this.addEvents();
  }

  // 解析路由表
  getRouteMap(){
    this._options.routes.forEach(route=>{
      this._routeMap[route.path] = route;
    });
  }

  initComponent(){
    let self = this; 
    Vue.component('router-view', {
      render(h){
        let currentPath = self._vm.currentPath;
        let component = self._routeMap[currentPath].component;
        return h(component);
      }
    });
    Vue.component('router-link', {
      props: {
        to: String
      },
      render(h){
        return h(
          'a',
          {
            class: {
              'router-link-exact-active': self._vm.currentPath === this.to
            },
            attrs: {
              href: '#'+this.to
            }
          },
          this.$slots.default
        )
      }
    });
  }

  // 加载路由
  addEvents(){
    window.addEventListener('hashchange', this.onHashChange.bind(this));
    window.addEventListener('load', this.onHashChange.bind(this));
  }

  getPath(){
    return window.location.hash.slice(1);
  }


  // 处理路由
  onHashChange(ev){
    console.log('onHashChange函数调用了.....');
    

    //调用路由拦截
    if(this._routeEach.length > 0){
      let {newURL, oldURL} = ev;
      let to = {};
      let from = {};
      if(oldURL){
        // hashChange事件
        // 重新设置currentPath,那么router-view就会重新装载
        to = {
          path: newURL.split('#')[1]
        }
        from = {
          path: oldURL.split('#')[1]
        }
      }else{
        //load事件
        to = {
          path: window.location.hash.split('#')[1]
        }
        from = {
          path: null
        }
      }

      this.runEach(to, from);
      
    }
    //没有拦截
    else{
      let path = this.getPath();
      let route = this._routeMap[path];
      this._vm.currentPath = route.path;
    }
  }

  // 切换路由
  push(path){
    // this._vm.currentPath = path;
    window.location.hash = '#'+path;
  }

  // 路由拦截
  beforeEach(cb){
    this._routeEach.push(cb);
  }

  //执行路由拦截
  runEach(to, from, index = 0){
    console.log(index);
    this._routeEach[index](to, from, ()=>{
      index++;
      if(index < this._routeEach.length){
        this.runEach(to, from, index);
      }else{
        let path = this.getPath();
        let route = this._routeMap[path];
        this._vm.currentPath = route.path;
      }
    });
  }

}

history hash routing and routing difference:

  1. hash # can only be changed back, and the hash value and the original value must be different
  2. pushState can modify the path, query parameters and fragment identifier, pushState more than the hash access method in line with the front end of the route, more elegant, but history mode requires the back-end with
Published 13 original articles · won praise 22 · views 390

Guess you like

Origin blog.csdn.net/weixin_44691775/article/details/104424612