一、概述

Vue Router >>Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

用 Vue + Vue Router 创建单页应用非常简单:通过 Vue.js,我们已经用组件组成了我们的应用。当加入 Vue Router 时,我们需要做的就是将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们。下面是一个基本的例子:

二、初探

接下来,我们先通过一个示例,让大家了解路由的基础使用:

1. 构建项目

  1. $ npm init vite@latest vue-router-test -- --template vue-ts

2. 新建页面文件

在项目根目录,输入如下终端指令,创建页面文件:

  1. # 新建目录
  2. $ mkdir -p src/pages/Home
  3. $ mkdir -p src/pages/News
  4. # 新建文件
  5. $ cd > src/pages/Home/index.vue
  6. $ cd > src/pages/News/index.vue

并在页面文件中,填入如下内容:

  1. <script setup lang="ts"></script>
  2. <template>
  3. <p>This is XXX page.</p>
  4. </template>

提示:请将 XXX 替换为目录名。

3. 新建路由文件 & 安装路由

  1. # 新建路由文件
  2. $ mkdir -p src/router && cd > src/router/index.ts
  3. # 安装路由
  4. $ npm install vue-router@4

src/router/index.ts

  1. import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
  2. // → 引入组件模块
  3. import Home from '../pages/Home/index.vue';
  4. // → 定义路由
  5. const routes: Array<RouteRecordRaw> = [
  6. { path: '/', redirect: '/home' /** 重定向 */ },
  7. { path: '/home', component: Home },
  8. { path: '/news', component: () => import('../pages/News/index.vue') /** 懒加载组件模块 */ },
  9. ];
  10. // → 创建路由
  11. const router = createRouter({
  12. history: createWebHistory() /** 创建基于history模式的路由 */,
  13. routes,
  14. });
  15. // → 导出路由
  16. export default router;

4. 注册路由

src/main.ts

  1. import { createApp } from 'vue';
  2. import router from './router';
  3. import App from './App.vue';
  4. // → 创建应用实例
  5. const app = createApp(App);
  6. // → 注入路由
  7. app.use(router);
  8. // → 挂载
  9. app.mount('#app');

5. 配置路由链接

src/App.vue

  1. <script setup lang="ts"></script>
  2. <template>
  3. <!-- 导航 -->
  4. <nav>
  5. <router-link to="/home">Home</router-link>
  6. <span> / </span>
  7. <router-link to="/news">News</router-link>
  8. </nav>
  9. <!-- 路由视图 -->
  10. <router-view></router-view>
  11. </template>

6. 运行项目,查看效果

  1. $ npm run dev

演示效果:

router.gif

注意:

  1. 路由实例创建后需在vue实例中注册路由。
  2. 通过 <router-link> 实现路由动态切换,最终会被渲染成超链接。
  3. 通过 <router-view> 呈现路由切换后显示的组件内容。

三、基础

1. 路由模式

vue-router 在创建路由实例时,提供了2种路由有模式:hash / history

# hash history
呈现形式 http://localhost:3000/#/home http://localhost:3000/home
构造函数 createWebHashHistory() createWebHistory()
是否重载 否(Hash不会被包括在Http请求中)
回车刷新 无影响 404,需配置重定向

2. 路由嵌套 *

一些应用程序的 UI 由多层嵌套的组件组成,比如一些UI组件库的使用文档就是使用这种结构,我们称之为路由嵌套结构。在路由配置中,同样根据嵌套结构进行嵌套配置,比如:

  1. /web/html
  2. /web/css
  3. /web/js

/web 路由下,嵌套 /html/css/js 路由:

  1. {
  2. path: '/web',
  3. redirect: '/web/html',
  4. component: import('../pages/Web/index.vue'),
  5. children: [
  6. { path: 'html', component: import('../pages/Html/index.vue') },
  7. { path: 'css', component: import('../pages/CSS/index.vue') },
  8. { path: 'js', component: import('../pages/JavaScript/index.vue') },
  9. ],
  10. };

注意事项:

  • 配置路由时通过指定 children 属性来配置当前路由的子路由。
  • 子路由属于谁,<router-view /> 就写在谁下面。 *
  • 默认重定向,使用 redirect 属性。
  • 如果子路由在主页中,设置 path 属性时,直接 /子路由路径 即可。

