共计 5471 个字符,预计需要花费 14 分钟才能阅读完成。
Vue 的路由及路由钩子函数
什么是路由
什么是路由?网络原理中,路由指的是根据上一接口的数据包中的 IP 地址,查询路由表转发到另一个接口,它决定的是一个端到端的网络路径。
web 中,路由的概念也是类似,根据 URL 来将请求分配到指定的一个 ’ 端 ’。(即根据网址找到能处理这个 URL 的程序或模块)
使用 vue.js 构建项目,vue.js 本身就可以通过组合组件来组成应用程序;当引入 vue-router 后,我们需要处理的是将组件 (components) 映射到路由(routes),然后在需要的地方进行使用渲染。
其所包含的功能有:
- 嵌套的路由 / 视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于 Vue.js 过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的 CSS class 的链接
- HTML5 历史模式或 hash 模式,在 IE9 中自动降级
- 自定义的滚动条行为
1、基础路由
当我们通过 vue create 创建项目的的时候,会选择是否安装 vue-router, 项目创建后,在主组件 App.vue 中的 HTML 部分:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
上述代码中,<router-view/> 是路由出口,路由匹配到的组件将渲染在这里。
2、在 router/index.js 文件中
// 0. 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)
// 1. 定义 (路由) 组件。可以从其他文件 import 进来
const Foo = {template: '<div>foo</div>'}
// 2. 定义路由每个路由应该映射一个组件。其中 "component" 可以是通过 Vue.extend() 创建的组件构造器,或者,只是一个组件配置对象。const routes = [{ path: '/foo', component: Foo},
]
// 3. 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({routes})
// 4. 创建和挂载根实例。通过 router 配置参数注入路由,从而让整个应用都有路由功能
const app = new Vue({router}).$mount('#app')
3、动态路由
什么是动态路由?动态路由是指路由器能够自动的建立自己的路由表,并且能够根据实际情况的变化实时地进行调整。
1、在 vue 项目中,使用 vue-router 如果进行不传递参数的路由模式,则称为静态路由;如果能够传递参数,对应的路由数量是不确定的,此时的路由称为动态路由。动态路由,是以冒号为开头的(:),例子如下:
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}, {
path: '/RouterComponents/:id',
name: 'RouterComponents',
component: RouterComponents
}
]
})
一个“路径参数”使用冒号 :
标记。当匹配到一个路由时,参数值会被设置到 this.$route.params
,可以在每个组件内使用。
2、路由跳转,执行方式有两大类;
第一大类:router-link 模式,直接把参数写在 to 属性里面:
<router-link :to="{name:'RouterComponents',params:{id:110}}"> 跳转 </router-link>
第二大类:$router.push()模式,代码如下:
methods: {changeFuc (val) {
this.$router.push({
name: 'RouterComponents',
params: {id: val}
})
}
}
或者:
methods: {changeFuc (val) {
this.$router.push({path: `/RouterComponents/${val}`,
})
}
}
4、路由嵌套
vue 项目中,界面通常由多个嵌套的组件构成;同理,URL 中的动态路由也可以按照某种结构对应嵌套的各层组件:
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
}
]
}
]
})
5、捕获所有路由或 404 NotFound 路由
常规参数只会匹配被 /
分隔的 URL 片段中的字符。如果想匹配 任意路径,我们可以使用通配符 (*
):
{
// 会匹配所有路径
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}
当使用 通配符 路由时,请确保路由的顺序是正确的,也就是说含有 通配符 的路由应该放在最后。路由 {path: '*'}
通常用于客户端 404 错误。如果你使用了History 模式,请确保正确配置你的服务器。
6、编程式导航
声明式 | 编程式 |
---|---|
<router-link :to=”…”> | router.push(…) |
想要导航到不同的 URL,则使用 router.push
方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
当你点击 <router-link>
时,这个方法会在内部调用,所以说,点击 <router-link :to="...">
等同于调用 router.push(...)
。
// 字符串
router.push('home')
// 对象
router.push({path: 'home'})
// 命名的路由
router.push({name: 'user', params: { userId: '123'}})
// 带查询参数,变成 /register?plan=private
router.push({path: 'register', query: { plan: 'private'}})
如果提供了 path,params 会被忽略
const userId = '123'
router.push({name: 'user', params: { userId}}) // -> /user/123
router.push({path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({path: '/user', params: { userId}}) // -> /user
7、命名路由
由于我们需要通过不同的路由跳转到不同的页面,这时给我们的路由都加一个名字操作起来会比较方便
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
<router-link :to="{name:'user', params: { userId: 123}}">User</router-link>
8、命名视图
有时候我们需要一个布局,这时候,我们就需要用到命名视图。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
9、嵌套命名视图
{
path: '/settings',
// 你也可以在顶级路由就配置命名视图
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
10、路由组件传参
在组件中使用 $route
会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
使用 props
将组件和路由解耦:
取代与 $route 的耦合
const User = {template: '<div>User {{ $route.params.id}}</div>'
}
const router = new VueRouter({
routes: [{ path: '/user/:id', component: User}
]
})
通过 props 解耦
const User = {props: ['id'],
template: '<div>User {{id}}</div>'
}
const router = new VueRouter({
routes: [{ path: '/user/:id', component: User, props: true},
// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:{
path: '/user/:id',
components: {default: User, sidebar: Sidebar},
props: {default: true, sidebar: false}
}
]
})
路由钩子
1、路由钩子
在某些情况下,当路由跳转前或跳转后、进入、离开某一个路由前、后,需要做某些操作,就可以使用路由钩子来监听路由的变化
全局路由钩子:router.beforeEach
注册一个全局前置守卫
router.beforeEach((to, from, next) => {
// 会在任意路由跳转前执行,next 一定要记着执行,不然路由不能跳转了
console.log('beforeEach')
console.log(to,from)
//
next()})
//
router.afterEach((to, from) => {
// 会在任意路由跳转后执行
console.log('afterEach')
})
单个路由钩子:
只有 beforeEnter,在进入前执行,to 参数就是当前路由
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {// ...}
}
]
路由组件钩子:
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
附:完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用离开守卫。
- 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 用创建好的实例调用
beforeRouteEnter
守卫中传给next
的回调函数。
2、路由元信息
定义路由的时候可以配置 meta
字段:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: {requiresAuth: true}
}
]
}
]
})