Vue复习三(路由)
https://router.vuejs.org/zh/installation.html
<router-link to="/foo">Go to Foo</router-link>
<router-view></router-view>
动态ID
/one/:id
this.$route.params.id
问号传参
this.$route.query
props 拿到动态的id值
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 }
}
]
})
props
被设置为true
,route.params
将会被设置为组件属性
函数的形式
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query})
props:['query']
?name=333
this.query
// {name:333}
响应路由的变化
watch:{
$route(to, from) {
// 现在的, 之前的
console.log(to, from);
}
},
匹配条件
// 匹配所有路径
path: '*'
路由导航
// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
可选
/optional-params/:foo?
案例
/optional-params
/optional-params/foo
限定id的条件
// 一个参数后面可以跟一个正则表达式模式的括号
//只有当:id为所有数字时,该路由才会匹配
/params-with-regex/:id(\\d+)
也就是id只有是数字的时候才能匹配
// 星号可以匹配任何东西
{ path: '/asterisk/*' }
// 括号中的内容是可选的
/optional-group/(foo/)?bar
嵌套路由
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 相对的跟angular类似
path:'posts'
component: UserProfile
},
]}
编程式导航
声明式 | 编程式 |
---|---|
<router-link :to="..."> |
router.push(...) |
// 字符串, 绝对的
router.push('home')
// 对象的形式是相对的, 如果是'two/three' => 'two/three'
router.push({ path: 'home' })
// 问好传参
router.push({path:'/two/three',query:{age:'gsg'}})
替换
替换掉当前的 history 记录
声明式 | 编程式 |
---|---|
<router-link :to="..." replace> |
router.replace(...) |
退后,前进
类似 window.history.go(n)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
Vue Router 的导航方法 (
push
、replace
、go
)在各类路由模式 (
history
、hash
和abstract
) 下表现一致。
router.push(location, onComplete?, onAbort?)
router.push(location).then(onComplete).catch(onAbort)
router.replace(location, onComplete?, onAbort?)
router.replace(location).then(onComplete).catch(onAbort)
router.go(n)
router.back()
router.forward()
命名视图
就是多视图
案例
<router-view></router-view>
<router-view name="three"></router-view>
{
path: 'three/:id',
components: {
default:()=>import('@/components/FourChild.vue'),
three:()=>import('@/components/ThreeChild.vue')
}
}
路由点击多次报错
禁止全局的路由错误处理
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {
return originalPush.call(this, location).catch(err => err)
}
命名路由
就是不用写那么多配置,快捷的跳转
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',// 快捷跳转方式
component: User
}
]
})
html跳转方式
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
如果你想独立显示的,就单独写一个吧
重定向
{ path: '/a', redirect: '/b' }
// 快捷重定向到命名路由
{ path: '/a', redirect: { name: 'foo' }}
//方法
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
别名
{ path: '/a', component: A, alias: '/b' }
/a 的别名是 /b
就是/b 访问的也是 /a 但是当前地址也是/b 本来不变, 类似一个别名
History 模式
默认hash模式, 设置history
前台后台就需要配置
路由守卫
全局前置守卫 beforeEach
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
router.beforeEach((to, from, next) => {
// ...
})
to: Route
: 即将要进入的目标
from: Route
: 当前导航正要离开的路由
next:Function
-
next()
进行管道中的下一个钩子 -
next(false)
中断当前导航 -
next('/')
或者next({path:'/'})
跳转到不同的地址replace: true name: 'home' 比较自由
-
next(error)
报错了, 会把错误传递到router.onError()
注册回调
next() 严格上调用一次
router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() })
路由独享的守卫 beforeEnter
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
组件内的守卫
{
data(){
return {
}
},
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
传一个回调给 next
来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false)
来取消
beforeRouteLeave (to, from, next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
全局解析守卫 beforeResolve
在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后
全局后置钩子
注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next
函数也不会改变导航本身
router.afterEach((to, from) => {
// ...
})
导航解析流程
默认
- beforeEach 全局前置守卫(登录)
- beforeEnter 路由独享的守卫
- beforeRouteEnter 组件内守卫(当守卫执行前)
- beforeRouteUpdate 组件内复用守卫(:id /1 /2)
- beforeRouteLeave 离开守卫,例如未保存
- beforeResolve 全局解析守卫
- afterEach 全局后置钩子
router.onError
router.onError(error=>{
console.log(error)
})
注册一个回调,该回调会在路由导航过程中出错时被调用。注意被调用的错误必须是下列情形中的一种:
- 错误在一个路由守卫函数中被同步抛出;
- 错误在一个路由守卫函数中通过调用
next(err)
的方式异步捕获并处理; - 渲染一个路由的过程中,需要尝试解析一个异步组件时发生错误。
元数据 meta
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
this.$route.meta
// { requiresAuth: true }
过渡
<transition>
<router-view></router-view>
</transition>
动态的
当前路由与目标路由的变化关系,
<!-- 使用动态的 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'
}
}
数据获取
导航完成后获取数据
data () {
return {
loading: false
}
},
created () {
// 当视图被创建并且数据被创建时,获取数据
this.fetchData()
},
watch: {
// 如果路由发生变化,再次调用该方法
'$route': 'fetchData'
},
methods: {
// 数据请求
fetchData () {
}
}
导航前获取
就使用 路由组件内守卫进行操作
滚动
自定义滚动
该scrollBehavior
函数接收to
和from
路由对象。第三个参数,savedPosition
仅在通过popstate
导航(由浏览器的后退/前进按钮触发)时才可用。也是history
模式
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// 返回对象 { x: number, y: number }
return { x: 0, y: 0 }
}
})
滚动到锚点
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
}
}
}
平滑滚动
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash,
behavior: 'smooth',
}
}
}
异步滚动
scrollBehavior (to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 500)
})
500ms 后滚动
延迟加载路线
const Foo = () => import('./Foo.vue')
{ path: '/foo', component: Foo }
将同一块中的组件分组
命名块
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
*导航失败
3.4.0 新功能
使用router-link
组件时,这些故障均不会记录错误
有点蒙蔽, 不太懂
router-link
<!-- 字符串 -->
<router-link to="home">Home</router-link>
<!-- 绑定数据 -->
<router-link :to="'home'">Home</router-link>
<!-- 命名的路由 -->
<!-- 动态ID :userId 的值-->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
<!-- 带查询参数,下面的结果为 /register?plan=private -->
<router-link :to="{ path: 'register', query: { plan: 'private' }}"
>Register</router-link>
replace 替换
<router-link :to="{ path: '/abc'}" replace></router-link>
append 相对
设置 append
属性后,则在当前 (相对) 路径前添加基路径
本来 /one/1
跳转后 /one/2
<router-link :to="{ path: 2}" append></router-link>
tag 更改标签名
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染结果 -->
<li>foo</li>
active-class 激活的路由class
- 类型:
string
- 默认值:
"router-link-active"
<router-link :to="{path:'2'}" active-class="aaa">222</router-link>
axact 完全匹配
<!-- 这个链接只会在地址为 / 的时候被激活 -->
<router-link to="/" exact></router-link>
event 事件促发的条件
- 类型:
string | Array<string>
- 默认值:
'click'
可以是一个字符串或是一个包含字符串的数组
<router-link :to="{path:1}" tag="input" event="blur">
222
</router-link>
改成input 失去焦点触发
exact-active-class 精准匹配class
router-view
<transition>
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
name 用于命名视图
Router
routes
interface RouteConfig = {
path: string,
component?: Component,
name?: string, // 命名路由
components?: { [name: string]: Component }, // 命名视图组件
redirect?: string | Location | Function,
props?: boolean | Object | Function,
alias?: string | Array<string>,
children?: Array<RouteConfig>, // 嵌套路由
beforeEnter?: (to: Route, from: Route, next: Function) => void,
meta?: any,// 元数据
// 2.6.0+
caseSensitive?: boolean, // 匹配规则是否大小写敏感?(默认值:false)
pathToRegexpOptions?: Object // 编译正则的选项
}
外部的配置
new VueRouter({
routes,
// 下面都是全局
mode:'history',
base:'/app/sex/' // 前缀
// 默认的激活的class 默认值: "router-link-exact-active"
linkActiveClass:'ccc'
// 默认精确激活的class 默认值: "router-link-exact-active"
linkExactActiveClass:"aaa"
})
parseQuery / stringifyQuery
提供自定义查询字符串的解析/反解析函数。覆盖默认行为。
其实提供这两种用法,是针对问号传参进行加密解决的作用
https://www.codenong.com/cs107092308/
// https://github.com/ljharb/qs
import qs from 'qs';
const router = new Router({
routes: [
// ...
],
// 反序列化
parseQuery(query) {
return qs.parse(query);
},
//序列化
stringifyQuery(query) {
var result = qs.stringify(query);
return result ? ('?' + result) : '';
}
});
router.app vue实例
// 拿到vue 实例
console.log(this.$router.app);
router.currentRoute 当前路由信息对象
console.log(this.$router.currentRoute);
## router.getMatchedComponents 当前路由匹配的组件数组
通常在服务端渲染的数据预加载时使用
全局后置路由守卫
router.beforeResolve((to, from, next) => {
// 拿到当前匹配的数组
console.log(router.getMatchedComponents(to));
next()
})
路由对象Route
// 当前路由的路径,总是解析绝对路径
$route.path
// 动态ID
$route.params
// 问号传参
$route.query
// hash
$route.hash
// 解析后URL,包括查询参数和hash的完整路径
$route.fullPath
// 一个包含从上到下的所有对象 (副本) 又叫路由记录
$route.matched
// 当前路由的名称, 命名路由
$route.name
// 如果存在重定向,即为重定向来源的路由的名字, (别名还想也可以)
$route.redirectedFrom
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者
总有一天我也能成为大佬