3. 编程式的导航

除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

3.1. router.push() *

语法如下:

  1. router.push(location, onComplete?, onAbort?)

注意:在 Vue 实例内部的模板文件中,你可以通过 $router 访问路由实例。

示例代码:

  1. <script setup lang="ts">
  2. import { useRouter } from 'vue-router';
  3. // → 获取router实例
  4. const router = useRouter();
  5. // → 在事件函数中调用
  6. const onGoNews = () => {
  7. router.push('/news');
  8. };
  9. </script>
  10. <template>
  11. <p>This is Home page.</p>
  12. <button type="button" @click="onGoNews">Go News</button>
  13. <!-- 直接在模板中获取router实例 -->
  14. <button type="button" @click="$router.push('/news')">Go News</button>
  15. </template>

想要导航到不同的 URL,可以使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。

当你点击 <router-link> 时,内部会调用这个方法,所以点击 <router-link :to="..."> 相当于调用 router.push(...)

声明式 编程式
<router-link :to="..."> router.push(...)

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

  1. // → 字符串
  2. router.push('/home')
  3. // → 对象
  4. router.push({ path: '/home' })
  5. // → 命名的路由
  6. router.push({ name: 'home', params: { id: '123' }})
  7. // → 带查询参数,变成 /register?plan=private
  8. router.push({ path: '/register', query: { plan: 'private' }})

注意:如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path

  1. const username = 'muzili'
  2. // → 我们可以手动建立 url,但我们必须自己处理编码
  3. router.push(`/user/${username}`) // → /user/muzili
  4. // → 同样
  5. router.push({ path: `/user/${username}` }) // → /user/muzili
  6. // → 如果可能的话,使用 name 和 params 从自动 URL 编码中获益
  7. router.push({ name: 'user', params: { username } }) // → /user/muzili
  8. // params 不能与 path 一起使用
  9. router.push({ path: '/user', params: { username } }) // → /user

提示:同样的规则也适用于 <router-link\> 组件的 to 属性。

3.2. router.replace()

语法如下:

  1. router.replace(location, onComplete?, onAbort?)

router.push() 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

声明式 编程式
<router-link :to="..." replace> router.replace(...)

也可以直接在传递给 router.pushrouteLocation 中增加一个属性 replace: true

  1. router.push({ path: '/home', replace: true })
  2. // 相当于
  3. router.replace({ path: '/home' })

3.3. router.go()

这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)

  1. // → 向前移动一条记录,与 router.forward() 相同
  2. router.go(1)
  3. // → 返回一条记录,与router.back() 相同
  4. router.go(-1)
  5. // → 前进 3 条记录
  6. router.go(3)
  7. // → 如果没有那么多记录,静默失败
  8. router.go(-100)
  9. router.go(100)

4. 命名路由

除了 path 之外,你还可以为任何路由提供 name。这有以下优点:

  • 没有硬编码的 URL
  • params 的自动编码/解码。
  • 防止你在 url 中出现打字错误。
  • 绕过路径排序(如显示一个)
  1. {
  2. path: '/commodities/:kind',
  3. name: 'commodities',
  4. component: Commodities
  5. }

要链接到一个命名路由,可以给 router-linkto 属性传一个对象:

  1. <router-link :to="{name:'commodities', params:{ kind:'phone'}}">Commodities</router-link>

这跟代码调用 router.push() 是一回事:

  1. router.push({ name:'commodities', params:{ kind:'phone'}})

这两种方式都会把路由导航到 /commodities/phone 路径。

5. 命名视图

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

  1. <router-view class="view left-sidebar" name="LeftSidebar" />
  2. <router-view class="view main-content" />
  3. <router-view class="view right-sidebar" name="RightSidebar" />

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s):

  1. {
  2. path: '/',
  3. components: {
  4. default: Home,
  5. // LeftSidebar: LeftSidebar 的缩写
  6. LeftSidebar,
  7. // 它们与 <router-view> 上的 name 属性匹配
  8. RightSidebar,
  9. },
  10. },

6. 路由高亮效果

在路由链接选中时,会自动添加 router-link-active 类名:

router-link-active.png

你可以在 App.vue 中全局设置高亮时的颜色,如:

  1. .router-link-active {
  2. color: blue;
  3. }

