The routing in the client is actually the display and hiding of DOM elements. When the home content is displayed in the page, all the contents in about are hidden, and vice versa. There are two ways to implement client-side routing: hash based and html5 history api based.
1, Basic concepts
1.1 route
Define each route
const indexRoute = { path: '/index', component: Index } const userRoute = { path: '/user', component: User }
1.2 routes (all routes)
Integrate each defined route into an array
const routes = [ indexRoute, userRoute ]
1.3 router
import Vue from "vue"; import VueRouter from "vue-router"; Vue.use(VueRouter); const routes = [ { path: '/index', component: Index }, { path: '/user', component: User } ] const router = new VueRouter({ routes }) export default router
1.4 configure router (inject Vue instance)
Configure router in Vue instance to use routing
import router from "./router.js" const app = new Vue({ router }).$mount('#app')
1.5 using router
When the user clicks the router link tab, the to attribute will be used to match the path configured in routes to find the matching component
<template> <div id="app"> ...... <router-link to="/index">Index</router-link> <router-link to="/user">User</router-link> ...... </div> </template>
1.6 rendering components
Finally, render the component to where the router view tag is located
<template> <div id="app"> ...... <router-view></router-view> ...... </div> </template>
2, Basic use
2.1 configuration example
// Introduce required functional modules import Router from 'vue-router' // Introduce components to render import Header from '@/components/Header' // Install | register plug-ins to the global Vue.use(Router) // Create route (delivery configuration) export default new Router({ mode: 'history', base: process.env.BASE_URL, // Configure routing (establish the corresponding relationship between components and requests) routes: [ // redirect {path: '/',redirect: '/home'}, { path: '/home', // route // The component variable name pointed to can uniquely identify a route. When the path is nested too deeply, the problem of too long path can be solved name: 'home', components: { header: Header, // Register imported components default: () => import('@/views/home/index.vue') // Route lazy loading }, // Sub routing configuration children: [ {path: '',redirect: 'concern'}, { path: 'concern', name: 'concern', components:{ default: () => import('@/components/home/Concern.vue') } }, /** * Dynamic routing configuration {path:'xx /: dynamic routing variable', component:xx} * When dynamic routing switches back and forth, because they all point to the same component, vue will not destroy and recreate it, but reuse it. Therefore, the life cycle of each component will not be triggered * At this time, if you want to do something when the component switches back and forth, you can only use watch inside the component to monitor the changes of $route */ { path: '/concern-detail/:id', name: 'concern-detail', components: { default: () => import('@/components/ConcernDetail.vue') } } ] } ] })
2.2 rendering example
<!--Render route configuration named default Components of--> <router-view></router-view> <!--// Render the component named header in the routing configuration -- > <router-view name="header"></router-view>
[note] if the child components in children in the routing configuration are to be rendered, they should also be added to the parent component
2.3 jump example
2.3.1 declarative jump
router-link to='xx/Route name?a=1b=2' /** * name It is equivalent to a name, which can uniquely identify a route. When the path is nested too deeply, the problem of too long path can be solved. * When using an object as a route, to should be preceded by a colon to indicate binding */ router-link :to='{path:'',name:'xx',params:{id:1},query:{a:2,b:3},redirect:'...'}'
2.3.2 programmed jump
There is no need to change the tag to router link like declarative jump, and then compile it back to the original tag. Programming jump can directly use the tag of native HTML. Just bind an event on it and use the syntax below in the event to complete the jump
// Add a route (record to History) this.$router.push(xx/Route name?a=1b=2) this.$router.push({name:'xx',params:{id:1},query:{a:2,b:3}}) // Replace a route (no history is recorded) this.$router.replace({name:'...'}) // Fallback / forward this.$router.go(-1|1)|goBack()
2.3.3 receiving parameters and data
- Template: {{$route.params | query | path}}
- Component: this. $route.params 𞓜 query
2.3.4 dynamic replacement route (address bar) parameters
import merge from 'webpack-merge'ï¼› // If the action parameter exists, it will be changed. If not, it will be added this.$router.push({ query:merge(this.$route.query,{'num':'630'}) }) // Replace all parameters: this.$router.push({ query:merge({},{'num':'630'}) })
3, Navigation guard
3.1 what is the navigation guard
Just like its name, the navigation guard provided by Vue router is mainly used to guard navigation by jumping or canceling.
In fact, the navigation guard is some hook functions in the route jump process. Then, the white dot route jump is a large process. This large process is divided into small processes before, during and after the jump. In each process, there is a function that allows you to operate some other things. This is the navigation guard.
3.2 global routing hook
The hook functions directly operated on the routing instance are characterized in that all components configured for routing will trigger. The straight white point is that triggering the routing will trigger these hook functions, as follows. The hook functions include beforeEach, beforeResolve and afterEach according to the execution order (the following hook functions are explained according to the execution order)
const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { // ... })
3.2.1 beforeEach(to,from, next)
Triggered before the route jump, this hook is mainly used for login verification, that is, the route has not been jump informed in advance, so as to avoid it will be too late to notify again after the jump
​
3.2.2 beforeResolve(to,from, next)
This hook is similar to beforeEach. It is also triggered before route jump. The official explanation of the difference between this hook and beforeEach is:
The difference is that before the navigation is confirmed, and after the guard and asynchronous routing components in all components are resolved, the resolution guard is called.
That is, after beforeEach and component beforeRouteEnter, afterEach is called before.
3.2.3 afterEach(to,from)
In contrast to beforeEach, it is triggered after the route jump is completed. It occurs after beforeEach and beforeResolve and before beforeRouteEnter (guard in component, later)
​
3.3 route exclusive hook
The hook function can also be set when configuring a single route. Its position is the position in the following example, that is, such hook functions exist in components such as Foo. At present, it has only one hook function beforeEnter
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ] })
3.3.1 beforeEnter(to,from, next)
It is exactly the same as beforeEach. If both are set, it will be executed immediately after beforeEach
3.4 routing hooks in components
The hook function executed in the component is similar to the life cycle in the component, which is equivalent to the life cycle hook function added for the component configured with routing. The hook function includes beforeRouteEnter, beforeRouteUpdate (2.2 +) and beforeRouteLeave in the following execution order:
<template> ... </template> export default{ data(){ //... }, beforeRouteEnter (to, from, next) { // Before rendering the component, the corresponding route is called before confirm. // No! Get component instance ` this` // Because the component instance has not been created before the guard is executed }, beforeRouteUpdate (to, from, next) { // Called when the current route changes but the component is reused // For example, for a path / foo/:id with dynamic parameters, when jumping between / foo/1 and / foo/2, // Since the same Foo component will be rendered, the component instance will be reused, and the hook will be called in this case. // Can access component instance ` this` }, beforeRouteLeave (to, from, next) { // Called when the navigation leaves the corresponding route of the component // Can access component instance ` this` } } <style> ... </style>
3.4.1 beforeRouteEnter(to,from, next)
Before the route is entered, the hook is invoked before the global guard beforeEach and the exclusive guard beforeEnter. Before the global beforeResolve and global afterEach are invoked, it is important to note that the instance is not accessible to the component inside the guard, that is, this is undefined, that is, it triggers before the beforeCreate life cycle. Call next to access the component instance. When the navigation is confirmed, execute the callback, and take the component instance as the parameter of the callback method. You can request the server to obtain data in this guard. When the data is successfully obtained and the route can be entered, call next and access the component instance through vm in the callback for assignment and other operations, (the function in the next is called after mounted: to ensure complete access to the component instance).
beforeRouteEnter (to, from, next) { // The component instance cannot be accessed here. this === undefined next( vm => { // Accessing component instances through 'vm' }) }
3.4.2 beforeRouteUpdate(to,from, next)
Called when the current route changes and the component is reused, you can access the instance through this.
- Current route change: query change
- The component is reused: for a path / foo/:id with dynamic parameters, when jumping between / foo/1 and / foo/2, the component instance will be reused and the guard will be called
3.4.3 beforeRouteLeave(to,from, next)
Called when the navigation leaves the corresponding route of the component, you can access the component instance this
3.5 navigation guard callback parameters
to: Destination Routing object
from: the routing object to leave
next: it is the most important parameter and plays a connecting role. The following points must be kept in mind:
- If a hook with a next parameter is involved, you must call next() to continue to execute the next hook, otherwise the route jump will stop.
- If you want to interrupt the current navigation, call next(false). If the URL of the browser is changed (either manually by the user or the browser Back button), the URL address will be reset to the address corresponding to the from route. (mainly used for the processing of login authentication failure)
- Of course, next can be used like this. next('/') or next({path: '/'}): jump to a different address. It means that the current navigation is interrupted, and then a new navigation is performed. The parameters that can be passed are consistent with the options in router.push.
- In the beforeRouteEnter hook, the callback function parameter received in next ((vm) = {}) is the instance vm of the current component. This callback function is called after the life cycle mounted, that is, it is the hook that all navigation guards and lifecycle functions perform at last.
- next(error): (v2.4.0 +) if the parameter passed into next is an Error instance, the navigation will be terminated and the Error will be passed to the callback registered by router.onError().
4, Basic implementation of plug-in
/** * VueRouter plug-in unit * The most basic routing function * It is required that there must be an install, which will be called by Vue.use in the future */ let Vue; // Save the Vue constructor, which should be used in the plug-in. It can be used without importing class VueRouter { constructor(options) { this.$options = options; // You need to create the matched attribute of the response // Use the defineReactive provided by Vue for responsiveness // In this way, when the matched changes in the future, the dependent components will be render ed again this.current = window.location.hash.slice(1) || "/"; Vue.util.defineReactive(this, "matched", []); this.match(); // Listen for hash changes window.addEventListener("hashchange", this.onHashChange.bind(this)); window.addEventListener("load", this.onHashChange.bind(this)); } onHashChange() { this.current = window.location.hash.slice(1); this.matched = []; this.match(); } // Task 3 is compatible with nested routing // Recursively traverse the routing table to obtain the matching relationship array match(routes) { routes = routes || this.$options.routes; // Recursive traversal for (const route of routes) { if (route.path === "/" && this.current === "/") { this.matched.push(route); return; } // /about/info if (route.path !== "/" && this.current.indexOf(route.path) !== -1) { this.matched.push(route); if (route.children) { this.match(route.children) } return; } } } } // The argument is the Vue constructor VueRouter.install = function(_Vue) { /** * Save constructor * Purpose: because the plug-in is an independent package, you don't want to package Vue when packaging */ Vue = _Vue; /** * Global blending * Purpose: delay the logic in the router until it has been created and attached to the option */ Vue.mixin({ // Only when the component is instantiated beforeCreate() { // Only the root instance has this option. This refers to the component instance if (this.$options.router) { // Task 1 mounts the $router attribute Vue.prototype.$router = this.$options.router; } } }); // Task 2 registers two components: router link and router view Vue.component("router-link", { props: { to: { type: String, required: true } }, render(h) { // JSX can also be used, but it is not recommended because of poor compatibility (related loader s need to be configured) // return <a href={ `#${this.to}` }>{ this.$slots.default }</a>; return h("a", { attrs: { href: `#${this.to}` } }, this.$slots.default); } }); Vue.component("router-view", { render(h) { // Mark the current router view depth this.$vnode.data.routerView = true; let depth = 0; let parent = this.$parent; while (parent) { const vnodeData = parent.$vnode && parent.$vnode.data; if (vnodeData) { if (vnodeData.routerView) { // This indicates that the current parent is a router view depth++; } } parent = parent.$parent; } // Get the component corresponding to the current route let component = null; const route = this.$router.matched[depth]; if (route) { component = route.component; } return h(component); } }); }; export default VueRouter;