06vue之路由vue-router
1. 介绍
路由就是一种对应关系(Hash 地址与组件之间的对应关系),在Vue.js路由允许我们通过不同的url访问不同的内容
SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。 此时,不同组件之间的切换需要通过前端路由来实现(在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成)
1.1 前端路由的工作方式
1、 用户点击了页面上的路由链接
2、 导致了 URL 地址栏中的 Hash 值发生了变化
3、 前端路由监听了到 Hash 地址的变化
4、 前端路由把当前 Hash 地址对应的组件渲染都浏览器中
前端路由,指的是 Hash 地址与组件之间的对应关系
可以通过 window.onhashchange 来监听路由的变化
2. vue-router使用步骤
2.1 下载安装vue-router
npm i -S vue-router
2.2 创建路由器对象
主模块: router/index.js
// 路由器对象模块
import Vue from 'vue'
// 1. 引入vue-router
import VueRouter from 'vue-router'
// 2. 引入各个路由组件
import Login from '../pages/Login/Login'
// 将一级的路由组件懒加载:设置路由懒加载
// 当使用了组件懒加载以后,MSite是一个返回路由组件的函数,只有在执行此函数时,才会加载路由组件
const MSite = () => import(/* webpackChunkName:about */ '../pages/MSite/MSite.vue')
// 3. 声明使用VueRouter
Vue.use(VueRouter);
// 4. 配置路由
const routes = [
{
path: '/',
redirect: '/msite',
// redirectc 表示如果找不到页面 / 就自动跳转到页面 /msite
},
{
path: '/msite',
// 路由组件函数,会在第一次请求对应的路由路径时,才会执行,因为使用了路由懒加载
component: MSite,
// 也可以简写为下面的方式,就不用在上面引入的
// component:()=>import(/* webpackChunkName:about */'../pages/MSite/MSite.vue')
name:'msitePage' // 别名:使用的时候可以通过路由别名访问
meta: {
// 设置路由对象的参数
showFooter: true
}
},
{
path: '/shop',
component: Shop,
// 嵌套二级路由
children: [
{
path: '/shop/goods',
component: ShopGoods
},
{
path: '',
redirect: '/shop/goods'
}
]
},
{
path: '/login',
component: Login
},
]
// 创建 router 实例,然后传 `routes` 配置
export default new VueRouter({
mode: 'history',
// 5. 配置所有路由
routes
})
路由重定向 redirect:重新指定路由的指向
通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向
path: '/',
redirect: '/shop/goods'
默认子路由:如果 children 数组中,路由path 为 空字符串,则这条规则定义的路由就是默认子路由
{
path: '/shop',
component: Shop,
children: [
{
// 如果设置了 默认子路由,那么 访问 /shop的时候,就会自动加载组件 ShopGoods2 里面的内容
path: '',
component: ShopGoods2
},
{
path: 'goods',
component: ShopGoods
}
]
},
2.3 注册路由器 (在main.js里面)
import router from './router'
new Vue({
router
})
2.4 使用路由
router-link标签,用来生成路由组件标签
2.4.1 路径链接跳转
<!-- 方式一:直接使用path跳转 -->
<router-link to="/about">Go to XXX</router-link>
<!-- 方式二:使用 name别名 跳转 -->
<router-link :to="{name:'aboutPage'}">Go to XXX</router-link>
2.4.2 解析为 li
标签
<!-- router-link默认是解析为a标签,如果想要解析为li标签,设置tag属性-->
<router-link tag="li" to="/about">Go to XXX</router-link>
2.2.3 重置 active类
<!-- 设置 链接激活时使用的 CSS 类名 active-class -->
<router-link to = "/about" active-class = "activeClass">XXX</router-link>
2.2.4 解析展示组件内容 router-view
<!-- router-view 用来显示当前路由组件的页面 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
默认情况下,当切换路由组件对象时,路由组件对象会死亡释放,再次切换回来时是重新创建的
如果可以缓存路由组件对象, 可以提高用户体验
<keep-alive>
<router-view></router-view>
</keep-alive>
3. 编程式导航
在浏览器中,点击链接实现导航的方式,叫做声明式导航,比如 <router-link>
形式设置的导航
在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航,比如 this.$router.push(path)
1) this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
2) this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
3) this.$router.go(n): 请求第n个记录路由 n正数,请求下一个路由记录;n为负数,请求上一个路由记录
在历史记录中,后退到上一个页面
this.$router.back()
在历史记录中,前进到下一个页面
this.$router.forward()
3.1 请求(返回)上一个记录路由
this.$router.go(-1) == 跳转到上一次的浏览器页面(浏览器的回退按钮)
this.$router.back()
3.2 跳转到指定路由
3.3.1 保存路由记录 push
相当于点击路由链接,可以返回到当前路由界面,浏览器会有一个回退按钮
this.$router.push('/home')
this.$router.push({name:'homePage'})
3.3.2 不保存路由记录 replace
用新路由替换当前路由,不可以返回到当前路由界面
导航后不会留下 history 记录
this.$router.replace(path)
5. 路由参数
路径参数-params
在hash地址中,/
后面的参数,一般是 路径参数,一般使用 this.$route.params
访问参数
查询参数-query
在hash地址中,?
后面的参数,一般是 查询参数,一般使用 this.$route.query
访问参数
案例设置路的由对象如下:
path路径 userID是占位,?表示userID参数可以为空,如果没有设置?,那么userID参数是必传 主要用于params传参
{
path: '/loginPage/:userID?'
name: 'loginLink', // 路由别名
component: login
}
5.1 方法一:编程式导航传递参数
5.1.1 params传参
注意:使用这种方式传递的参数,如果在目标页面刷新是会出错的,而且传参对象必须是name
// 设置参数(路由别名跳转):
this.$router.push({ name: 'loginLink', params: { userId: 123 }})
// 接收参数:params
this.$route.params.userId
5.1.2 query传参
// 设置参数(路由路径跳转):
this.$router.push({ path: '/loginPage', query: { userId: 123 }});
// 接收参数:query
this.$route.query.userId
5.1.3 总结三种传参方式
//第一种:字符串形式--params参数和query参数
this.$router.push("/search/" + this.keyword+"?k="+this.keyword.toUppercase());
//第二种:模板字符串--params参数和query参数
this.$router.push(`/search/${this.keyword}?k=${this.keyword.touppercase()}`)
//第三种:对象 I--params参数和query参数
this.$router.push({name:"search",params : keyword: this.keyword},query: {k: this.keyword.touppercase()}})
注意:
路由跳转传参的时候,对象的写法可以是name、path形式,但是需要注意的是,path这种写法不能与params参数一起使用
5.2 方法二:声明式的导航 <router-link>
传参
5.2.1 动态路由参数
这种是params传参的方式,目标页面没有 ?
,是属于 路径参数,注意一定要设置 占位
// 1. 配置路由
{
path: '/loginPage/:userID', // 传递参数userID,采用占位符的方式
name: 'loginLink', // 路由别名
component: login
}
// 2. 路由router-link设置参数值
<router-link :to="'/loginPage/'+json.id">{{json.id}}</router-link>
// 3. 组件中获取参数
this.$route.params.userID
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参
// 在定义规则的时候,声明 props:true 选项
{path:'/home:id',component:Home,props:true}
// 组件汇中可以直接使用 props 直接接收 参数
props:["id"]
5.2.2 router-link设置参数
// 1. 通过 name别名的方式 设置参数:params传参
<router-link :to="{ name: 'loginLink', params: { userId: 1111}}">跳转页面</router-link>
this.$route.params.userId(接收参数)
// 2. 通过 path路径的方式 设置参数:query传参
<router-link :to="{ path: '/stype/details', query: { userId: 1111}}">跳转页面</router-link>
this.$route.query.userId(接收参数)
// 另外一种方式:query传参
<router-link :to="`/stype/details?id=`{{item.id}}">跳转页面</router-link>
// 接收参数
this.$route.query.id
6. 导航守卫
6.1 介绍
vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航
注意: 参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察
$route
对象来应对这些变化,或使用beforeRouteUpdate
的组件内守卫
6.2 全局前置守卫
使用 router.beforeEach
注册一个全局前置守卫:当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中
确保
next
函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
每个守卫接收的参数说明:
-
to: Route
: 即将要进入的目标 路由对象 -
from: Route
: 当前导航正要离开的路由 -
next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from
路由对应的地址next('/')
或者next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next
传递任意位置对象,且允许设置诸如replace: true
、name: 'home'
之类的选项以及任何用在router-link
的to
prop 或router.push
中的选项next(error)
: (2.4.0+) 如果传入next
的参数是一个Error
实例,则导航会被终止且该错误会被传递给router.onError()
注册过的回调
案例展示:在用户未能验证身份时重定向到 /login
// BAD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
// 如果用户未能验证身份,则 `next` 会被调用两次
next()
})
// GOOD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
6.3 全局解析守卫
用 router.beforeResolve
注册一个全局守卫。这和 router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
6.4 全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next
函数也不会改变导航本身
router.afterEach((to, from) => {
// ...
})
6.5 路由独享的守卫
可以在路由配置上直接定义 beforeEnter
守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
6.6 完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave
守卫。 - 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入