7. 重定向/取别名

7.1. 重定向

重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b

  1. { path: '/a', redirect: '/b' }

重定向的目标也可以是一个命名的路由:

  1. { path: '/a', redirect: { name: 'foo' }}

甚至是一个方法,动态返回重定向目标:

  1. { path: '/a', redirect: to => {
  2. // → 方法接收 目标路由 作为参数
  3. // → return 重定向的 字符串路径/路径对象
  4. return { path: '/search', query: { q: to.params.searchText } }
  5. }}

注意:导航守卫 并没有应用在跳转路由上,而仅仅应用在其目标上。

7.2. 取别名

重定向 的意思是,当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,那么 别名 又是什么呢?

/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。

上面对应的路由配置为:

  1. { path: '/a', component: A, alias: '/b' }

别名的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。

8. 路由参数 *

设置路由跳转时,可以进行参数传递,如下所示:

8.1. 参数以 / 传递(path

@指定传递参数:

  1. {
  2. // → 动态路径参数 以冒号开头
  3. // → name、course 为传递数据
  4. path: '/news/:name/:course',
  5. name: 'news',
  6. component: News
  7. }

@设置传递数据:

  1. <!-- 方法1 ->
  2. <router-link to="/news/muzili/web">News</router-link>
  3. <!-- 方法2 ->
  4. <router-link :to="{name:'news', params:{name:'muzili', course:'web'}}">News</router-link>

@获取传递数据:

  1. <script setup lang="ts">
  2. import { useRoute } from 'vue-router';
  3. const route = useRoute();
  4. // 1. 脚本中访问
  5. console.log(route.params); // → {name: 'muzili', course: 'web'}
  6. </script>
  7. <template>
  8. <!-- 2. 模板中访问 -->
  9. <p>{{ $route.params.name }} - {{ $route.params.course }}</p>
  10. </template>

注意:是 $route.params 而不是 $router.params,后面不要加 s

8.2. 参数以 ? 传递(query

我们常见的参数传递是通过 location.search 传递的,即在 URI 中将参数 以 ? 隔开进行传递,vue中要使用此方式传递设置如下:

@指定参数传递:

使用query传参,路由配置的时候path不用带参数

@设置传递参数:

  1. <router-link to="/news?name=muzili&course=web">News</router-link>
  2. <router-link :to="{path:'/news', query:{name:'muzili', course:'web'">}}">News</router-link>

@获取传递数据:

  1. <script setup lang="ts">
  2. import { useRoute } from 'vue-router';
  3. const route = useRoute();
  4. // 1. 脚本中访问
  5. console.log(route.query); // → {name: 'muzili', course: 'web'}
  6. </script>
  7. <template>
  8. <!-- 2. 模板中访问 -->
  9. <p>{{ $route.query.name }} - {{ $route.query.course }}</p>
  10. </template>

8.3. Path vs Query

# 类型 路由配置文件 传递 访问
**Path** / /path/:params name
/ params
$route.params.xxx
**Query** ? 无需特殊配置 path
/ query
$route.query.x
xx

9. 匹配 404 Not Fount

  1. { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound }

10. v-slot

<router-link> 通过一个作用域插槽暴露底层的定制能力。这是一个更高阶的 API,主要面向库作者,但也可以为开发者提供便利,大多数情况下用在一个类似 NavLink 这样的组件里。

  1. <router-link
  2. to="/about"
  3. custom
  4. v-slot="{ href, route, navigate, isActive, isExactActive }"
  5. >
  6. <NavLink :active="isActive" :href="href" @click="navigate">
  7. {{ route.fullPath }}
  8. </NavLink>
  9. </router-link>
  • href:解析后的 URL。将会作为一个 <a> 元素的 href 属性。如果什么都没提供,则它会包含 base
  • route:解析后的规范化的地址。
  • navigate:触发导航的函数。 会在必要时自动阻止事件,和 router-link 一样。例如:ctrl 或者 cmd + 点击仍然会被 navigate 忽略。
  • isActive:如果需要应用 active class,则为 true。允许应用一个任意的 class。
  • isExactActive:如果需要应用 exact active class,则为 true。允许应用一个任意的 class。

四、进阶

1. 导航守卫

导航:表示路由正在发生改变

