路由基础
router-link
// 包装的 a 标签, router-link 组件
// to 属性
<router-link to="/about"></router-link>
router-view
// router-view 组件 视图渲染
<router-view></router-view>
router.js
// router.js
// 配置路由列表
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
component: Home // 路径对应渲染组件
},
{
path: '/about',
component: () => import('views/About.vue') // 懒加载,优化
}
]
动态路由匹配
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
component: Home // 路径对应渲染组件
},
{
path: '/about',
component: () => import('views/About.vue') // 懒加载,优化
},
// 动态路由匹配
{
path: '/argu/:name',
component: () => import('views/argu.vue')
}
]
// argu.vue
<template>
<div>
{{ $route.params.name }} // 当前加载的路由对象 参数 params 对象里的 name 动态路由参数
</div>
</template>
// url
http://localhost:8080/argu/yym // $route.params.name == yym
嵌套路由
// parent.vue
<template>
<div>
I am parent
<router-view></router-view> // 视图渲染
</div>
</template>
// router.js
// 配置路由列表
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
component: Home // 路径对应渲染组件
},
{
path: '/about',
component: () => import('views/About.vue') // 懒加载,优化
},
// 动态路由匹配
{
path: '/argu/:name',
component: () => import('views/argu.vue')
},
{
path: '/parent',
component: () => import('views/parent.vue'),
children: [
{
path: 'child', // 不需要写斜杠, 在child 里自动补全
component: () => import('views/child.vue')
}
]
}
]
命名路由
// router.js
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
name: 'home', // 命名 设置 name 值
component: Home // 路径对应渲染组件
},
{
path: '/about',
name: 'about', // 命名 设置 name 值
component: () => import('views/About.vue') // 懒加载,优化
},
]
// App.vue
<template>
<div>
<router-link :to="{ name: 'home' }"></router-link>
<router-link :to="{ name: 'about' }"></router-link>
</div>
</template>
命名视图
// router.js
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
name: 'Home',
component: Home // 路径对应渲染组件
},
{
path: '/about',
name: 'About',
component: () => import('views/About.vue') // 懒加载,优化
},
{
path: '/named_view',
components: { // 加s 命名多个视图
default: () => import('views/child.vue'),
email: () => import('views/email.vue'),
tel: () => import('views/tel.vue')
}
}
]
// App.vue
<template>
<div>
<router-link :to="{ name: 'home' }"></router-link>
<router-link :to="{ name: 'about' }"></router-link>
<router-view/>
<router-view name="email" />
<router-view name="tel" />
</div>
</template>
重定向
// router.js
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
name: 'Home',
component: Home // 路径对应渲染组件
},
{
path: '/about',
name: 'About',
component: () => import('views/About.vue') // 懒加载,优化
},
{
path: '/main', // 进入 /main 路径
redirect: '/' // 重定向到 home
},
{
path: '/main',
redirect: { // 对象形式
name: 'Home'
}
},
{
path: '/main',
redirect: to => { // 函数形式
return { // return 一个路径
name: 'Home'
}
}
},
{
path: '/main',
redirect: to => { // 函数形式
return '/'
}
}
]
别名
// router.js
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
alias: '/home_page', // 别名
name: 'Home',
component: Home // 路径对应渲染组件
},
]
编程式导航
// 通过 js 来控制路由跳转
// Home.vue
<template>
<div>
<button @click="hanlderClick">返回</button>
</div>
</template>
<script>
export default {
methods: {
handlerClick () {
this.$router.go(-1) // 返回上一页
this.$router.go(1) // 前进一页
this.$router.back() // 返回上一页
// 浏览历史中加入一个记录
this.$router.push('/parent') // 进入 parent
this.$router.push({ // 命名
name: 'parent',
query: { // query 参数 url: /parent?name=yym
name: 'yym'
}
})
// 当前浏览历史 替换
this.$router.replace({
name: 'parent'
})
}
}
}
</script>
路由进阶
1. 路由传参
props: 布尔模式
// argu.vue
<template>
{{ name }}
</template>
<script>
export default {
props: {
name: {
type: String,
default: 'yym'
}
}
}
</script>
import Home from './views/Home.vue'
export default [
// 动态路由匹配
{
path: '/argu/:name',
name: 'argu'
component: () => import('views/argu.vue'),
props: true
// props: true , 会使用 router.params 作为组件的属性, name 插入到组件中,
}
]
props 对象模式
// about.vue
<template>
{{ food }} // banana
</template>
<script>
export default {
props: {
food: {
type: String,
default: 'apple'
}
}
}
</script>
import Home from './views/Home.vue'
export default [
{
path: '/about',
name: 'about'
component: () => import('views/about.vue'),
props: { // 对象模式传参
food: 'banana'
}
}
]
props: 函数模式
// router.js
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
name: 'Home',
component: Home, // 路径对应渲染组件
props: route => ({
food: route.query.food
})
},
]
// home.vue
// url: /home?food=banana
<template>
{{ food }} // banana
</template>
<script>
export default {
props: {
food: {
type: String,
default: 'apple'
}
}
}
</script>
2. history 模式
// src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router.js'
Vue.use(Router)
export default new Router({
mode: 'hash', // 默认 #/
routes
})
// 有一个问题: 匹配不到页面静态资源, 匹配不到组件, 配置404
export default new Router({
mode: 'history', // 需要后端同学配合
routes
})
// url: /home/abc
// router.js
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
name: 'Home',
component: Home, // 路径对应渲染组件
props: route => ({
food: route.query.food
})
},
// 路由有优先级, 所以 404 放在最后不会影响其他路由的匹配
{
path: '*',
component: () => import('views/error_404.vue')
}
]
3. 导航守卫
全局导航守卫
// src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router.js'
Vue.use(Router)
const router = new Router({
routes
})
// 假设登录判断
const HAS_LOGIN = true
// 全局路由前置守卫
router.beforeEach((to, from, next) => {
// to 即将跳转到的路由对象
// from 将要离开的路由对象
// next() 函数
// 开始是否进入登录
if (to.name !== 'login') {
if (HAS_LOGIN) next() // 如果登录了, 继续
else next({
name: 'login'
})
} else { // 如果跳转到登录页面
if (HAS_LOGIN) next({name: 'home'}) // 登录了, 到主页
else next() // 没有登录 next()
}
})
// 全局路由后置守卫
router.afterEach((to, from) => {
// 可以设置 loading 的进度 为 false
})
export default router
路由独享守卫
// router.js
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
name: 'Home',
component: Home, // 路径对应渲染组件
beforeEnter: (to, from, next) => {
// ...
next()
}
}
]
组件内的钩子
// home.vue
<template>
home
</template>
<script>
export default {
beforeRouteEnter (to, from, next) => {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
next(vm => {
console.log(vm)
})
},
// 用户编辑未保存离开, 可以询问是否离开
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
const leave = confirm('您确认要离开吗')
if (leave) next()
else next(false)
}
}
</script>
// argu.vue
<template>
argu
</template>
<script>
export default {
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
}
</script>
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用离开守卫。
beforeRouteLeave
- 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发
DOM
更新。 - 用创建好的实例调用
beforeRouteEnter
守卫中传给 next 的回调函数。
4. 路由元信息
// router.js
import Home from './views/Home.vue'
export default [
{
path: '/', // url 路径
name: 'Home',
component: Home,
meta: { // 路由元信息
title: '主页'
}
},
]
// src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router.js'
Vue.use(Router)
const router = new Router({
routes
})
// 全局路由前置守卫
router.beforeEach((to, from, next) => {
// 元信息的判断
if (to.meta && to.meta.title) {
window.document.title = to.meta.title || 'admin'
}
})
export default router
5. 过渡动效
单个路由过渡
<template>
<transition name="router">
<router-view></router-view>
</transition>
</template>
<style>
.router-enter {
// ...
}
.router-enter-active {
// ...
}
.router-enter-to {
// ...
}
.router-leave {
// ...
}
.router-leave-active {
// ...
}
.router-leave-to {
// ...
}
</style>
- 动态过渡
<!-- 使用动态的 transition name -->
<transition :name="transitionName">
<router-view></router-view>
</transition>
// 接着在父组件内
// watch $route 决定使用哪种过渡
watch: {
'$route' (to, from) {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}
网友评论