1. 复习组件中自定义v-model指令使用:
组件中v-model
最基本的用法:在父组件给子组件绑定v-model
的时候,相当于绑定了自定义事件@input
,v-model=
后面传递的值,是子组件在$emit('input', val)
时父组件接受的val值,也是转递给子组件的值:
自定义v-model的用法:
子组件:
<script>
//在子组件定义model{ prop: 'fatherdata', event: 'custEventName' }
export default {
model: {
prop: 'fatherdata', // 对应 v-model=后面传递的值
event: 'custEventName'//自定义事件名 即$emit时执行传入的事件名 默认为'input'
},
//prop参数为父组件传递的值,然后可以设置props接受参数配置
props: {
fatherdata: String,
default() {
return ''
}
}
}
//子组件调用:this.$emit('custEventName', val);
</script>
父组件:
<template>
<div>
<!-- 接收子组件调用:this.$emit('custEventName', val)中的val值;并且双向绑定至data中的name值,并且也是传递给子组件的fatherdata值-->
<CustomVModel v-model="name"/>
</div>
</template>
<script>
import CustomVModel from './CustomVModel'
export default {
components: {
CustomVModel
},
data() {
return {
name: '',
}
}
}
</script>
遗留问题:
① v-model的双向绑定底层原理是什么?
2.refs 最基本最简单的使用
实现dom
的查找:在元素上绑定ref属性
(ref="dom1"
),使用this.$refs.dom1
来找到该节点。
3.slot最基本使用
①匿名插槽:main
子组件
<!-- CustSlot组件 -->
<template>
<div>
<main>
<!-- 默认被插的位置 -->
<slot> 我是默认内容 </slot> <!-- slot中也可以自定义默认内容 -->
</main>
</div>
</template>
父组件
<!-- 父组件使用CustSlot组件 -->
<CustSlot>
<template>
<p>需要插入的内容</p><!-- 会被默认插到 main slot中 -->
</template>
</CustSlot>
②具名插槽:v-slot
、name
子组件
<!-- CustSlot组件 -->
<template>
<div>
<slot name="part1"></slot>
<slot name="part2"></slot>
</div>
</template>
父组件
<!-- 父组件使用CustSlot组件 -->
<CustSlot>
<template v-slot:part1>
<p>***内容***</p><!-- 会被默认插到name="part1" 的slot中 -->
</template>
<template v-slot:part2>
<p>***内容***</p><!-- 会被默认插到name="part2" 的slot中 -->
</template>
</CustSlot>
③有关slot中的数据传递: v-slot
、作用域插槽
备注:这里slotData、slotProps均可以自定义名称
子组件
<!-- ScopedSlotDemo组件 -->
<template>
<slot :slotData="website"><!-- website为子组件传递给父组件的值 -->
<div>{{website.subTitle}}</div> <!-- 默认值显示 subTitle ,即父组件不传内容时,数据为自身组件data中的数据 -->
</slot>
</template>
<script>
export default {
data() {
return {
website: {
url: 'https://blog.csdn.net/banzhangshenlin?spm=1011.2124.3001.5343',
title: '半藏森林',
subTitle: '易忘知识点整理(持续更新)——VUE'
}
}
}
}
</script>
父组件
<!-- 父组件使用ScopedSlotDemo组件 -->
<ScopedSlotDemo>
<template v-slot="slotProps"><!-- slotProps为接受子组件上绑定的属性们 -->
{{slotProps.slotData.title}}<!-- slotProps.slotData为接受子组件的属性slotData的属性值,即数据website。这里会显示:半藏森林-->
</template>
</ScopedSlotDemo>
4.动态组件简单使用
例:父组件中使用Dynamic组件:<Dynamic/>
相当于 <component :is="customName"/>
。:is
后面为动态绑定的组件名称
<script>
import CustomVModel from './CustomVModel'
import NextTick from './NextTick'
import SlotDemo from './SlotDemo'
import ScopedSlotDemo from './ScopedSlotDemo'
import KeepAlive from './KeepAlive'
import Dynamic from './Dynamic'
export default {
components: {
NextTick,
Dynamic,
KeepAlive,
ScopedSlotDemo,
SlotDemo,
CustomVModel
},
data() {
return {
customName: "Dynamic",
}
}
}
</script>
5.简单的异步组件加载
加载的时候不像其他同步组件直接import
,而是通过该方式:当通过标签访问到该组件时,才会执行后面的箭头函数,从而加载该组件。之后组件可以使用v-if
来按需加载。<FormDemo v-if="showFormDemo"/>
components: {
FormDemo: () => import('../BaseUse/FormDemo'),
},
6.缓存组件使用 keep-alive
不用keep-alive
包住的话,每次切换子组件都会重新destroy
,mounte
;(让然也可以不用keep-alive,使用v-show,比较低级)
<keep-alive> <!-- tab 切换 -->
<KeepAliveStageA v-if="state === 'A'"/> <!-- 不是v-show,vue内部切换渲染 -->
<KeepAliveStageB v-if="state === 'B'"/>
<KeepAliveStageC v-if="state === 'C'"/>
</keep-alive>
7.mixin的使用
- 使用场景: 多个组件有相同的逻辑,抽离出来,一般用来简化代码或抽离共用的变量和方法
- 问题: 变量来源不明确,不利于阅读;多mixin可能会造成命名冲突;mixin和组件可能出现多对多的关系,复杂度较高
- 解决: 2.x只能避免,3.0提出Composition API旨在解决这些问题
使用:
<template>
<div>
<p>{{name}} {{major}} {{city}}</p>
<button @click="showName">显示姓名</button>
</div>
</template>
<script>
import myMixin from './mixin'
export default {
mixins: [myMixin], // 可以添加多个,会自动合并起来
data() {
return {
city2: '绍兴',
}
},
methods: {
},
mounted() {
}
}
</script>
mixin.js:
export default {
data() {
return {
city1: '杭州'//抽离的变量city1
}
},
methods: {
showName() {//抽离的方法
console.log(this.city2)
}
},
mounted() {
console.log('mixin mounted', this.city2)
}
}
8.关于Vuex的使用 看注释
https://vuex.vuejs.org/zh
安装: npm install vuex --save
(脚手架、npm)
创建一个store.js (这里不用module,太复杂,不易阅读)
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
//存储公共数据
state:{
name1:'helloVueX1',
name2: 'hellowVuex2',
name3: 'hellowVuex3',
},
//getters类似于computed,可以将state中的数据处理后返回,具有缓存特性,state数据变化,相关的getters中的属性值也会跟着变化
getters: {
NewName1(state, getters){//会将state以形参传入,也可以将getters作为第二个参数传入
if(!getters) {
return state + 'this is NewName1'
} else {
return getters.NewName2 + '和' + getters.NewName3;
}
},
NewName2(state, getters){
if(!getters) {
return state + 'this is NewName2'
}
},
NewName3(state, getters){
if(!getters) {
return state + 'this is NewName2'
}
},
},
//mutations只能进行同步任务,改变state中的值只能在这进行
mutations:{
edit1(state, payload){//会将state以形参传入,也可将载荷作为第二个参数传入
state.name1 = payload.name;
},
edit2(state, payload){
state.name2 = payload.name;
},
edit3(state, payload){
state.name3 = payload.name;
}
},
//将有关的异步任务放在这里执行,但是不能在这直接更改state的值,通过commit来调用mutations中的方法,来改变state中的值。
actions: {
edit1Sync({commit, state}, payload) {//接受一个与 store 实例具有相同方法和属性的 context 对象(如果是子模块则为子模块对象实例,所以context不是store 实例本身)。
//因此可以调用 context.commit,context.dispatch以及访问context.state 和 context.getters。
//也可以传入第二个参数载荷payload
//这里使用ES6语法糖,将commit, state从context结构了出来
return new Promise((resolve) => {//可以封装成promise并返回,可以调用之后.then和.catch
setTimeout(() => {
console.log(state.name1,payload.amount, '执行了 edit1Sync');
commit('edit1', {
name: 'edit1Sync',
})
resolve();
console.log(state.name1, '执行了 edit1Sync');
}, 1000);
});
},
edit2Sync({commit, state}) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(state.name2, '执行了 edit2Sync');
commit('edit2', {
name: 'edit2Sync',
})
console.log(state.name2, '执行了 edit2Sync');
resolve();
}, 1000);
});
},
//也可以使用async await语法 官网的例子
//假设 getData() 和 getOtherData() 返回的是 Promise
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成 这里说明,action中的方法可以组合使用
commit('gotOtherData', await getOtherData())
}
}
})
export default store
子实例代码:(不使用map辅助函数)
<template>
<!-- 读取state中的数据 name1 -->
<div>
<span>{{$store.state.name1}}</span>或
<span>{{name1}}</span><!-- 看computed -->
</div>
<!-- 读取getter中处理后的数据(具有缓存)-->
<div>
{{$store.getters.NewName1}}
</div><!-- 后面可以使用mapGetters辅助函数优化代码 -->
</template>
<script>
export default {
data() {
return {
}
},
computed: {
name1() {
return this.$store.state.name1//后面可以使用mapState辅助函数优化代码
}
},
methods: {
},
mounted() {
//调用store中的mutation中的方法
// 以载荷形式分发
this.$store.commit('edit1', {
name: '《I love vuex》'
});
//以对象形式分发
this.$store.commit({
type:'edit1',
name: '《I love vuex》',
})
//调用store中的actions中的方法
// 以载荷形式分发
this.$store.dispatch('edit1Sync', {
amount: 10
})
// 以对象形式分发
this.$store.dispatch({
type: 'edit1Sync',
amount: 10
});
}
}
</script>
子实例代码:(使用map辅助函数)
<template>
<!-- 读取state中的数据 name1 -->
<div>
<span>{{name1}}</span>
</div>
<!-- 读取getter中处理后的数据(具有缓存)-->
<div>
{{NewName1}}
</div>
</template>
<script>
// 在单独构建的版本中辅助函数 先引入
import { mapGetters, mapState, mapMutations, mapActions } from 'vuex'
export default {
data() {
return {
}
},
computed: {
//当映射的计算属性的名称与 state 的子节点名称相同时,可以给 mapState 传一个字符串数组
...mapState([//mapState(['name1', 'name2', 'name3']) 返回一个对象,里面每一个function都return state中对应的数据
//这里可以使用...对象展开运算符,将其打散至computed中。
'name1',//映射 this.name1 为 this.$store.state.name1
'name2',
'name3'
]),
...mapGetters(['NewName1','NewName2','NewName3'])
},
methods: {
...mapMutations(['edit1','edit2','edit3']),
...mapActions(['edit1Sync'])
},
mounted() {
//调用store中的mutation中的方法
this.edit1({
name: '《I love vuex1》',
});
//调用store中的actions中的方法
this.edit1Sync({
amount: 10
});
}
}
</script>
9.Vue Router 的使用
https://router.vuejs.org/zh/installation.html
起步:
①安装:
npm install vue-router
(npm)
vue add router
(Vue Cli)会覆盖原有App.vue
②基本模板:
router.js:
import Vue from 'vue'
import VueRouter from 'vue-router'//引入vue-router
Vue.use(VueRouter)//挂载
const routes = [
{
path: '/Cart/index',//路由地址
name: 'Cart',//组件名称
component: () => import(/* webpackChunkName: "about" */ '../components/Cart/index.vue')//这边为异步加载
}
]
const router = new VueRouter({
mode: 'history',//路由模式 设为history
base: process.env.BASE_URL,
routes
})
export default router
main.js引入router:
import router from './router'
new Vue({
render: h => h(App),
router,//将router 植入到 应用中
}).$mount('#app')
我们可以在任何组件内通过this.$router
访问路由器,也可以通过 this.$route
访问当前路由
基本使用:
①动态路由匹配
携带响应参数:比如参数id
const User = import('./components/user');
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
路径参数使用:
标记,当匹配到一个路由时,参数值会被设置到this.$route.params
,可以在每个组件内使用。
可设置多段“路径参数”,对应的值都会设置到$route.params
,例如:
模式 | 匹配路径 | $route.params |
---|---|---|
/user/:username | /user/evan | { username: ‘evan’ } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: ‘evan’, post_id: ‘123’ } |
备注:
1.如果用查询字符串传参,可以通过$route.query
来访问。其他$route的属性可以查看API:
https://router.vuejs.org/zh/api/#%E8%B7%AF%E7%94%B1%E5%AF%B9%E8%B1%A1
2.当使用路由参数时,例如:/user/10 导航到 /user/11,原来的组件实例会被复用。组件的生命周期钩子不会再被调用。(解决方法监听$route 或者 使用导航守卫:beforeRouteUpdate
)
②匹配模式
常规参数只会匹配被 /
分隔的 URL 片段中的字符。如果想匹配任意路径,我们可以使用通配符 (*
):
例:
{
// 会匹配所有路径
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}
含有通配符的路由应该放在最后。路由{ path: '*' }
通常用于客户端 404 错误。
备注:
1.当使用一个通配符时,$route.params
内会自动添加一个名为 pathMatch
参数。它包含了 URL 通过通配符被匹配的部分:
例:
// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
2.高级匹配模式、匹配优先级
(详见文档:https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#parameters)
③嵌套路由(一般项目使用路由嵌套或组件的引用来动态显示,而并非v-if v-else)
像#app
嵌套着<router-view></router-view>
,同样,组件内也可以嵌套自己的路由组件。
例如:在Cart路由组件中添加一个<router-view></router-view>
。
Cart组件:
<template>
<div id="ele1">..内容1..</div>
<div id="ele2">..内容2..</div>
<div id="ele3">
<router-view></router-view>
</div>
</template>
VueRouter JS文件使用children
配置:
//假设已经引入了 UserProfile和 UserPosts组件
const router = new VueRouter({
routes: [
{
path: '/cart/',
component: Cart,
children: [
{
// 当 /cart/profile 匹配成功,
// UserProfile 会被渲染在 Cart 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /cart/posts 匹配成功
// UserPosts 会被渲染在 Cart 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
如果没有匹配到子路由,但路由子组<router-view>
件想要渲染点东西,以提供一个 空的 子路由:
const router = new VueRouter({
routes: [
{
path: '/cart/',
component: Cart,
children: [
// 当 /cart 匹配成功,
// UserHome 会被渲染在 Cart 的 <router-view> 中
{ path: '', component: UserHome }
// ...其他子路由
]
}
]
})
④编程式导航 router.push(location, onComplete, onAbort)
onComplete, onAbort为3.2之后的回调入参,暂不做了解
声明式 | 编程式 |
---|---|
<router-link :to="..."> | router.push(...) |
// 字符串 /home
router.push('home')
// 对象 /home
router.push({ path: 'home' })
// 命名的路由 跳转至名为user的路由,携带/123
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
备注:!!
1.如果提供了 path
,params
会被忽略,但 query 并不属于这种情况。做法:提供路由的 name
或手写完整的带有参数的 path
2.如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2
),需要使用 beforeRouteUpdate
来响应这个变化 (比如抓取用户信息)。
3.其他router方法:
方法 | 使用 | 声明式 |
---|---|---|
router.replace(location, onComplete?, onAbort?) | 不会向 history 添加新记录,用法与router.push 一样 | <router-link :to="..." replace> |
router.go(n) | 类似于history.go(n) |
⑤命名路由 及 命名视图
实际项目中,使用该方法动态显示组件
命名路由:创建 Router 实例的时候,在 routes 配置中给某个路由设置名称name
属性
命名视图:(不常用)
一个路由地址,渲染多个路由组件
场景:如果访问某个路由地址,具有比较复杂的动态渲染视图,可以拆成多个<router-view></router-view>
,并为其设置name名字。注意:vue-cli中需要更改app.vue
app.vue:
<template>
<div id="app">
<div id="nav">
<router-view class="view one"/><!-- 没有设置名字,那么默认为 default -->
<router-view class="view two" name="a"/>
</div>
<router-view class="view three" name="b"/>
</div>
</template>
router js:
//加入Foo,Bar, Baz组件已经import
const router = new VueRouter({
routes: [
{
path: '/',
components: {//components 配置 (带上 s)
default: Foo,
a: Bar,
b: Baz
}
}
]
})
嵌套命名视图: (常用)
一个路由组件,渲染多个路由子组件。
例:路由组件 UserSettings
<template>
<div>
<h1>User Settings</h1>
<NavBar/>
<!-- /settings/emails 渲染 CompA -->
<!-- /settings/profile 渲染 CompB -->
<router-view/>
<!-- /settings/emails 不渲染 -->
<!-- /settings/profile 渲染 CompC -->
<router-view name="helper"/>
</div>
</template>
router js配置:children
//假设组件CompA、 CompB、CompC已经import
{
path: '/settings',
// 你也可以在顶级路由就配置命名视图
component: UserSettings,
children: [
{
path: 'emails',
component: CompA
},
{
path: 'profile',
components: {
default: CompB,
helper: CompC
}
}
]
}
⑥重定向和别名
当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b
重定向:redirect
1.目标为path
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
2.目标为命名路由
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
3.作为方法,动态返回重定向目标
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
别名:alias
/a 的别名是 /b,当用户访问 /a 或 /b时,匹配路由均为 /b
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
⑦路由组件传参
之前像访问路由“/user/:id”,组件内id只能通过this.$route.params.id
来获取,现在可以使用props
作为属性传给路由组件。
1.布尔模式
如果 props
被设置为 true,route.params
将会被设置为组件属性。
注意: 对于包含命名视图的路由,必须为每个命名视图添加 props
选项
router.js
//假设已经import了User和Sidebar路由组件
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: ['id']
来接收id。
2.对象模式
如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。
router.js
const router = new VueRouter({
routes: [
{
path: '/promotion/from-newsletter',
component: Promotion,
props: { newsletterPopup: '参数值1' }
}
]
})
路由组件内:props: ['newsletterPopup']
来接收newsletterPopup
。
3.函数模式
创建一个函数返回 props。可以将参数转换成另一种类型,将静态值与基于路由的值结合等等
router.js
const router = new VueRouter({
routes: [
{
path: '/search/:id',
component: SearchUser,
props: route => ({ query: route.query.q, userId: route.params.id})
}
]
})
路由组件内:props: ['query', 'userId']
来接收$route.query.q
及 $route.params.id
。
⑧导航守卫
不使用导航守卫的话,简单的业务场景可以使用watch
监听$route
对象来处理。
形参:
1.to
即将要进入的目标 路由对象
2.from
当前导航正要离开的路由
3.next
一定要调用该方法来resolve
这个钩子。
next()
:进行管道中的下一个钩子.next(false)
:中断当前的导航。next('/') 或者 next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
-next(error)
:导航会被终止且该错误会被传递给router.onError()
注册过的回调。
1.全局前置守卫 router.beforeEach
(在进入对应路由之前触发)
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
2.全局解析守卫 router.beforeResolve
(在所有组件内守卫和异步路由组件被解析之后触发)
const router = new VueRouter({ ... })
router.beforeResolve((to, from, next) => {
// ...
})
3.全局后置钩子(不是守卫) router.afterEach
(在进入对应路由之后触发)
这些钩子不会接受 next 函数也不会改变导航本身
router.afterEach((to, from) => {
// ...
})
4.路由独享的守卫
(在进入独享路由之前触发)
定义在routes路由配置项中,属性beforeEnter
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
5.组件内的守卫
beforeRouteEnter
无法访问this
(在进入组件路由之前触发)
beforeRouteUpdate
(在组件路由变化之后触发)
beforeRouteLeave
(在离开当前组件路由之前触发)
组件内代码:
export default {
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`
}
}
备注:
1.beforeRouteEnter
无法访问this
,解决:
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
2.只有beforeRouteEnter
支持next回调
6.完整的导航解析流程
1.导航被触发。
2.在失活的组件里调用 beforeRouteLeave
守卫。
3.调用全局的 beforeEach
守卫。
4.在重用的组件里调用 beforeRouteUpdate
守卫 (2.2+)。
5.在路由配置里调用 beforeEnter
。
6.解析异步路由组件。
7.在被激活的组件里调用 beforeRouteEnter
。
8.调用全局的 beforeResolve
守卫 (2.5+)。
9.导航被确认。
10.调用全局的 afterEach
钩子。
11触发 DOM 更新
。
12调用 beforeRouteEnter 守卫中传给 next 的回调函数
,创建好的组件实例会作为回调函数的参数传入。
如此循环
⑨路由元信息
配置 meta
字段
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
meta: { yaodehai: '姚老板1' }
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { yaodehai: '姚老板2' }
}
]
}
]
})
路由记录: routes
配置中的每个路由对象
访问meta: 一个路由匹配到的所有路由记录会暴露为 $route 对象
(还有在导航守卫中的路由对象) 的 $route.matched 数组
。可以遍历 $route.matched
来检查路由记录中的 meta 字段
⑩过渡动效
transition插件的使用: https://cn.vuejs.org/v2/guide/transitions.html
1.单个路由的过渡: 使用 <transition>
并设置 name
例:
路由组件Foo(通过路由访问该路由组件的时候,过渡特效显示)
<template>
<transition name="slide">
<div class="foo">..路由的html内容..</div>
</transition>
</template>
2.基于路由的动态过渡
父路由组件内:
<template>
<transition :name="transitionName">
<router-view></router-view>
</transition>
<transition :name="transitionName">
<router-view name="a"></router-view>
</transition>
</template>
<script>
export default {
data() {
return {
transitionName: ''
}
},
methed: {},
watch: {
'$route'(to, from) {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}
}
</script>
①①滚动行为 scrollBehavior
return { x: number, y: number }
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {//savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。
// return 期望滚动到哪个的位置 { x: number, y: number }
}
})
对于所有路由导航,简单地让页面滚动到顶部。
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
模拟“滚动到锚点”的行为:
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
}
}
}
还可以利用路由元信息更细颗粒度地控制滚动。
异步滚动:
scrollBehavior (to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 500)
})
}
平滑滚动: behavior
选项
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash,
behavior: 'smooth',
}
}
}