守卫: 防守护卫,类似于关卡,决定是否放行

正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的单个路由独享的,或者 组件级 的。

守卫参数:

每个守卫方法接收三个参数:

返回值:

各个守卫可以返回的值如下:

  • false: 取消当前的导航(不执行跳转)
  • 路由地址:多用于重定向,比如在守卫中判断登录状态,如果没有登录则重定向到登录页
  • undefine / true:导航有效,放行并调用下一个导航守卫。

2.1. 全局守卫

@前置守卫

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中

  1. // -- 前置守卫
  2. router.beforeEach(async (to, from) => {
  3. console.log('__beforeEach__');
  4. // → 检测登录状态
  5. if (to.path !== '/login' && !localStorage.getItem('loginStatus')) {
  6. return '/login';
  7. }
  8. });

@解析守卫

和前置守卫类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

  1. // -- 解析守卫
  2. router.beforeResolve(async (to, from) => {
  3. console.log('__beforeResolve__');
  4. });

提示:解析守卫是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。

@后置钩子

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

  1. // -- 后置钩子
  2. router.afterEach(async (to, from) => {
  3. console.log('__afterEach__');
  4. document.title = to.meta.title as string;
  5. });

提示:后置钩子对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用.

2.2. 路由独享守卫

你可以直接在路由配置上定义 beforeEnter 守卫:

  1. {
  2. path: '/home',
  3. component: Home,
  4. beforeEnter: (to, from) => {
  5. /** ... */
  6. },
  7. },

2.3. 组件内守卫

