Vue: the basic principles of vue-router

Vue: the basic principles of vue-router

I. Introduction

  • Most Vue applications are single-page applications, and the most critical tool for implementing single-page applications is router. The bottom layer of router encapsulates the history class of the browser, so that the browser does not need to request a new page when the page is switched;

Second, the basic knowledge of vue-router

1. Three modes of vue-router

  • Vue-router has three modes in total: HTML5 History, HashHistory, AbstractHistory (not expanded temporarily)

  • History mode:

    • For example: http://test.com/abc

    • popstate event:

      Definition: When the browsing history (history object) of the same document changes, the popstate event will be triggered;

      note:

      1. The event cannot be triggered simply by calling the pushState method or the replaceState method. It will only be triggered when the user clicks the browser forward or back button, or when the history.back/forward/go method is used.

        1. Only for the same document, if the browsing history switch will switch different documents, the event will not be triggered;

      Usage: When using, you can specify a callback function for the popstate event. The parameter of this callback function is an event event object, and its state attribute points to the first parameter of the pushState and replaceState methods (that is, the state object of the url).

  • Hash mode:

    • For example: http://test.com/#/abc

    • If you click the redirect link or the browser history jump, the hashchange event will be triggered, and the corresponding routing rules will be matched by parsing the url, thereby jumping to the abc page;

      • Triggering conditions of hashchange event:

        Change the browser address directly, add or change #hash at the end

        By changing the value of location.href or location.hash

        By clicking on the link with a dot

        The forward and backward of the browser may change the hash, provided that the value of the hash is different

    • If it is manually refreshed, the browser will not send a request to the server, but it will not trigger the hashchange event. You can use the load event to parse the url, match the corresponding routing rules, and jump to the abc page;

    • Hash mode uses dom replacement to change the page content;

  • Abstract mode: Pending

2. The key implementation of Hash routing

  • First, create an index.html page, which contains a tag, a hash value in the tag, and page jump can be performed;

  • Prevent the browser's default behavior, which is the redirection of links.

  • Capture the content of the a tag as a hash value;

  • Perform browser hash jump;

  • Implementation logic in Vue source code:

    $router.push()=>

    hashHistory.push()=>

    History.transitionTo()=>

    History.updateRoute()=>

    {app._route = route}=>

    vm.render()

  • Key code implementation:

    // 捕获hash
    document.querySelectorAll('a').forEach(item=>{
          
          
        item.addEventListener('click',e=>{
          
          
            e.preventDefault();
            let link = item.textContent;
            location.hash = link;
        },false);
    })
    
    // 监听路由
    window.addEventListener('hashchange',e=>{
          
          
        console.log({
          
          
            location : location.href,
            hash : location.hash
        });
        // 根据hash,进行dom操作
    })
    

3. The key implementation of History routing

  • First, create an index.html page, which contains a tag, and a target path in the a tag, which can be used to jump to the page;

  • Prevent the browser's default behavior, which is the redirection of links.

  • Capture the content of a tag as the target path;

  • Use the history.pushState method to change the page state;

  • Key code:

    // 捕获路径
    document.querySelectorAll('a').forEach(item=>{
          
          
        item.addEventListener('click',e=>{
          
          
            e.preventDefault();
            let link = item.textContent;
            if(!!window.history %% history.pushState){
          
          
                window.history.pushState({
          
          name : 'history'},link,link);
            }else{
          
          
                // 不支持,安装polyfill补丁
            }
        },false);
    })
    
    // 监听路由
    window.addEventListener('popstate',e=>{
          
          
        console.log({
          
          
            location : location.href,
            state : e.state
        });
        // 根据路径,进行dom操作
    })
    

4. Navigation Guard

  • Function: As the name suggests, the vue-routerprovided navigation guard is mainly used to guard the navigation by jumping or canceling. In layman's terms, it is to detect specific changes in the route jump process. There are many opportunities to be implanted in the routing and navigation process: global, single-route exclusive, or component-level;

  • Three types of navigation guards: global guards, route guards, component guards

  • The global guard is used when the instance object of the route is registered: beforeEach, beforeResolve, afterEach

  • The route guard is defined in the route configuration item: beforeEnter

  • The component guard is defined in the component properties: beforeRouteEnter, beforeRouteUpdate, beforeRouterLeave

  • Each guard method receives three parameters:

    to: Route, the route object to be entered

    from: Route, the route that the current navigation is about to leave

    next: Function. Finally, you must call this method to resolve this hook. The execution effect depends on the call parameters of the next method:

    1. next() : 进行管道中的下一个钩子。如果全部执行完了,则导航的状态就是comfirmded。
    2. next(false) : 终端当前的导航。若浏览器的url改变了,那么url地址会重置到from路由对应的地址;
    3. next('/')或者next({path : '/'}) : 跳转到一个不同的地址,当前的导航被中断,然后执行一个新的导航。可以向next转递任意位置的对象,且允许设置`replace: true`、`name: 'home'` 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
    4. next(error) : (2.4.0+)若传入next的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调函数。
    
  • Complete navigation analysis process:

    1. Navigation trigger;
    2. Call the beforeRouteLeaveguard on the component that is about to be deactivated ;
    3. Call the global beforeEachguard;
    4. Call the beforeRouteUpdateguard in the pet component ;
    5. Call the beforeEnterguard in the routing configuration ;
    6. Resolve asynchronous routing components;
    7. Call the beforeRouteEnterguard in the next activated component ;
    8. Call the global beforeResolveguard;
    9. Navigation is confirmed;
    10. Call the global afterEachhook;
    11. Trigger the update of the DOM;
    12. Call beforeRouteEnterthe callback function passed to next in the guard, and the created component instance will be passed in as the parameter of the callback function;

