🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 前言 设置vue的router是必备的知识点,通过本篇你将得到官网基本的知识点解析以及相关的理解。部分知识点还会有案例提供。 ## 官网文档 * [官方vue-router2.0的中文文档](https://router.vuejs.org/zh-cn/) ## 基础 ### 页面使用 ~~~ <div id="app"> <h1>Hello App!</h1> <p> <!-- 使用 router-link 组件来导航. 默认会被渲染成一个 `<a>` 标签 --> <!-- 通过传入 `to` 属性指定链接. 如果你想在新页面打开,那么设置target="_blank"即可--> <router-link to="/foo">Go to Foo</router-link> <router-link to="/bar">Go to Bar</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div> ~~~ ### 动态路由 * 访问路径中带查询参数的,影响路由定义 ~~~ const router = new VueRouter({ routes: [ // 动态路径参数 以冒号开头 { path: '/bookDetail/:id', component: bookDetail } ] }) //页面中定义 <router-link :to="'/bookDetail/' +book.id" > {{book.id}}</router-link> //编程式导航 router.push({ name: 'bookDetail', params: { id: 123 }}) ~~~ | 模式 | 匹配路径 | $route.params | | --- | --- | --- | | /user/:username | user/evan | { username: 'evan' } | | /user/:username/post/:post_id | /user/evan/post/123 |{ username: 'evan', post_id: 123 } | * 除了 $route.params 外,$route 对象还提供了其它有用的信息,例如,$route.query(如果 URL 中有查询参数)、$route.hash 等等。 详细参考地址:[route-object](https://router.vuejs.org/zh-cn/api/route-object.html)。 对于$route.query的使用:url中直接拼接就可以,不影响路由定义 ~~~ //页面中 <router-link :to="'infoplan?id='+scope.row.id"> //脚本导航,带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }} //使用 this.$route.query.id //路由定义 path:'infoplan' ~~~ ### 路由参数变化时,组件不会渲染 解决方案:因为vue默认是同一路由时不会重复渲染,所以需要做监听,当参数变化时,重新获取数据。写法如下: ~~~ watch: { '$route' (to, from) { // 对路由变化作出响应... } } ~~~ ### 定义路由 指定的参数有path,name,component,其中path为#/之后的路径,name为路由别称,component为路由指向的组件。针对任何不在路由设定之内的可以使用path:'*' 来匹配,针对需要拦截的情况使用钩子函数。 ~~~ // 每个路由应该映射一个组件。 其中"component" 可以是 //通过 Vue.extend() 创建的组件构造器, 或者,只是一个组件配置对象。 //定义工具函数 方法引入需要的组件(推荐使用),这个是路由的懒加载的写法。 const compLoad = (name) => (resolve) => require([`@/components/${name}`], resolve) const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar } ] ~~~ ### 嵌套路由 实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,最常见的后台系统以及多层递进的窗口都可能是多层路由的。在直接的父路由的情况下,可能没有对应的组件渲染,这时候需要提供一个默认的路由组件。 要在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置: ~~~ const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ { // 当 /user/:id/profile 匹配成功, // UserProfile 会被渲染在 User 的 <router-view> 中 path: 'profile', component: UserProfile }, { // 当 /user/:id/posts 匹配成功 // UserPosts 会被渲染在 User 的 <router-view> 中 path: 'posts', component: UserPosts } ] } ] }) ~~~ * **要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径**。 * ### router 实例,配置使用 ~~~ // router 实例,然后传 `routes` 配置 // 你还可以传别的配置参数, 不过先这么简单着吧。 const router = new VueRouter({ routes // (缩写)相当于 routes: routes }) //配置使用router var app=new Vue({ router }).$mount('#app') ~~~ ### 声明式导航(to绑定链接地址) `<router-link :to="...">` ### 编程式导航 除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。 ~~~ //基本格式 router.push(location) // 字符串,不带任何参数的 router.push('home') // 对象 router.push({ path: 'home' }) // 命名的路由,带简单的参数列表的,会用多个斜杠分开 router.push({ name: 'user', params: { userId: 123 }}) // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }}) ~~~ ### 获取url参数 * 如果你想获取url上的参数,需要保证传入时按照规定的格式,那么你规定路由时以及使用参数参考下面的。多参的时候加&连接就可以了。下面规定的是不带参数名称的形式。 ~~~ //页面书写 <router-link :to="'/bookDetail/' +book.id+'&'+book.type" > {{book.id}}{{book.pubdate}}</router-link> //也可以通过脚本push跳转,也称为编程式导航 this.$router.push({name:'infoplan',params:{id:id}}) //规定路由参数 { path: '/bookDetail/:id&:type', name: 'bookDetail', component: vueLoad('bookDetail') } //使用路由参数,写在created函数中生效,这里是route不是router,切记 created(){ this.id=this.$route.params.id; this.type=this.$route.params.type; } ~~~ ### 特殊路由 404 因为其他错误页面配置与正常页面没有什么差别,只有404页面是特殊的,404页面针对的是当前地址匹配不到任何路由地址,所以其地址匹配是正则形式的,除了这点还要注意404的路由必须写在所有路由最后的位置,否则会覆盖其他路由不能生效。 ~~~ { path: '/*', name: '404', component: _import('notFound') } ~~~ ## 进阶 ### 路由懒加载 也就是进入页面时才加载对应的路由资源,更加方便,打包时也打包出更多的文件。可以采用官方的写法,也可以使用require.ensure写法。需要注意的是:1 组件的写法不用变化 2 目前不支持动态载入组件。 ~~~ let index = ()=>import('@/views/index') let index = resolve=>require(['@/views/index'],resolve) let index = ~~~ 当然 标准的写法还是require.ensure ,参考:[代码分割ensure写法](http://www.css88.com/doc/webpack2/guides/code-splitting-require/) 示例如下: ~~~ require.ensure(dependencies: String[], callback: function(require), chunkName: String) let index = ( )=>require.ensure([],() => r(require('../page/home')), 'home'); ~~~ 如果需要把某些文件打包到一个文件中,可以通过,需要webpack 2.4以上的版本呢支持才可以。 ~~~ const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') ~~~ ### 权限路由 权限路由是指针对路由部分,前端写成全部路由,但是在前端菜单渲染的时候,写成根据接口数据进行匹配的部分,然后进行菜单过滤,最终只得到匹配的部分。这个匹配的关键字可以是路径,name,或者权限点,不包含这个权限点的可以不用显示。(权限点可以设置到meta信息中) 特别备注:有些时候权限是分子集的,路由也分子路由,当我们针对某些父路由,也需要设置默认一个组件,但这样会与我们设置子路由组件时冲突的,那么此时我们是一般设置父路由默认进入一个子路由,然后其默认进入的子路由,地址设置为空。代码如下: ~~~ { path: '/report', name: 'report', meta:{}, component: viewloader('reportlist'), children:[ { path:'', name:'reportlist', meta:{}, component: viewloader('reportList'), }, { path:'', name:'reportDetail', meta:{}, component: viewloader('reportDetail'), } ] }, ~~~ 当然你也可以设置一个默认的面板 ,这个面板可以展示一些公共内容,包含其他子路由的入口。 ~~~ { path: '/report', name: 'report', meta:{}, component: viewloader('reportDefault'), children:[ { path:'list', name:'reportlist', meta:{}, component: viewloader('reportList'), }, { path:'', name:'reportDetail', meta:{}, component: viewloader('reportDetail'), } ] }, ~~~ **深入思考 :** 关于权限的部分,我们肯定希望其是固定的并且是更新的,固定的可以用vuex或者web storage实现,把菜单的数据保存下,保证刷新或者切换时路由信息不会丢失;更新的则是用户在登录或者及时性很强的操作中,需要及时的替换去菜单部分数据,保证最新。 ### 路由优先级 如果一个地址可以匹配多个路由,谁先定义的,谁的优先级就高,这也解释了为什么我们的404匹配路由要写在最后。 ### 高阶路由 vue-router 使用 path-to-regexp 作为路径匹配引擎,所以支持很多高级的匹配模式,例如:可选的动态路径参数、匹配零个或多个、一个或多个,甚至是自定义正则匹配。查看它的 [文档](https://github.com/pillarjs/path-to-regexp#parameters) 学习高阶的路径匹配,还有 这个例子 展示 vue-router 怎么使用这类匹配。 ### 多业务路由 如果你有多层的路由,都维护在一个router文件肯定是不方便的,建议你将他们维护在多个业务的路由模块,具体写法如下: ~~~ //router/index.js import system from './system' import customer from './customer' const routes = [...system,...customer,...defaultRouters] //router/system.js export default [ { path:'', name:'', component:'' meta:{}, children:[], } ] ~~~ ### 路由钩子函数 在路由的钩子函数中,我们处理一些效果,进行一些拦截,下面举例说明: 核心语法 : ~~~ //全局前置钩子 router.beforeEach((to, from, next) => { // ... }) //全局置后钩子 router.afterEach((to, from) => { // ... }) // 2.2之后新增路由更新操作 ~~~ 备注 :如果觉得我就某些页面写钩子太麻烦,你也可以在路由里直接定义好路由部分对应的钩子函数执行对应的操作。 ~~~ const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ] }) ~~~ - 登录拦截 - 非关键页面拦截 - 携带页面位置信息 - 加载动画 - ### 组件内的导航守卫 也可以直接在组件里定义好自己的导航守卫,做一些高级的程序条件鉴定。 ~~~ const Foo = { template: `...`, beforeRouteEnter (to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 }, beforeRouteUpdate (to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, beforeRouteLeave (to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` } } ~~~ ### 完整的导航解析过程 1. 导航被触发。 2. 在失活的组件里调用离开守卫。 3. 调用全局的 beforeEach 守卫。 4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。 5. 在路由配置里调用 beforeEnter。 6. 解析异步路由组件。 7. 在被激活的组件里调用 beforeRouteEnter。 8. 调用全局的 beforeResolve 守卫 (2.5+)。 9. 导航被确认。 10. 调用全局的 afterEach 钩子。 11. 触发 DOM 更新。 12. 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。 ### 同一组件渲染 ### 路由元信息 定义路由的时候可以定义meta字段 ~~~ const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, children: [ { path: 'bar', component: Bar, // a meta field meta: { requiresAuth: true } } ] } ] }) ~~~ 它的访问时这样的:直接路由中匹配到的路由.meta.keyname . - 拓展:既然是这样,可能有什么作用呢? > 1 基于登录的判断,你可以设置一个字段用来决定这个页面是否需要登录访问 > 2 基于刷新的判断,也许你很多组件都用了keep-alive的特性,对于有些页面你不想使用,可以定义一个字段区分 > 3 定义或者使用一些初始化字段,或者页面初始化信息,属于全局比较公用的一些,不想每个页面都去维护 > 4 其他自定义的需求字段都是可以加的,自由发挥,作为一种解决方案可以实现很多你想做的个性需求。 ### 加载动画 ### 加载优化 ### 保持数据 ### 运行模式 ## 路由传参 ## 文档