Vue-Router路由管理
- 基础使用
- 进阶
1 基础
- 安装与基本使用
- 动态路由
- 嵌套路由
- 编程式导航
- 命名路由
- 命名视图
- 重定向和别名
- 路由组件传参
- History模式
- Modules
1.1 安装与基本使用
//安装
npm install vue-router --save
//main.js内引入
import VueRouter from 'vue-router'
Vue.use(VueRouter)
//此时在vue实例上会多出`$route`和`$router`两个属性
创建两个页面
//About.vue
<template>
<div>关于页面</div>
</template>
//Detail.vue
<template>
<div>关于页面</div>
</template>
配置路由
//router.js文件
import VueRouter from "vue-router";
import About from './pages/About'
import Detail from './pages/Detail'
export const router = new VueRouter({
routes: [
{
path: '/about',
component: About,
},
{
path: '/detail',
component: Detail
}
]
})
- 引入VueRouter并实例化,引入组件
- 配置对象的
routes
选项,以数组-对象
形式注册路由,包括路径与组件
在mian.js中引入router实例并注册
//mian.js
import { router } from './router'
new Vue({
router,
render: h => h(App),
}).$mount('#app')
//此时可以观察到$router与$route的相关配置
在App.vue中使用路由
<template>
<div id="app">
<router-link to="/about">跳转about</router-link><br />
<router-link to="/detail">跳转detail</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App",
};
</script>
router-link
为路由入口
,在to中配置路径router-view
为路由出口
,跳转的页面在此处显示
类似于动态组件
,实现了基本的跳转功能,路由的功能更加强大
$router类似于$store,在根实例注册后,子组件都可以访问到原路由实例,不需要频繁的在子组件中引入。$route是当前路由
1.2 动态路由
根据不同的路径参数跳转至同一组件,显示不同的数据
//router.js
{
path: '/about:id',
component: About,
},
//App.vue
<template>
<div id="app">
<router-link to="/about/:'1'">用户1</router-link><br />
<button @click="to">用户2</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App",
methods: {
to() {
this.$router.push('/about/:"2"');
},
},
};
</script>
//About.vue
<template>
<div>关于页面{{ userId }}</div>
</template>
<script>
export default {
computed: {
userId() {
return this.$route.params.id;
},
},
};
</script>
- 在router中注册的路径添加
:param
- 跳转时可以使用router-link或者使用$router.push,路径中携带参数
在跳转至的路由可以通过$route.params访问动态路径的参数
可以根据参数去请求数据再渲染页面
:
,对应定义的是params参数? = &
,可以定义query参数,通过$route.query访问<router-link to="/about/?id=1">用户1</router-link><br />
通配符
捕获所有路由或404 Not found路由
{ //匹配所有路径
path:'*'
}
{ //匹配以'/user'开头的任意路径
path:'/user-*'
}
使用通配符路由时,$route.params会添加一个名为pathMatch参数。包含URL通配符匹配的部分
//router.js
{
path: '*',
component: () => import('./pages/Notfound')
}
当跳转的路径匹配失败时,可以匹配通配符路径,并通过pathMatch
参数提示错误的路径
使用'/user-*',可以设置特定路径下某种页面未找到的提示信息
1.3 嵌套路由
有时在路由跳转后可能还需要路由跳转,这时就需要嵌套多级路由
在路由注册时添加children
属性
//router.js
{
path: '/about',
component: About,
children: [
{
path: 'child',
component: () => import('./pages/Child')
}
]
},
//跳转
<router-link to="/about/child">二级跳转</router-link>
子路由的路径前不需要/
,不然会认为从跟路径开始
1.4 编程式导航
除了使用router-link声明式
创建a标签的方式进行跳转,也可以是借助router的实例方法编程式
实现跳转
push:向history栈添加
一个新的记录,可以点击浏览器的后退至之前的URL
replace:替换
当前的history栈顶记录
go(n):前进或后退|n|步,负数为后退,正数为前进,记录不够用则失败
this.$router.push({
path: "/detail",
// params: { userId: 123 },
query: { user: "张三" },
});
push中的数据可以使用对象形式,params参数不能与path同时使用,router-link中的to
也支持对象形式的写法
可以使用name
属性传递params参数
params
参数:必须在路径上添加参数名,并且跳转时使用name属性query
参数:不必在注册时在路径上添加信息,跳转时使用path或name都可当在路径中添加了参数,但是跳转时没有传递params参数,也会跳转失败
使用name属性时,支持同时传递params与query参数
1.5 命名路由
上述已经对name进行了一些介绍,是对路由的命名
可以简化路径过长的问题,同时避免params参数的问题
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
1.6 命名视图
router-view中可以添加name
属性,对应路由中的组件
注册路由时的components可以是对象形式,包含多个组件,key提供给name选择
//router.js
{
name: 'detail',
path: '/detail/:userId',
components: {
default: Detail,
a: () => import('./pages/Ads')
}
},
//视图
<router-view></router-view>
<router-view name="a"></router-view>
没有name属性的视图,使用default
的组件
1.7 重定向和别名
重定向
访问'/a'重定向'/b',URL与匹配的路由都换成'/b'
//路径
routes: [
{ path: '/a', redirect: '/b' }
]
//命名
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
//动态方法
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
可以在返回主页时将根路径重定向为子路由路径,保证页面的填充
别名
'/a'的别名'b',当访问'/b'时,URL仍然是'/b',但是路由匹配为'/a',相当于访问'/a',只是名字不同
routes: [
{ path: '/a', component: A, alias: '/b' }
]
可以自由地将UI结构映射到任意的URL,不受限与配置的嵌套的路由结构
1.8 路由组件传参
props
属性:boolean|Object|Function
$route会使组件与对应路由高耦合,导致组件只能在特定的URL上使用,限制灵活性。
使用props属性可以将组件和路由解耦
//对象形式
{
name:'detail',
path:'/detail',
component:Detail,
props:{foo:'a',bar:'b'}
}
在Detail组件中,可以直接在props中声明后使用
对象形式传递的是静态数据,在配置的时候就写好的,使用场景较少
//布尔形式
{
...
props:true,
}
//多组件
{
...
components:{Detial,Ads},
props:{Detail:true,Ads:false}
}
将路由跳转时的params
参数传入组件中,在组件的props中声明后使用
//函数形式
{
...
props($route){
return {
id:$route.params.userId,
name:$route.query.user
}
}
}
//解构赋值简写 - 类似于vuex的action中的context对象
{
...
props({params,query}){
return {
id:params.userId,
name:query.user
}
}
}
在不使用props的情况下,传递的参数全部都在URL上,需要使用计算属性逐个获取
使用props的函数形式,可以将params和query参数以及静态数据在组件中的props中声明
这样简化了取值的操作,解耦合
1.9 History模式
router中的配置除了routes
选项外,还要其他选项,例如mode
mode默认为hash,地址栏会有一个#
号,URL改变时,页面不会重新加载
更改为正常的网址形式,使用mode:'history'
history模式需要一定的后台支持
,不然会出现404
这是因为hash模式下#后的地址作为路由跳转,不会发起http请求,但是在history模式下,刷新页面时,会发起http请求,由于没有#的分隔会将整个地址都作为请求地址
nginx
location / {
try_files $uri $uri/ /index.html;
}
Node.js
const http = require('http')
const fs = require('fs')
cosnt httpPort = 80
http.createServer((req,res)=>{
fs.readFile('index.html','utf-8',(err,content)=>{
if(err){
...
}
res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'})
res.end(content)
})
}).listen(httpPort,()=>{
console.log('Server listening on:http://loaclhost:%s',httpPort)
})
可以在Node.js中使用connect-history-api-fallback
插件,快速解决history模式后端地址和前端路由地址的冲突,造成请求失败
2.10 Modules
当嵌套路由的层级过多时,可以将其分割成多个模块
与vuex拆分的方式类似,不用创建module数组,引入后直接在routes数组中注册即可
//routers文件夹中的index.js 入口文件
import VueRouter from 'vue-router'
import { about } from './modules/about'
import { detail } from './modules/detail'
import { Notfound } from './Notfound'
export const router = new VueRouter({
routes: [
{ path: '/' },
about,
detail,
Notfound
]
})
修改为模块化后需要重新在main.js中引入并注册
2 进阶
- 路由守卫
- 路由元信息
- 过渡动效
- 数据获取
- 滚动行为
- 路由懒加载
- 导航故障
2.1 路由守卫
- 全局前置守卫
- 全局解析守卫
- 全局后置钩子
- 路由独享守卫
- 组件内的守卫
- 整体流程
2.1.1 全局前置守卫
beforeEach
const router = new VueRouter({})
router.beforeEach((to,from,next)=>{})
- to : 目标路由
- from : 离开的路由
next : 放行函数,
无参数
直接跳转至目标路由- next(false) 中断当前导航
- next('/')或next({path:'/'}) 指定路由跳转,可以使用
name
以及其他选项 - next(error) 中断当前导航,并将Error实例传入router.onError()执行回调
- next函数仅能被调用一次
//index.js - 模拟未登录用户访问页面,重定向至登录页面
router.beforeEach((to, from, next) => {
if (to.name !== 'login' && to.name !== 'register' && !isAuthenticated) {
next({ name: 'login' })
} else {
next()
}
})
如果跳转使用的push的方式,被守卫拦截重定向后会报错,但不影响使用
可以在push后使用catch捕获错误
2.1.2 全局解析守卫
beforeResolve
类似于beforeEach
区别:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫被调用
2.1.3 全局后置钩子
afterEach
与守卫不同的是没有next放行函数
router.afterEach((to, from) => {
// ...
})
2.1.4 路由独享守卫
beforeEnter
与全局不同的在于它作为一个路由的配置项,单独控制一个路由
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
to : 跳转至当前路由
from : 由某路由跳转来
可以限制from可以从何处跳转至该路由
2.1.5 组件内的守卫
在组件中作为配置选项
beforeRouteEnter:类似于独享守卫,在组件实例创建前调用,故不能获取vm实例
可以在next中添加回调函数访问组件实例
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
beforeRouteUpdate:路径改变,组件被复用时调用,可以访问vm实例
此时的this代表的时from的路由组件实例
beforeRouteLeave:离开该组件的对应路由时调用,可以访问该组件vm实例
可以在离开前对组件中的内容进行判断,确认是否离开
2.1.6 整体流程
2.2 路由元信息
meta配置项,存储路由元信息
在连续的路由嵌套中,每一层的路由信息都会被收集到$route.matched数组
中
可以使用数组的遍历方法遍历matched数组对meta字段进行检查
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 确保一定要调用 next()
}
})
2.3 过渡动效
router-view是基本的动态组件,相当于component标签
所以可以使用transition标签对其进行包裹实现过渡
效果
同样的使用keep-alive实现组件缓存
transition与keep-alive
2.4 数据获取
在路由跳转时经常会伴随着从服务器获取数据
导航完成后请求:完成导航后,在组件的声明周期钩子中请求数据。请求完成期间显示"加载"提示。
使用v-if条件渲染加载页面、错误页面以及请求成功页面
在created钩子中请求数据
导航完成前请求:在路由守卫中获取数据,获取成功后执行导航
beforeRouteEnter中请求数据,在next()回调
中配置请求获得的数据
2.5 滚动行为
scrollBehavior配置选项
切换至新路由时,页面滚动到顶部或者保持原先的滚动位置
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
默认情况下不发生滚动,返回svaedPosition时前进/后退与浏览器原生操作一致
return {
x:0,y:0
}
根据坐标直接滚动
meta:{x:0,y:0}
return {
selector:to.meta,
behavior:'smooth'
}
根据元信息进行平滑滚动
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 500)
})
可以返回一个Promise,resolve中传入滚动信息,实现异步滚动
2.6 路由懒加载
异步组件
//异步组件工厂函数
const AsyncComponent = (url) => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import(url),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
使用import()函数异步引入组件,可以在首次渲染时先不下载部分组件
在访问异步组件时再进行下载
2.7 导航故障
在路由导航失败时可以在push后链式处理错误信息
- isNavigationFailure(failure,NavigationFailureType)
NavigationFailureType
- redirected:调用了 next(newLocation) 重定向到了其他地方
- aborted:调用了 next(false) 中断了本次导航
- cancelled:当前导航还没有完成之前又有了一个新的导航
- duplicated:导航被阻止,因为我们已经在目标位置了
import VueRouter from 'vue-router'
const { isNavigationFailure, NavigationFailureType } = VueRouter
// 正在尝试访问 admin 页面
router.push('/admin').catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
// 向用户显示一个小通知
showToast('Login in order to access the admin panel')
}
})
// 正在尝试访问 admin 页面
router.push('/admin').catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
failure.to.path // '/admin'
failure.from.path // '/'
}
})
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。