vue-router
安装:
vue add router
配置
// router.js import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' Vue.useVueRouter) // 引入Router插件 const routes = [ { path: '/', name: 'home', component: ) => import'../components/form') }, { path: '/test', name: 'test', // 懒加载 component: ) => import'../components/formTest') } ] const router = new VueRouter{ mode: 'history', // 模式:hash | history | abstract base: process.env.BASE_URL, // http://localhost:8080/cart routes }) export default router
指定路由器
// main.js new Vue{ router, render: h => hApp) }).$mount'#app')
路由视图
<router-view/>
导航链接
<router-link to="/">Home</router-link> <router-link to="/about">About</router-link>
路由嵌套
应用界面通常由多层嵌套的组件组合而成。同样的,URL中各段动态路径也按某种结构对应嵌套的各层组件。
配置嵌套路由,router.js
{ path: "/", component: Home, children: [{ path: "/list", name: "list", component: List }] }
动态路由
我们经常需要把某种模式匹配到的所有路由,全部映射到同一个组件
详情页路由配置,router.js
{ path: '/', name: 'home', component: ) => import'../components/form'), children: [ { path: '/detail/:id', component: Detail } ] }
获取动态路由的参数
<template> <div> <h2>商品详情</h2> <p>{{$route.params.id}}</p> </div> </template>
另一种传参方式:传递路由组件参数 props
{ path: "detail/:id", component: Detail, props: true }
组件中以属性方式获取:
export default { props: ['id'] }
路由守卫
路由导航过程中有若干生命周期钩子,可以在这里实现逻辑控制
全局守卫,router.js
// 路由配置 { path: "/about", name: "about", meta: { auth: true }, // 需要认证 component: ) => import'../components/About.vue') } // 全局守卫 router.beforeEachto, from, next) => { // 要访问 /about 且未登录需要去登录 ifto.meta.auth && !window.isLogin){ ifwindow.confirm"请登录")){ window.isLogin = true next) // 登录成功,继续 } else { next'/') // 放弃登录,回首页 } } else { next) // 不需登录,继续 } })
路由独享守卫
{ path: '/test', name: 'test', component: ) => import'../components/formTest'), beforeEnter: to, from, next) => { // 路由内部知道自己需要认证 if!window.isLogin){ // ... }else{ next) } } }
组件内的守卫
export default { beforeRouteEnterto, from, next) { }, beforeRouteUpdateto, from, next) { }, beforeRouteLeaveto, from, next) { } }
vue-router 拓展
动态路由
利用 $router.addRoutes) 可以实现动态路由添加,常用于用户权限控制。
// router.js --伪码 // 返回数据可能是这样的 // [{ // path: '/', // name: 'home', // component: 'Home' // }] // 异步获取路由 api.getRoutes).thenroutes => { const routeConfig = routes.maproute => mapComponentroute)) router.addRoutesrouteConfig) }) // 映射关系 const compMap = { 'Home': ) => import"./view/Home.vue") } // 递归替换 function mapComponentroute) { route.component = compMap[route.component] ifroute.children) { route.children = route.children.mapchild => mapComponentchild)) } return route }
面包屑
利用 $route.matched 可得到路由匹配数组,按顺序解析可得路由层次关系
// Breadcrumb.vue watch: { $route: { handlerroute) { // [{name:'home',path:'/'},{name:'list',path:'/list'}] console.logthis.$route.matched); // ['home','list'] this.crumbData = this.$route.matched.mapm => m.name || m.redirect); }, immediate: true // 这⼀一⾏行行要加上,让它⼀一开始执⾏行行⼀一次 } }
vue-router 源码实现
通常用法
// krouter.js import Home from "./views/Home"; import About from "./views/About"; import VueRouter from "./kvue-router"; Vue.useVueRouter); export default new VueRouter{ routes: [ { path: "/", component: Home }, { path: "/about", component: About } ] });
// main.js import router from './krouter'
分析一下需要完成的任务:
1、要能解析 routes 配置,变成一个key为path,value为component的map
2、要能监控url变化事件,把最新的hash值保存到current路由
3、要定义两个全局组件:router-view用于显示匹配组件的内容,router-link用于修改hash
4、current应该是响应式的,这样可以触发 router-view 的重写渲染
具体实现:
创建 krouter.js — 测试代码,相当于 平常使用的 router.js
// krouter.js import Vue from 'vue' import Home from "./views/Home"; import About from "./views/About"; import VueRouter from "./kvue-router"; Vue.useVueRouter); export default new VueRouter{ routes: [ { path: "/", component: Home }, { path: "/about", component: About } ] }); // main.js import router from './krouter'
kvue-router.js,相当于 import VueRouter from ‘vue-router’ 中的 vue-router插件
let Vue class VueRouter { constructoroptions) { this.$options = options // 创建一个路由path和route映射 this.routeMap = {} // 将来当前路径current需要响应式 // 利用Vue响应式原理可以做到这一点 this.app = new Vue{ data: { current: '/' } }) } init) { // 绑定浏览器事件 this.bindEvents) // 解析路由配置 this.createRouteMapthis.$options) // 创建router-link和router-view this.initComponent) } bindEvents) { // 修正this执行,因为我们想在这里拿 router的实例 window.addEventListener'hashchange', this.onHashChange.bindthis)) window.addEventListener'load', this.onHashChange.bindthis)) } onHashChange){ // localhost/#/hash slice1)可以拿出#后面的部分 this.app.current = window.location.hash.slice1) || '/' } createRouteMapoptions){ options.routes.forEachitem => { // ['/home']: {path: '/home', component: Home} this.routeMap[item.path] = item }) } initComponent){ // 声明两个全局组件 Vue.component'router-link', { props: { to: String }, renderh) { // 目标是:<a :href="to">xxx</a> return h'a', {attrs: {href: '#' + this.to}}, this.$slots.default) // jsx的写法 // return <a href={this.to}>{this.$slots.default}</a> } }) Vue.component'router-view', { // 箭头函数能保留this指向,这里指向VueRouter render: h => { const Comp = this.routeMap[this.app.current].component return hComp) } }) } } // 把VueRouter变为插件,Vue插件只需要实现一个install方法就行了 VueRouter.install = function_Vue) { Vue = _Vue // 这里保存,上面使用 // 混入任务 Vue.mixin{ // 将来这个Vue被new的时候,会执行这里的代码 beforeCreate) { // 这里的代码将来会在外面初始化的时候被调用 // 这样外面就实现了Vue扩展 // 这里this就是Vue组件实例 // 但是这里只希望根组件执行一次 ifthis.$options.router){ Vue.prototype.$router = this.$options.router // 对路由器进行初始化 this.$options.router.init) } } }) } export default VueRouter
测试注意:
不要出现 view-router 嵌套,因为这里的代码没做嵌套处理