Third, the source code analysis of vue-router

1. Source code analysis

  • First look at the constructor of vue-router

    constructor (options: RouterOptions = {
          
          }) {
          
          
     this.app = null
     this.apps = []
     this.options = options
     this.beforeHooks = []
     this.resolveHooks = []
     this.afterHooks = []
     this.matcher = createMatcher(options.routes || [], this)
    
     let mode = options.mode || 'hash'
     this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
     if (this.fallback) {
          
          
     	mode = 'hash'
     }
     if (!inBrowser) {
          
          
     	mode = 'abstract'
     }
     this.mode = mode
    
     switch (mode) {
          
          
     	case 'history':
     		this.history = new HTML5History(this, options.base)
     		break
     	case 'hash':
     		this.history = new HashHistory(this, options.base, this.fallback)
     		break
     	case 'abstract': //非浏览器环境下
     		this.history = new AbstractHistory(this, options.base) 
     		break
     	default:
     		if (process.env.NODE_ENV !== 'production') {
          
          
      			assert(false, `invalid mode: ${
            
            mode}`)
     		}
     	}
     }
    
  • The first is to get the mode value passed by the constructor. If the mode is history and the browser does not support this mode, the mode is forced to hash; if history is supported, the mode is selected according to the mode.

  • After obtaining the mode, the route is initialized. Take a look at the init method:

    init (app: any /* Vue component instance */) {
          
          
    // ....
     	const history = this.history
    
     	if (history instanceof HTML5History) {
          
          
     		history.transitionTo(history.getCurrentLocation())
    	}else if (history instanceof HashHistory) {
          
          
     		const setupHashListener = () => {
          
          
     			history.setupListeners()
     		}
     		history.transitionTo(
     			history.getCurrentLocation(),
     			setupHashListener,
    	 		setupHashListener
     		)
    	}
    
     	history.listen(route => {
          
          
     		this.apps.forEach((app) => {
          
          
     			app._route = route
     		})
    	})
     }
    // ....
    // VueRouter类暴露的以下方法实际是调用具体history对象的方法
     	push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
          
          
     		this.history.push(location, onComplete, onAbort)
     	}
    
    	replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
          
          
     		this.history.replace(location, onComplete, onAbort)
     	}
    }
    
  • As can be seen from the source code above, the transitionTo function is used in both modes.

  • In Hash mode HashHistory.push():

    push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
          
          
     	this.transitionTo(location, route => {
          
          
     		pushHash(route.fullPath);
     		onComplete && onComplete(route);
     	}, onAbort)
    }
     
    function pushHash (path) {
          
          
     	window.location.hash = path
    }
    
  • HashHistory.pushThe main part of the method is to assign a value to the hash of the location, and the change of the hash will be automatically added to the browser's access history.

  • The view update involves the TransitionTofunction:

    transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
          
          
     	// 调用 match 得到匹配的 route 对象
     	const route = this.router.match(location, this.current)
     	this.confirmTransition(route, () => {
          
          
     		this.updateRoute(route)
     		...
     	})
    }
    updateRoute (route: Route) {
          
          
     	this.cb && this.cb(route)
    }
    listen (cb: Function) {
          
          
     	this.cb = cb
    }
    
  • When the route changes, the this.cb method of History is called, which is History.listenset by the method. Let us go back to the definition of the VueRouter class and find the init method:

    init (app: any /* Vue component instance */) {
          
          
     
     	this.apps.push(app)
    
     	history.listen(route => {
          
          
     		this.apps.forEach((app) => {
          
          
     			app._route = route
     		})
     	});
    }
    
  • The app in the code refers to the Vue instance, app._route is a mixture of global registration Vue.use(Router)through the Vue.mixinmethod when the vue-router plug- in is loaded , and affects each Vue instance after registration. This mixture defines the response in the beforeCreatedhook. Vue.util.defineReactiveStyle _route. When the route changes, it will be automatically called Vue.renderto update the view. vm.renderThe components corresponding to the route are rendered based on the path, name and other attributes of the current _route.

2. The process of routing change to view update

1. this.$router.push(path)
2. HashHistory,push
3. History.transitionTo()
4. const route = this.$router.match(location,this.current) // 进行地址匹配,得到当前地址的route对象
5. History.updateRoute(route)
6. app._route = route
7. vm.render() // 在<router-view></router-view>中渲染
8. window.location.hash = route.fullpath // 浏览器地址栏显示新的地址

Guess you like

Origin blog.csdn.net/yivisir/article/details/109305952