🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] >[success] # 路由详解(二)----进阶篇 <br> >[success] ## 路由组件传参(.vue文件路由跳页传参) 我们如果在一个页面中 **需要根据路由获取参数, 在页面中用参数做一些逻辑处理**,首先可以使用 **路由** 的实例 **$route** 来 **获取路由的参数** , 但是这样有一个 **缺点** ,我们的 **页面** 和 **路由** 进行了 **高度耦合** ,为了 **解耦** ,使我们的组件能在最大程度上 **复用** , 我们就会用到 **路由组件传参** , **路由组件传参** 有 **3种** 模式: >[success] ### 布尔模式 **布尔模式**: 适用于在 **路由对象** 中有 **路由参数(例如:path: '/about/:name')** 的 **路由配置**。 **场景** : 我有一个 **demo.vue页面** 中使用 **$route.params.name** 在页面中进行 **逻辑判断** ,这样 **耦合性就很高** ,只能用 **$route.params.name** 方式判断,其他页面想使用这个 **demo.vue页面** 也只能通过 **路由传参** 的方式使用该页面,如果使用了 **布尔模式** , **demo.vue页面** 中就可以在 **props** 中设置 **name** 属性,页面中可以直接使用 **name** 属性,而不是这样使用 **$route.params.name** ,这样其他页面使用 **demo.vue页面** 作为 **组件** 使用可以直接写 : ~~~ <demo :name="'小明'" /> ~~~ 这样的话 **demo.vue** 的 **name参数** 即可以用 **路由params方式传参** ,也可以用 **父传子的props方式传参**。 2. **布尔模式使用方法有2种如下**: >[success] #### 字符串模板拼接方式 1. 首先在 **router.js文件** 对应页面的 **路由对象** 中设置 **props属性** 置为 **true** , **$route.params 将会被设置为组件属性**,所以 **query的传参方式是不好用的** , **路由对象的path属性** 一定要是 **属性拼接** ( **path:'/about/:name/:age'** )或者 **params** 的写法。 **router.js** ~~~ import Home from '@/views/Home' export default [ { path: '/', name: 'home', component: Home }, { path: '/about/:name/:age', // 1. 路由参数 name: 'about', component: () => import('@/views/About.vue'), props: true // 2. 开启布尔模式 } ~~~ 2. 在 **Home.vue** 页面通过 **点击事件** 跳转 **About.vue** 页面,给 **path属性** 用 **字符串模板** 进行 **参数拼接** 的方式进行 **传参** 。 **Home.vue** ~~~ <template> <div> <button @click="handleClick">点击我跳转About页面</button> </div> </template> <script> export default { data(){ return{ name: '小明', age: '18' } }, methods: { handleClick(){ this.$router.push({ path: `/about/${ this.name }/${ this.age }`, // 3. 向跳转的页面传参 }) } } } </script> ~~~ 3. 在 **使用参数的页面(About.vue)中** 用 **props设置属性** ,可以给 **属性** 设置 **类型(支持多种类型,数组形式即可)、默认值** ,这种方式可以直接在使 **template标签** 中使用 **name** 属性,不用像之前用 **$route** 的形式使用 **参数** ,具体写法如下: **About.vue** ~~~ <template> <div> 我的名字叫{{ name }},今年{{ age }}岁 </div> </template> <script> export default{ props: { // 4. 设置参数在页面中使用 name: { // 参数名称 type: String, // 类型,支持数组形式例如:type: [String, Number] default: '我是默认值的name' // 默认值 }, age: { // 参数名称 type: String, // 类型,支持数组形式例如:type: [String, Number] default: '10' // 默认值 }, } } </script> ~~~ >[success] #### params方式 1. **params** 跟 **字符串模板拼接方式** 的不同点就是 **Home.vue** 页面的传参写法不一样。 **Home.vue** ~~~ handleClick(){ this.$router.push({ // params方式传参 name: 'about', params: { // 向跳转的页面传参 name: this.name, age: this.age } }) ~~~ >[success] ### 对象模式 **对象模式**:如果 **router.js** 中的 **路由对象** 的 **props** 是一个 **对象** , **props对象** 中的 **属性** 为 **组件属性** 。**当props是静态的时候有用**。 **场景**: 也就是说使用 **对象模式** 时,该页面会**有一个静态写死的props属性(跟props的default属性有一点像,但它不支持修改)**,而且不支持 **动态修改**,如果想修改,只能 **手动** 去 **router.js** 中对应的 **路由对象** 中 **修改props对象中的属性**。 >[success] #### 对象模式使用方法 1. 在 **router.js** 中设置 **props属性** 为 **对象形式**, 它的 **key** 作为 **组件属性** , 如果 **props对象** 中 **不写属性** , 会显示对应页面中 **props对象** 中 **default** 属性的默认值。 **router.js** ~~~ import Home from '@/views/Home' export default [ { path: '/', name: 'home', component: Home }, { path: '/about', name: 'about', component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'), props: { name: '小黑' // 这里不写name属性,就会显示props中设置的默认值小明 } } ] ~~~ 2. **从 Home.vue 页面通过点击事件跳转 About.vue 中**,强调一下,用 **对象模式传参是无效的**。 **Home.vue** ~~~ <template> <div> <button @click="handleClick">点击我跳转About页面</button> </div> </template> <script> export default { methods: { handleClick(){ this.$router.push({ // 方案1 name: 'about' }) // this.$router.push({ // 方案2 // path: '/about' // })} } } </script> ~~~ 3. **About.vue** 页面也正常写 **props对象属性默认值、类型** 即可。 **About.vue** ~~~ <template> <div> {{ name }} </div> </template> <script> export default{ props: { name: { // 参数名称 type: String, // 类型,支持数组形式例如:type: [String, Number] default: '小明' // 默认值 } } } </script> ~~~ >[success] ### 函数模式 **函数模式** : **根据当前的路由传入的参数,来做一些处理逻辑** ,从而 **设置我们传入组件的属性值**。这么说不太容易懂,简单的说就是 **可以根据传入的 params 或者 query 对象中的参数,在函数中进行判断逻辑,最终给页面传出路由参数**。 **场景** : 有 **a.vue** 、 **b.vue** 、 **c.vue 3** 个页面,**a** 和 **b页面** 都要跳转到 **c页面** 并且用 **params** (或者 **query** )传入 **name** 、 **alias属性** ,**c页面** 的 **路由对象** 的 **props函数** 中需要判断 **name** 是否等于 **孙悟空** ,如果等于 **孙悟空** ,就把 **alias** 属性值 **弼马温** 赋的给 **name**,这样就做到了 **在路由层面直接处理要展示的值**,代码如下: <br/> 1. 在 **router.js** 中给 **c.vue** 设置 **props属性** 为 **函数** ,它有一个 **回调参数route对象**, **route对象** 中可以取到 **当前路由对象信息,包括:params、query等**,并且通过 **route对象** 取到 **route.query.name** 来进行判断是否等于 **孙悟空**, 如果等于 **孙悟空**,就用它的 **alias(别名)** **弼马温** 作为 **name** 展示。 **router.js** ~~~ export default [ { path: '/', name: 'a', component: () => import('@/views/a.vue') }, { path: '/b', name: 'b', component: () => import('@/views/b.vue') }, { path: '/c', name: 'c', component: () => import('@/views/c.vue'), // route代表当前路由对象(里面能取到params、query对象中的参数信息), return一个对象, // 对象中的name属性对应c页面中props的name属性,这里的name: route.query.name完全取决于 // 传值的页面使用哪种方式(query、params)传值 props: route => ({ name: route.query.name === '孙悟空' ? route.query.alias : route.query.name }) } ] ~~~ **a.vue** ~~~ <template> <div> <p>a页面</p> <button @click="handleClick">跳转c页面</button> </div> </template> <script> export default { methods: { handleClick(){ this.$router.push({ // query方式传参 path: '/c', query: { name: '孙悟空', alias: '弼马温' } }) } } } </script> ~~~ **b.vue** ~~~ <template> <div> <p>b页面</p> <button @click="handleClick">跳转c页面</button> </div> </template> <script> export default { methods: { handleClick(){ this.$router.push({ // query方式传参 path: '/c', query: { name: '猪八戒', alias: '猪刚鬣' } }) } } } </script> ~~~ **c.vue** ~~~ <template> <div> 我的名字叫{{ name }} </div> </template> <script> export default{ props: { name: { // 参数名称 type: String, // 类型,支持数组形式例如:type: [String, Number] default: '墨迹嘴唐僧' // 默认值 } } } </script> ~~~ >[success] ## HTML5 History模式 参考文章:[Vue番外篇 -- vue-router浅析原理](https://juejin.im/post/5bc6eb875188255c9c755df2#heading-2) **vue-router** 默认 **hash模式** ,在 **URL** 中会有一个 **# 号** ,一般场景下,**hash** 和 **history** 都可以,除非你更在意颜值, **#号** 夹杂在 **URL** 里看起来确实有些不太美丽。 <br> 1. **hash** —— 即地址栏 **URL** 中的 **#号** (此 hash 不是密码学里的散列运算)。 比如这个 **URL**: **http://www.abc.com/#/hello**, **hash** 的值为 **#/hello** 。它的特点在于: **hash** 虽然出现在 **URL** 中,但不会被包括在 **HTTP请求** 中,对后端完全没有影响,因此 **改变 hash 不会重新加载页面**。 2. **history** —— 利用了 **HTML5 History Interface** 中新增的 **pushState()** 和 **replaceState()** 方法。(需要特定浏览器支持) 这两个方法应用于浏览器的历史记录栈,在当前已有的 **back** 、**forward**、**go** 的基础之上,它们提供了对 **历史记录进行修改*** 的功能。只是当它们执行修改时,虽然改变了当前的 **URL**,但**浏览器不会立即向后端发送请求**。 因此可以说,**hash模式** 和 **history模式** 都属于 **浏览器自身的特性**,**vue-router** 只是利用了这 **两个特性**(通过调用浏览器提供的接口)来实现 **前端路由**。 3. **abstract模式** —— **abstract模式** 是使用一个 **不依赖于浏览器的浏览历史虚拟管理后端**。 根据平台差异可以看出,在 **Weex 环境** 中只支持使用 **abstract模式** 。 不过,**vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式**,所以 在 **使用 vue-router 时只要不写 mode 配置即可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式**。 (当然,你也可以明确指定在所有情况下都使用 **abstract 模式** ) >[success] ### History模式配置 1. 默认 **router.js** 中的 **mode(模式)** 属性是 **hash**,在 **RRL** 中会有 **# 符号** ,例如: **[http://localhost:8080/#/](http://localhost:8080/#/)** ~~~ import Vue from 'vue' import Router from 'vue-router' import routes from './router' Vue.use(Router) export default new Router({ mode: 'hash', // 默认哈希模式 routes }) ~~~ 2. 但是在 **正式环境** 时候,我们不希望看到这个 **# 符号**,把 **mode** 修改成 **history模式**,它是使用的 **history**的一些 **api** 来做页面的 **无刷新跳转** ~~~ export default new Router({ mode: 'history', // history模式 routes }) ~~~ 3. 使用 **history模式** ,所有 **匹配不到静态资源的页面** 的 **URL** 都会指向 **index.html** ,这样会有一个问题 **当你匹配不到静态资源,前端路由也匹配不到组件的话**,这时候就会有问题了,所以需要在 **router.js路由列表** 里 **最后(为什么是最后,因为路由列表是有优先原则的,顺序的从上到下的读取顺序,如果两个组件name都叫同样的名字,它会使用上面的组件)** 添加一项 **404页面** 的 **路由对象**。 **router.js** ~~~ { // 一定要写在路由列表的最后面 path: '*', // * 符号,代表匹配任何的路径 component: () => import('@/views/error_404.vue') } ~~~ 4. 当然 **history模式** 也是需要 **后端配合** 后期视频讲到部署时候这里补写上 >[success] ## 导航守卫 **导航守卫** :在 **路由发生跳转** 到 **导航结束** 这段时间能做一些相应的 **逻辑处理** 。 | 导航守卫名称 | 导航守卫方法 | 参数 | 应用场景 | | --- | --- | --- | --- | | 全局前置守卫 | router.beforeEach | **参数** 是一个 **回调函数** ,router.beforeEach((to, from, next) => { })<br> 1. **to** :代表 **要跳转的页面** <br> 2. **from** :代表 **从哪个页面跳转来** <br> 3. **next** : **next** 是一个函数,执行 **next()** 代表 **继续跳转要去的页面** | **判断是否登录** ,如果 **未登录跳转到登录页面** , **已登录的话就跳转想去的页面** | | 全局解析守卫 | router.beforeResolve | **参数** 跟 **router.beforeEach** 相同 | 后期补上。暂时不知道应用场景 | | 全局后置钩子 | router.afterEach | **参数** 里没有 **next** 其他 **参数** 都跟 **router.beforeEach** 相同 | **跳转页面完毕** 之后 **关闭loading** | | 路由独享守卫 | beforeEnter | **参数** 跟 **router.beforeEach** 相同 | 能对 **单独的路由页面进行逻辑处理** | | 组件内的守卫 | 1. beforeRouteEnter | **参数** 跟 **router.beforeEach** 相同 | 1. **进入页面时触发** <br>2. 不能 **获取组件实例this** ,如果想使用 **this** ,用 **next(vm => {})** 方式。 | | | 2. beforeRouteLeave | **参数** 跟 **router.beforeEach** 相同 | 1. **离开页面时触发** <br>2. 可以 **获取组件实例this**| | | 3. beforeRouteUpdate | **参数** 跟 **router.beforeEach** 相同 | 1. **路由发生变化,组件被复用时触发** <br>2. 可以 **获取组件实例this** | >[success] ### 全局前置守卫 **场景** :比如在 **跳转一个页面时要判断这个用户有没有登录过,如果没有登录过要跳转到登录页面,如果登录过就跳转要跳转的页面** ,以及做一些 **路由权限** 的 **判断处理** 。 1. 你可以使用 **router.beforeEach** 注册一个 **全局前置守卫** ,在里面写 **判断跳转页面是否登录的逻辑**,代码如下: **router / index.js** ~~~ import Vue from 'vue' import Router from 'vue-router' import routes from './router' Vue.use(Router) const router = new Router({ routes }) // 这个登录状态是由接口来返回的,这里只是模拟一下登录跳转逻辑 const HAS_LOGINED = false // 注册全局前置守卫 router.beforeEach((to, from, next) => { if(to.name !== 'login'){ // 访问的页面不是 【登录页面】 // 【已登录】就继续跳转要去的页面 if(HAS_LOGINED) next() // 【未登录】跳转到login页面。next方法里面传入的参数跟$router.push同样 else next({ name: 'login' }) } else { // 访问的页面是 【登录页面】 // 【已登录】就跳转到首页 if(HAS_LOGINED) next({ name: 'home' }) // 【未登录】跳转到登录页 else next() } }) export default router ~~~ **router / router.js** ~~~ import Home from '@/views/Home' export default [ { path: '/', name: 'home', alias: '/home_page', component: Home, }, { path: '/login', name: 'login', component: () => import('@/views/login.vue'), }, { path: '*', // * 符号,代表匹配任何的路径 component: () => import('@/views/error_404.vue') } ] ~~~ **login.vue** ~~~ <template> <div>登录页面</div> </template> ~~~ >[success] ### 全局解析守卫 **全局解析守卫** :在 **导航被确认之前** ,**异步路由组件被解析之后**, **解析守卫** 就被调用。 1. 问:什么叫 **导航被确认之前?** 答: **所有导航钩子(导航守卫)都结束,导航就是被确定了** 。 2. 问: 什么叫 **路由组件被解析之后** ? 答:页面渲染后。 **用法**: ~~~ import Vue from 'vue' import Router from 'vue-router' import routes from './router' import { Loading } from 'element-ui'; // 注册路由 Vue.use(Router) // vue-router实例 const router = new Router({ routes }) // 全局解析守卫 router.beforeResolve((to, from, next) => { // 逻辑处理 next() }) export default router ~~~ >[success] ### 全局后置钩子 **场景** :我们在跳转页面时候要在 **全局前置守卫(router.beforeEach)** 中执行 **打开 Loading 加载提示** ,跳转完毕后需要在 **全局后置钩子(router.afterEach)** 中 **关闭 Loading 加载提示** 。 **router / index.js** ~~~ import Vue from 'vue' import Router from 'vue-router' import routes from './router' import { Loading } from 'element-ui'; // 注册路由 Vue.use(Router) // vue-router实例 const router = new Router({ routes }) // 登录状态 const HAS_LOGINED = true // loading实例 let loadingInstance = null // 注册全局前置守卫 router.beforeEach((to, from, next) => { // 打开loading loadingInstance = Loading.service({ lock: true, // 锁定屏幕滚动 text: '拼命加载中... >_<', // 提示文字 spinner: 'el-icon-loading', // 自定义加载图标类名 background: 'rgba(0, 0, 0, 0.7)' // loading背景颜色 }) // 是否登录页面跳转逻辑 if(to.name !== 'login'){ if(HAS_LOGINED) next() else next({ name: 'login' }) } else { if(HAS_LOGINED) next({ name: 'home' }) else next() } }) // 注册全局后置钩子 router.afterEach((to, from) => { // 关闭loading loadingInstance && loadingInstance.close() }) export default router ~~~ >[success] ### 路由独享守卫 **路由独享守卫** : 给 **指定的路由添加路由守卫** ,**单独做逻辑处理**。 **用法**: 1. 在想要 **进行逻辑处理页面** 的 **路由对象** 中写上 **beforeEnter属性** ,属性的参数跟 **router.beforeEach** 相同。 ~~~ import Home from '@/views/Home' export default [ { path: '/', name: 'home', alias: '/home_page', component: Home }, { path: '/about', name: 'about', component: () => import('@/views/About.vue'), beforeEnter: (to, from, next) => { // 在里面进行逻辑处理 if(from.nmae === 'home') alert('这是从home页来的') else alert('这不是从home页来的') next() } } ] ~~~ >[success] ### 组件内的守卫 每个 **组件** 都可以有 **3个钩子** 包括: **beforeRouteEnter** 、 **beforeRouteLeave** 、 **beforeRouteUpdate** **组件内的守卫** : 在 **组件进入(beforeRouteEnter)** 、 **组件离开(beforeRouteLeave )** 、 **组件改变(beforeRouteUpdate)** 页面时触发。 >[success] #### beforeRouteEnter **beforeRouteEnter** : 渲染 **组件** 的 **对应路由** ,被确认前调用(跳转 **对应页面** 时 **beforeRouteEnter** 才会 **被调用** ) **用法** : 1. **Home.vue** ~~~ <template> <div> 我是Home.vue页面 </div> </template> <script> export default { beforeRouteEnter(to, from, next){ // 参数跟beforeEach方法一样 next() } } </script> ~~~ 2. **需要注意**: **beforeRouteEnter** 是在 **路由触发要进页面的时候调用** ,此时 **页面还没有渲染** ,所以此时在 **beforeRouteEnter** 中 **访问组件实例 this** 是无效的,如下: ~~~ <script> export default { data(){ return{ name: '星哥' } }, beforeRouteEnter(to, from, next){ // 参数跟beforeEach方法一样 console.log(this.name) // Cannot read property 'name' of undefined 此时是取不到this实例的 next() } } </script> ~~~ 如果想 **访问组件实例 this** 的话可以这样写: ~~~ <script> export default { data(){ return{ name: '星哥' } }, beforeRouteEnter(to, from, next){ // 参数跟beforeEach方法一样 next(vm => { // vm是组件的实例 console.log(vm,'vm') }) } } </script> ~~~ >[success] #### beforeRouteLeave **beforeRouteLeave** :在页面要离开时触发 **beforeRouteLeave** 。 **补充一句 beforeRouteLeave** 可以 **访问组件实例this** 。 用法: 1. 例如现在有个 **编辑页面**,还没 **编辑** 完,就要 **跳转其他页面** ,此时要弹出一个 **提示信息:您确定要离开吗?** ,代码如下: **Home.vue** ~~~ <template> <div> 我是Home.vue页面 </div> </template> <script> export default { beforeRouteLeave(to, from, next) { // 参数跟beforeEach方法一样 const leave = confirm('您确定要离开吗?') if(leave) next() // 确定离开 else next(false) // 取消离开 }, } </script> ~~~ >[success] #### beforeRouteUpdate **beforeRouteUpdate** : **路由发生变化,组件被复用时触发 beforeRouteUpdate** 。**补充一句 beforeRouteUpdate** 可以 **访问组件实例this** 。 **用法**: 1. 在 **router.js** 中配置好 **路由对象** 信息。 **router.js** ~~~ import Home from '@/views/Home' export default [ { path: '/', name: 'a', component: () => import('@/views/a.vue') }, { path: '/c/:name', name: 'c', component: () => import('@/views/c.vue'), props: true // 开启布尔模式 } ] ~~~ **a.vue** ~~~ <template> <div> <p>我是a页面</p> <button @click="handleClick">点击我跳转c页面</button> </div> </template> <script> export default { methods: { handleClick() { this.$router.push({ // params方式传参 name: 'c', params: { name: '小a' } }) } } } </script> ~~~ 重点是下面这个 **c.vue** , 在 **c.vue** 中使用了 **c.vue** ,也就是应了上面那句话: **路由发生变化,组件被复用时触发 beforeRouteUpdate** ,下面的 **c.vue** 中路由参数 **name发生变化** ,同时 **组件也被复用** 。 **c.vue** ~~~ <template> <div> <p>{{ name }}</p> <p>我是c页面</p> <button @click="handleClick">点我还跳转c页面</button> </div> </template> <script> export default{ props: { name: { // 参数名称 type: String, // 类型,支持数组形式例如:type: [String, Number] default: '小c' // 默认值 } }, methods: { handleClick() { this.$router.push({ // params方式传参 name: 'c', params: { name: '小c2', } }) } }, beforeRouteUpdate(to, from, next){ console.log(to.name, from.name) // c c next() } } </script> ~~~ >[success] ### 完整的导航解析流程(vue-router钩子生命周期执行顺序) 1. 导航被触发(**this.$router.push** 或者 **修改URL** 触发) 2. 在失活的组件(**即将离开的页面组件**)里调用 **beforeRouteLeave** 守卫 3. 调用全局的前置守卫 **beforeEach** 4. 在重用的组件里调用 **beforeRouteUpdate** 5. 调用路由独享的守卫 **beforeEnter** 6. 解析异步路由组件 7. 在被激活的组件里(**即将计入的页面组件**)里调用 **beforeRouteEnter** 8. 调用全局的解析守卫 9. 导航被确认 10. 调用全局的后置守卫 **afterEach** 11. 触发 **DOM** 更新 12. 用创建好的实例调用 **beforeRouteEnter** 守卫中传给 **next** 的**回调函数(next (vm => {}))**。 >[success] ## 路由元信息 **路由元信息**:每个 **路由对象** 都有一个 **meta** 属性,里面可以 **配置一些权限** 。 **场景**:例如 **title** , 默认的 **title** 是在 **index.html** 文件中写死的,如果想每个页面有不同的 **title** , **home** 的 **title** 是 【首页】, **about** 的 **title** 是 【关于】,就要在对应的 **路由对象** 中配置 **meta** 配置。 **用法** : 1. 在 **路由列表** 中的 **路由对象** 上设置 **title** 信息 **router/router.js** ~~~ import Home from '@/views/Home' export default [ { path: '/', name: 'home', alias: '/home_page', component: Home, meta: { // 配置title信息 title: '首页' } }, { path: '/about', name: 'about', component: () => import('@/views/About.vue') }, { path: '*', // * 符号,代表匹配任何的路径 component: () => import('@/views/error_404.vue') } ] ~~~ 2. 在 **全局前置守卫(router.beforeEach)** 中通过 **setTitle方法** 动态设置 **title** **router/index.js** ~~~ import Vue from 'vue' import Router from 'vue-router' import routes from './router' import { Loading } from 'element-ui'; import { setTitle } from '@/lib/util' // 注册路由 Vue.use(Router) // vue-router实例 const router = new Router({ routes }) // 登录状态 const HAS_LOGINED = true // loading实例 let loadingInstance = null // 注册全局前置守卫 router.beforeEach((to, from, next) => { // 动态设置title to.meta && setTitle(to.meta.title) // 打开loading loadingInstance = Loading.service({ lock: true, // 锁定屏幕滚动 text: '拼命加载中... >_<', // 提示文字 spinner: 'el-icon-loading', // 自定义加载图标类名 background: 'rgba(0, 0, 0, 0.7)' // loading背景颜色 }) // 是否登录页面跳转逻辑 if(to.name !== 'login'){ if(HAS_LOGINED) next() else next({ name: 'login' }) } else { if(HAS_LOGINED) next({ name: 'home' }) else next() } }) // 注册全局后置钩子 router.afterEach((to, from) => { // 关闭loading loadingInstance && loadingInstance.close() }) export default router ~~~ 3. 引入 **setTitle方法**, 如果 **路由对象** 中没有设置 **title属性**的,会自动设置为 **admin** 。 **lib/util.js** ~~~ export const setTitle = (title) => { window.document.title = title || 'admin' // 默认title } ~~~ >[success] ## 过渡动效 **过渡动效**: 页面的 **切换** 就是 **一个组件注销,一个组件加载** ,过渡动效就是 **给页面跳转时添加过渡效果** 。 用法: 1. 在 **app.vue** 页面中用 **transition-group** 标签包裹 **多个视图组件(router-view )** ,如果只有一个 **视图组件** , 用 **transition** 标签包裹,需要给视图组件设置 **key** ,**transition-group**标签设置 **name** ,这里的 **name** 对应下面的 **style** 标签 **class** 类名前缀。 ~~~ <template> <div id="app"> <!-- 多个视图组件(router-view)用transition-group标签包裹,如果只有一个视图组件用transition标签包裹即可。 --> <transition-group name="router"> <router-view key="default"/> <router-view key="email" name="email"/> <router-view key="tel" name="tel"/> </transition-group> </div> </template> <style lang="scss"> // 页面进入的效果 .router-enter{ // 初始化加载状态 opacity: 0; } .router-enter-active{ // 设置组件从无到有的过程 transform: opacity 1s ease; } .router-enter-to{ // 页面完全显示之后 opacity: 1; } // 页面离开的效果 .router-leave{ // 初始化加载状态 opacity: 1; } .router-leave-active{ // 设置组件从无到有的过程 transform: opacity 1s ease; } .router-leave-to{ // 页面完全显示之后 opacity: 0; } </style> ~~~ 2. 或者也可以写成 **动态类名**, **根据路由参数来决定**,如果你想为 **某个页面设置特定的动画效果** ,可以使用这种方式,写法如下: ~~~ <template> <div id="app"> <!-- 多个视图组件(router-view)用transition-group标签包裹,如果只有一个视图组件用transition标签包裹即可。 --> <transition-group :name="routerTransition"> <router-view key="default"/> <router-view key="email" name="email"/> <router-view key="tel" name="tel"/> </transition-group> </div> </template> <script> export default{ data(){ return{ routerTransition: '' // 类名 } }, watch:{ '$route'(to){ // 监听路由参数变化改变过渡效果的类 to.query && to.query.transitionName && (this.routerTransition = to.query.transitionName) } } } </script> <style lang="scss"> // 页面进入的效果 .router-enter{ // 初始化加载状态 opacity: 0; } .router-enter-active{ // 设置组件从无到有的过程 transform: opacity 1s ease; } .router-enter-to{ // 页面完全显示之后 opacity: 1; } // 页面离开的效果 .router-leave{ // 初始化加载状态 opacity: 1; } .router-leave-active{ // 设置组件从无到有的过程 transform: opacity 1s ease; } .router-leave-to{ // 页面完全显示之后 opacity: 0; } </style> ~~~ >[success] ## 数据获取 后期补充 >[success] ## 滚动行为 后期补充 >[success] ## 路由懒加载 后期补充 >[warning] ## 后期补充(该项后期删除) 1. 看vue-router文档 3. **对象模式** 的 **路由对象** 的 **props属性**到底可不可以写成动态的,个人理解: 3.1 布尔模式是【任意个数参数】在页面中简化params属性 3.2 对象模式是【静态的值】写死的 3.3 函数模式是 【根据逻辑来判断 **指定简化的参数** 】改写法比较灵活 4. 路由组件传参的3种模式的应用场景 5. beforeEnter无法跳转指定页面,例如: ![](https://img.kancloud.cn/eb/06/eb0601bc89d4bb979dc95223876407bf_957x881.png) 6. 组件内的守卫到底是不是3个钩子有没有新出的钩子 搜索 【后期】、【补充】,把没写的补充上