最后,你可以在路由组件内直接定义路由导航守卫(传递给路由配置的)

  • beforeRouteEnter
  • `beforeRouteUpdate
  • beforeRouteLeave
  1. const UserDetails = {
  2. template: `...`,
  3. beforeRouteEnter(to, from) {
  4. // 在渲染该组件的对应路由被验证前调用
  5. // 不能获取组件实例 `this` !
  6. // 因为当守卫执行时,组件实例还没被创建!
  7. },
  8. beforeRouteUpdate(to, from) {
  9. // 在当前路由改变,但是该组件被复用时调用
  10. // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
  11. // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
  12. // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  13. },
  14. beforeRouteLeave(to, from) {
  15. // 在导航离开渲染该组件的对应路由时调用
  16. // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  17. },
  18. };

beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数:

  1. beforeRouteEnter (to, from, next) {
  2. next(vm => {
  3. // → 通过 vm 访问组件实例
  4. })
  5. }

注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdatebeforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了:

  1. beforeRouteUpdate (to, from) {
  2. // just use `this`
  3. this.name = to.params.name
  4. }

这个 离开守卫 通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消。

  1. beforeRouteLeave (to, from) {
  2. const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  3. if (!answer) return false
  4. }

提示:在組合式API <script setup> 中,你可以通过 onBeforeRouteUpdateonBeforeRouteLeave 分别添加 updateleave 守卫。

2.4. 完整的导航解析流程

  • → 导航被触发
  • → 在失活的组件里调用 beforeRouteLeave 守卫
  • → 调用全局的 beforeEach 守卫
  • → 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)
  • → 在路由配置里调用 beforeEnter
  • → 解析异步路由组件
  • → 在被激活的组件里调用 beforeRouteEnter
  • → 调用全局的 beforeResolve 守卫(2.5+)
  • → 导航被确认
  • → 调用全局的 afterEach 钩子
  • → 触发 DOM 更新
  • → 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入

2. 路由元信息

定义路由的时候可以配置 meta 字段,用于添加关于路由的一些额外信息,比如页面标题:

  1. {
  2. path: '/home',
  3. name: 'home',
  4. component: Home,
  5. meta: { title: '首页' },
  6. }

那么如何访问这个 meta 字段呢?

  1. // -- 后置钩子
  2. router.afterEach(async (to, from) => {
  3. console.log('__afterEach__');
  4. document.title = to.meta.title as string;
  5. });

3. 过渡动效

想要在你的路径组件上使用转场,并对导航进行动画处理,你需要使用 v-slot API >>

  1. <router-view v-slot="{ Component }">
  2. <transition
  3. enter-active-class="animate__animated animate__fadeInRight"
  4. leave-active-class="animate__animated animate__fadeOutLeft"
  5. >
  6. <component :is="Component" />
  7. </transition>
  8. </router-view>

提示:上述示例中,通过 animate.css >> 来定义过渡效果。

!异常处理

提示:

  1. Component inside <Transition> renders non-element root node that cannot be animated.

原因:由于 <Transition> 只能应用在 单节点元素 上,而我们在构造组件模块时,直接在 <template> 中写布局,如:

  1. <template>
  2. <p>This is Home page.</p>
  3. <p>Hello, vue.js!</p>
  4. </template>

在渲染之后,实际呈现的结构为:

  1. <transition>
  2. <p>This is Home page.</p>
  3. <p>Hello, vue.js!</p>
  4. </transition>

所以抛出了异常,解决方案,我们只需要在组件外部包裹一层标签即可,如:

  1. <template>
  2. <div class="page">
  3. <p>This is Home page.</p>
  4. <p>Hello, vue.js!</p>
  5. </div>
  6. </template>

1.1. 单个路由的过渡

上面的用法会对所有的路由使用相同的过渡。如果你想让每个路由的组件有不同的过渡,你可以将元信息和动态的 name 结合在一起,放在 <transition> 上:

  1. const routes = [
  2. {
  3. path: '/custom-transition',
  4. component: PanelLeft,
  5. meta: { transition: 'slide-left' },
  6. },
  7. {
  8. path: '/other-transition',
  9. component: PanelRight,
  10. meta: { transition: 'slide-right' },
  11. },
  12. ]
  1. <router-view v-slot="{ Component, route }">
  2. <!-- 使用任何自定义过渡和回退到 fade -->
  3. <transition :name="route.meta.transition || 'fade'">
  4. <component :is="Component" />
  5. </transition>
  6. </router-view>

1.2. 基于路由的动态过渡

还可以根据目标路由和当前路由之间的关系,动态地确定使用的过渡。使用和刚才非常相似的片段:

  1. <!-- 使用动态过渡名称 -->
  2. <router-view v-slot="{ Component, route }">
  3. <transition :name="route.meta.transition">
  4. <component :is="Component" />
  5. </transition>
  6. </router-view>

我们可以添加一个 导航后置钩子(**afterEach**,根据路径的深度动态添加信息到 meta 字段。

  1. router.afterEach((to, from) => {
  2. const toDepth = to.path.split('/').length
  3. const fromDepth = from.path.split('/').length
  4. to.meta.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
  5. })

4. 数据获取 *

有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示 加载中… 之类的指示。
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。

从技术角度讲,两种方式都不错 —— 就看你想要的用户体验是哪种。

@导航完成之后获取

  1. created() {
  2. // → 显示加载中,提升用户体验
  3. // → ajax
  4. }

@导航完成之前获取

  1. beforeRouteEnter() {
  2. // => ajax
  3. }

5. 滚动行为

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

注意: 这个功能只在支持 history.pushState 的浏览器中可用。

当创建一个 Router 实例,你可以提供一个 scrollBehavior 方法:

  1. const router = createRouter({
  2. history: createWebHashHistory(),
  3. routes: [...],
  4. scrollBehavior (to, from, savedPosition) {
  5. // return 期望滚动到哪个的位置
  6. }
  7. })

scrollBehavior 方法接收 tofrom 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。

该函数可以返回一个 [ScrollToOptions](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions) 位置对象:

  • { x: number, y: number }
  • { selector: string, offset? : { x: number, y: number }} (offset 只在 2.6.0+ 支持)

如果返回一个 falsy (译者注:falsy 不是 false,参考这里)的值,或者是一个空对象,那么不会发生滚动。

  1. scrollBehavior (to, from, savedPosition) {
  2. return { x: 0, y: 0 }
  3. }

对于所有路由导航,简单地让页面滚动到顶部。

返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:

  1. scrollBehavior (to, from, savedPosition) {
  2. if (savedPosition) {
  3. return savedPosition
  4. } else {
  5. return { x: 0, y: 0 }
  6. }
  7. }

如果你要模拟“滚动到锚点”的行为:

  1. scrollBehavior (to, from, savedPosition) {
  2. if (to.hash) {
  3. return {
  4. selector: to.hash
  5. }
  6. }
  7. }