[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个钩子有没有新出的钩子
搜索 【后期】、【补充】,把没写的补充上
- vue 26课
- Vue-cli3.0项目搭建
- Vue-ui 创建cli3.0项目
- Vue-ui 界面详解
- 项目目录详解
- public文件夹
- favicon.ico
- index.html
- src文件夹
- api文件夹
- assets文件夹
- components文件夹
- config文件夹
- directive文件夹
- lib文件夹
- mock文件夹
- mock简明文档
- router文件夹
- store文件夹
- views文件夹
- App.vue
- main.js
- .browserslistrc
- .editorconfig
- .eslintrc.js
- .gitignore
- babel.config.js
- package-lock.json
- package.json
- postcss.config.js
- README.en.md
- README.md
- vue.config.js
- Vue Router
- 路由详解(一)----基础篇
- 路由详解(二)----进阶篇
- Vuex
- Bus
- Vuex-基础-state&getter
- Vuex-基础-mutation&action/module
- Vuex-进阶
- Ajax请求
- 解决跨域问题
- 封装axios
- Mock.js模拟Ajax响应
- 组件封装
- 从数字渐变组件谈第三方JS库使用
- 从SplitPane组件谈Vue中如何【操作】DOM
- 渲染函数和JSX快速掌握
- 递归组件的使用
- 登陆/登出以及JWT认证
- 响应式布局
- 可收缩多级菜单的实现
- vue杂项
- vue递归组件
- vue-cli3.0多环境打包配置
- Vue+Canvas实现图片剪切
- vue3系统入门与项目实战
- Vue语法初探
- 初学编写 HelloWorld 和 Counter
- 编写字符串反转和内容隐藏功能
- 编写TodoList功能了解循环与双向绑定
- 组件概念初探,对 TodoList 进行组件代码拆分
- Vue基础语法
- Vue 中应用和组件的基础概念
- 理解 Vue 中的生命周期函数
- 常用模版语法讲解
- 数据,方法,计算属性和侦听器
- 样式绑定语法
- 条件渲染
- 列表循环渲染
- 事件绑定
- 表单中双向绑定指令的使用
- 探索组件的理念
- 组件的定义及复用性,局部组件和全局组件
- 组件间传值及传值校验
- 单向数据流的理解
- Non-Props 属性是什么
- 父子组件间如何通过事件进行通信
- 组件间双向绑定高级内容
- 使用匿名插槽和具名插槽解决组件内容传递问题
- 作用域插槽
- 动态组件和异步组件
- 基础语法知识点查缺补漏
- Vue 中的动画
- 使用 Vue 实现基础的 CSS 过渡与动画效果
- 使用 transition 标签实现单元素组件的过渡和动画效果
- 组件和元素切换动画的实现
- 列表动画
- 状态动画
- Vue 中的高级语法
- Mixin 混入的基础语法
- 开发实现 Vue 中的自定义指令
- Teleport 传送门功能
- 更加底层的 render 函数
- 插件的定义和使用
- 数据校验插件开发实例
- Composition API
- Setup 函数的使用
- ref,reactive 响应式引用的用法和原理
- toRef 以及 context 参数
- 使用 Composition API 开发TodoList
- computed方法生成计算属性
- watch 和 watchEffect 的使用和差异性
- 生命周期函数的新写法
- Provide,Inject,模版 Ref 的用法
- Vue 项目开发配套工具讲解
- VueCLI 的使用和单文件组件
- 使用单文件组件编写 TodoList
- Vue-Router 路由的理解和使用
- VueX 的语法详解
- CompositionAPI 中如何使用 VueX
- 使用 axios 发送ajax 请求
- Vue3.0(正式版) + TS
- 你好 Typescript: 进入类型的世界
- 什么是 Typescript
- 为什么要学习 Typescript
- 安装 Typescript
- 原始数据类型和 Any 类型
- 数组和元组
- Interface- 接口初探
- 函数
- 类型推论 联合类型和 类型断言
- class - 类 初次见面
- 类和接口 - 完美搭档
- 枚举(Enum)
- 泛型(Generics) 第一部分
- 泛型(Generics) 第二部分 - 约束泛型
- 泛型第三部分 - 泛型在类和接口中的使用
- 类型别名,字面量 和 交叉类型
- 声明文件
- 内置类型
- 总结