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:
-
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.
- 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-router
provided 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:
- Navigation trigger;
- Call the
beforeRouteLeave
guard on the component that is about to be deactivated ; - Call the global
beforeEach
guard; - Call the
beforeRouteUpdate
guard in the pet component ; - Call the
beforeEnter
guard in the routing configuration ; - Resolve asynchronous routing components;
- Call the
beforeRouteEnter
guard in the next activated component ; - Call the global
beforeResolve
guard; - Navigation is confirmed;
- Call the global
afterEach
hook; - Trigger the update of the DOM;
- Call
beforeRouteEnter
the 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.push
The 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
TransitionTo
function: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.listen
set 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 theVue.mixin
method when the vue-router plug- in is loaded , and affects each Vue instance after registration. This mixture defines the response in thebeforeCreated
hook.Vue.util.defineReactive
Style _route. When the route changes, it will be automatically calledVue.render
to update the view.vm.render
The 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 // 浏览器地址栏显示新的地址