Vue-Router 官网速通

前言:参考Vue Router,本文档例子使用 vue3 语法。

一:介绍

1. 什么是 Vue Router

是 Vue.js 的官方路由。功能包括:1. 路由映射。2. 动态路由。3. 路由配置。4. 路由参数。5. 过渡效果。6. 导航控制。7. CSS 类链接。8. HTML5 模式。9. 滚动行为。10. URL 编码

2. 安装

对于一个现有使用 JS 包管理的项目,添加 Vue Router 依赖:

npm install vue-router@4
# or
yarn add vue-router@4
# or
pnpm add vue-router@4

通过 create-vue 脚手架创建一个基于 Vite 的新项目,加入 Vue Router 的选项:

npm create vue@latest
# or
yarn create vue
# or
pnpm create vue

二:基础

1. 入门

router-view 显示与 URL 对应的组件。可以放在任何地方,以适应布局。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><script src="https://unpkg.com/vue@3"></script><script src="https://unpkg.com/vue-router@4"></script><div id="app"><h1>Hello App!</h1><p><!--使用 router-link 组件进行导航 --><!--通过传递 `to` 来指定链接 --><!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签--><router-link to="/">Go to Home</router-link><router-link to="/about">Go to About</router-link></p><!-- 路由出口 --><!-- 路由匹配到的组件将渲染在这里 --><router-view></router-view></div></body><script>// 1. 定义路由组件.const Home = { template: "<div>Home</div>" };const About = { template: "<div>About</div>" };// 2. 定义一些路由// 每个路由映射一个组件。const routes = [{ path: "/", component: Home },{ path: "/about", component: About },];// 3. 创建路由实例并传递 `routes` 配置// 可以在这里输入更多的配置const router = VueRouter.createRouter({// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。history: VueRouter.createWebHashHistory(),routes, // `routes: routes` 的缩写});// 5. 创建并挂载根实例const app = Vue.createApp({});//确保 _use_ 路由实例使//整个应用支持路由。app.use(router);app.mount("#app");</script>
</html>

在 Vue3 中通过调用 useRouter 和 useRoute 函数创建实例来访问。

<script setup lang="ts">import { useRouter, useRoute } from "vue-router";const router = useRouter();const route = useRoute();router.push("/"); // 跳转route.params; // 参数
</script>

2. 动态路由匹配

多个匹配路由映射到同一组件,通过一个动态字段来实现,称之为路径参数,路径参数用冒号 : 表示。如下 /users/johnny 和 /users/jolyne 会映射到同一个路由。

const User = {template: "<div>User</div>",
};// 这些都会传递给 `createRouter`
const routes = [// 动态字段以冒号开始{ path: "/users/:id", component: User },
];

通过 this.$route.params 来访问 URL 上的参数:

const User = {template: "<div>User {{ $route.params.id }}</div>",
};

路由可以设置多个参数,在 $route.params 上相对应:

const User = {template: "<div>User</div>",
};// 传递给 `createRouter`
const routes = [// 动态字段以冒号开始{ path: "/users/:username/posts/:postId", component: User },
];// 匹配路由:/users/eduardo/posts/123
// $route.params:{ username: 'eduardo', postId: '123' }

2.1 响应路由参数的变化

同一路由的不同参数跳转,因为映射的是相同组件,所以复用组件显得更高效,但生命周期函数就不会被调用,比如从 /users/johnny 导航到 /users/jolyne。要对同一路由的参数做出响应,需要用 watch 监听:

// vue3
import { watchEffect } from "vue";
watchEffect(() => {console.log(1, route.params, route.query);
});

2.2 捕获所有路由或 404 Not found 路由

想匹配任意路径,使用路径参数正则表达式,在路径参数后面的括号中加入正则表达式 :

const routes = [// 将匹配所有内容并将其放在 `$route.params.pathMatch` 下{ path: "/:pathMatch(.*)*", name: "NotFound", component: NotFound },// 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下{ path: "/user-:afterUser(.*)", component: UserGeneric },
];

3. 路由的匹配语法

大多数应用都会使用 /about 静态路由和 /users/:userId 动态路由,但是 Vue Router 提供了更多的方式!

3.1 在参数中自定义正则

两个路由 /:orderId 和 /:productName,会匹配完全相同的 URL,需要一种方法来区分他们,最简单的方法是添加一个静态部分来区分:

const routes = [// 匹配 /o/3549{ path: "/o/:orderId" },// 匹配 /p/books{ path: "/p/:productName" },
];

如果不想添加静态部分,也可以添加正则,orderId 总是一个数字,productName 可以是任何东西。以在括号中为参数指定正则,现在,/25 将匹配 /:orderId,其他情况匹配 /:productName。

const routes = [// /:orderId -> 仅匹配数字{ path: "/:orderId(\\d+)" },// /:productName -> 匹配其他任何内容{ path: "/:productName" },
];

TIP:确保转义反斜杠( \ ),就像对 \d (变成\\d)所做的那样,在 JS 中实际传递字符串中的反斜杠字符。

3.2 可重复的参数

匹配 /first/second/third 路由,应该用 \*(0 个或多个)和 +(1 个或多个)将参数标记为可重复:

const routes = [// /:chapters ->  匹配 /one, /one/two, /one/two/three, 等{ path: "/:chapters+" },// /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等{ path: "/:chapters*" },
];

提供了参数数组,而不是字符串,并且在使用命名路由时也需要你传递一个数组:

// 给定 { path: '/:chapters*', name: 'chapters' },
router.resolve({ name: "chapters", params: { chapters: [] } }).href;
// 产生 /
router.resolve({ name: "chapters", params: { chapters: ["a", "b"] } }).href;
// 产生 /a/b// 给定 { path: '/:chapters+', name: 'chapters' },
router.resolve({ name: "chapters", params: { chapters: [] } }).href;
// 抛出错误,因为 `chapters` 为空

可以与正则结合使用:

const routes = [// 仅匹配数字// 匹配 /1, /1/2, 等{ path: "/:chapters(\\d+)+" },// 匹配 /, /1, /1/2, 等{ path: "/:chapters(\\d+)*" },
];

3.3 Sensitive 与 strict 路由配置

默认情况下,所有路由是不区分大小写和带尾部斜线的路由的。例如,路由 /users 将匹配 /users、/users/、/Users/。通过 strict 和 sensitive 选项来修改。

const router = createRouter({history: createWebHistory(),routes: [// 将匹配 /users/posva 而非:// - /users/posva/ 当 strict: true,尾部不能有斜线// - /Users/posva 当 sensitive: true,路由区分大小写{ path: "/users/:id", sensitive: true },// 将匹配 /users, /Users, 以及 /users/42 而非 /users/ 或 /users/42/{ path: "/users/:id?" },],strict: true, // applies to all routes
});

3.4 可选参数

通过使用 ? 修饰符(0 个或 1 个)将一个参数标为可选:

const routes = [// 匹配 /users 和 /users/posva{ path: "/users/:userId?" },// 匹配 /users 和 /users/42{ path: "/users/:userId(\\d+)?" },
];

4. 嵌套路由

顶层的 router-view 渲染顶层路由匹配的组件。一个被渲染的组件也可以包含嵌套的 <router-view>:

const User = {template: `<div class="user"><h2>User {{ $route.params.id }}</h2><router-view></router-view></div>`,
};

要将组件渲染到嵌套的 router-view 中,需要在路由中配置 children,以 / 开头的嵌套路径将被视为根路径。不必使用嵌套的 URL:

const routes = [{path: "/user/:id",component: User,children: [{// 当 /user/:id/profile 匹配成功// UserProfile 将被渲染到 User 的 <router-view> 内部path: "profile",component: UserProfile,},{// 当 /posts 匹配成功// UserPosts 将被渲染到 User 的 <router-view> 内部path: "/posts",component: UserPosts,},],},
];

如上配置,访问 /user/eduardo ,User 的 router-view 什么都不展示,因为没有匹配到嵌套路由。可以提供一个空的嵌套路径,来展示一些东西:

const routes = [{path: "/user/:id",component: User,children: [// 当 /user/:id 匹配成功// UserHome 将被渲染到 User 的 <router-view> 内部{ path: "", component: UserHome },// ...其他子路由],},
];

5. 编程式导航

除了 <router-link> 定义导航链接,还可以使用 router 的实例方法来导航。router.push 返回的是 Promise。

5.1 router.push 导航

使用 router.push 方法,会向 history 栈添加一个新的记录,所以,当点击浏览器后退按钮时,会回到之前的 URL。当点击 <router-link> 时,内部会调用这个方法,所以点击 <router-link :to="..."> 相当于调用 router.push(...) :

// 字符串路径
router.push("/users/eduardo");// 带有路径的对象
router.push({ path: "/users/eduardo" });// 如果路由没有命名username参数,会报错,path: /user/:username?
router.push({ name: "user", params: { username: "eduardo" } });// 带查询参数,结果是 /register?plan=private
router.push({ path: "/register", query: { plan: "private" } });// 带 hash,结果是 /about#team
router.push({ path: "/about", hash: "#team" });

使用 path 跳转 params 参数会无效,query 参数适用 path 或 name 跳转:

const username = "eduardo";
router.push(`/user/${username}`); // -> /user/eduardo
router.push({ path: `/user/${username}` }); // -> /user/eduardo
router.push({ name: "user", params: { username } }); // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: "/user", params: { username } }); // -> /user

5.2 替换当前位置

不会向 history 添加新记录,直接取代了当前的路由。用法:

<script setup lang="ts">
import { useRouter } from "vue-router";const router = useRouter();
router.push({ path: "/home", replace: true });
// 相当于
router.replace({ path: "/home" });
</script><template><router-link :to="..." replace>
</template>

5.3 横跨历史

router.go(n) 用一个整数作为参数,表示在历史堆栈中前进或后退多少步,类似于 window.history.go(n)。

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

6. 命名路由

使用 name 命名路由。优点:1. 没有硬编码的 URL。2. params 的自动编码/解码。3. 防止你在 url 中出现打字错误。4. 绕过路径排序(如显示一个)。

const routes = [{path: "/user/:username",name: "user",component: User,},
];

命名路由,通过 params 传递路由参数,如下路由将导航到 /user/erina。

<script setup lang="ts">
import { useRouter } from "vue-router";const router = useRouter();const handleGo = () =>router.push({ name: "user", params: { username: "erina" } });
</script><template><router-link :to="{ name: 'user', params: { username: 'erina' } }">User</router-link><div class="home" @click="handleGo">home</div>
</template>

7. 命名视图

设置多个同级<router-view>,通过 name 属性确定渲染组件,默认 default:

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

多个视图就需要多个组件。正确配置 components (带上 s):

const router = createRouter({history: createWebHistory(),routes: [{path: "/",components: {default: Home,// LeftSidebar: LeftSidebar 的缩写LeftSidebar,// 它们与 `<router-view>` 上的 `name` 属性匹配RightSidebar,},},],
});

8. 重定向和别名

8.1 重定向

通过配置 redirect,实现路由重定向,当有 redirect 属性时,可以省略 component ,因为没有被访问,如下从 /home 重定向到 /:

const routes = [{ path: "/home", redirect: "/" }];

也可以使用 name 重定向:

const routes = [{ path: "/home", redirect: { name: "homepage" } }];

使用一个方法,动态返回重定向目标:

const routes = [{// /search/screens -> /search?q=screenspath: "/search/:searchText",redirect: (to) => {// 方法接收目标路由作为参数// return 重定向的字符串路径/路径对象return { path: "/search", query: { q: to.params.searchText } };},},{path: "/search",// ...},
];

8.2 别名

访问别名和访问路由一个效果,使用 alias 声明别名,以 / 开头,并可以使用数组提供多个别名:

const routes = [{path: "/",component: Homepage,alias: "/home",},{path: "/users",component: UsersLayout,children: [// 为这 3 个 URL 呈现 UserList// - /users// - /users/list// - /people{ path: "", component: UserList, alias: ["/people", "list"] },],},
];

9. 路由组件传参

在组件中使用 $route 会与路由紧密耦合,限制了组件的灵活性,因为它只能用于特定的 URL。可以通过 props 配置来解除这种行为:

const User = {template: "<div>User {{ $route.params.id }}</div>",
};
const routes = [{ path: "/user/:id", component: User }];// 替换成
const User = {// 请确保添加一个与路由参数完全相同的 prop 名props: ["id"],template: "<div>User {{ id }}</div>",
};
const routes = [{ path: "/user/:id", component: User, props: true }];

9.1 命名视图

对于有命名视图的路由,必须为每个命名视图定义 props 配置:

const routes = [{path: "/user/:id",components: { default: User, sidebar: Sidebar },props: { default: true, sidebar: false },},
];

9.2 函数模式

创建一个返回 props 的函数。可以将参数转换为其他类型:

const routes = [{path: "/demo/:id?",name: "demo",component: Demo,props: (route) => {return { id: +route.params.id };},},
];

9.3 Via RouterView

<RouterView v-slot="{ Component }"><component:is="Component"view-prop="value"/>
</RouterView>

10. 不同的历史模式

在创建路由器实例时,history 配置可以选择不同的历史模式。

10.1 Hash 模式

hash 模式是用 createWebHashHistory() 创建的,会在 url 后添加哈希字符(#),对 SEO 不友好,不推荐:

import { createRouter, createWebHashHistory } from "vue-router";const router = createRouter({// /home#/history: createWebHashHistory(),routes: [//...],
});

10.2 Memory 模式

Memory 模式是用 createMemoryHistory() 创建的,适合 Node 环境和 SSR,不推荐:

import { createRouter, createMemoryHistory } from "vue-router";
const router = createRouter({history: createMemoryHistory(),routes: [//...],
});

10.3 HTML5 模式

用 createWebHistory() 创建 HTML5 模式,URL 会看起来很 "正常",推荐使用这个模式:

import { createRouter, createWebHistory } from "vue-router";const router = createRouter({history: createWebHistory(),routes: [//...],
});

三. 进阶

1. 导航守卫

导航守卫通过跳转或取消的方式守卫导航。路由导航包括:全局,单个路由,组件级。

1.1 全局前置守卫

使用 router.beforeEach 注册全局前置守卫,导航跳转前触发,接受两个参数,to 即将进入的路由,from 即将离开的路由,返回 false 是取消当前跳转,返回一个路由是跳转到返回路由中:

const router = createRouter({ ... })router.beforeEach(async (to, from) => {if (// 检查用户是否已登录!isAuthenticated &&// ❗️ 避免无限重定向to.name !== "Login") {// 将用户重定向到登录页面return { name: "Login" };}
});

1.2 全局解析守卫

使用 router.beforeResolve 注册全局解析守卫。解析守卫在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。是获取数据或执行任何其他操作(如用户无法进入页面时希望避免执行的操作)的理想位置。例如:确保用户可以访问自定义 meta 属性 requiresCamera 的路由:

router.beforeResolve(async (to) => {if (to.meta.requiresCamera) {try {await askForCameraPermission();} catch (error) {if (error instanceof NotAllowedError) {// ... 处理错误,然后取消导航return false;} else {// 意料之外的错误,取消导航并把错误传给全局处理器throw error;}}}
});

1.3 全局后置钩子

使用 router.afterEach 注册全局后置钩子,后置钩子不会改变导航本身,可以用于分析、更改页面标题、声明页面等辅助功能以及许多其他事情:

router.afterEach((to, from) => {sendToAnalytics(to.fullPath);
});

1.4 在守卫内的全局注入

使用 inject() 方法在导航守卫内注入全局属性。app.provide() 中所有内容都可以在 router.beforeEach()、router.beforeResolve()、router.afterEach() 内获取到:

// main.ts
const app = createApp(App);
app.provide("global", "hello injections");// router.ts or main.ts
router.beforeEach((to, from) => {const global = inject("global"); // 'hello injections'// a pinia storeconst userStore = useAuthStore();// ...
});

1.5 路由独享的守卫

在路由配置上定义 beforeEnter 守卫,在进入路由时触发,不会在 params、query、hash 改变时触发:

const routes = [{path: "/users/:id",component: UserDetails,beforeEnter: (to, from) => {// reject the navigationreturn false;},},
];

也可以将函数数组传给 beforeEnter,这在为不同的路由重用守卫时很有用:

function removeQueryParams(to) {if (Object.keys(to.query).length)return { path: to.path, query: {}, hash: to.hash };
}function removeHash(to) {if (to.hash) return { path: to.path, query: to.query, hash: "" };
}const routes = [{path: "/users/:id",component: UserDetails,beforeEnter: [removeQueryParams, removeHash],},{path: "/about",component: UserDetails,beforeEnter: [removeQueryParams],},
];

1.6 组件内的守卫

在路由组件内直接定义路由导航守卫,onBeforeRouteLeave 离开组件时调用,onBeforeRouteUpdate 路由更新时调用

import { onBeforeRouteLeave, onBeforeRouteUpdate } from "vue-router";onBeforeRouteLeave((to, from) => {});
onBeforeRouteUpdate((to, from) => {});

1.7 完整的导航解析流程

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

2. 路由元信息

使用 meta 定义路由元信息,可以存放任何信息,如过渡名,路由访问权限等,可以在导航守卫被访问到:

const routes = [{path: '/posts',component: PostsLayout,children: [{path: 'new',component: PostsNew,// 只有经过身份验证的用户才能创建帖子meta: { requiresAuth: true },},{path: ':id',component: PostsDetail// 任何人都可以阅读文章meta: { requiresAuth: false },}]}
]

怎么访问 meta 字段呢?

router.beforeEach((to, from) => {// 而不是去检查每条路由记录// to.matched.some(record => record.meta.requiresAuth)if (to.meta.requiresAuth && !auth.isLoggedIn()) {// 此路由需要授权,请检查是否已登录// 如果没有,则重定向到登录页面return {path: "/login",// 保存我们所在的位置,以便以后再来query: { redirect: to.fullPath },};}
});

2.1 TypeScript

可以继承来自 vue-router 中的 RouteMeta 来为 meta 字段添加类型:

// 添加到一个 `.d.ts` 文件中。将这个文件包含在项目的 `tsconfig.json` 中的 "file" 字段内。
import "vue-router";// 为了确保这个文件被当作一个模块,添加至少一个 `export` 声明
export {};declare module "vue-router" {interface RouteMeta {// 是可选的isAdmin?: boolean;// 每个路由都必须声明requiresAuth: boolean;}
}

3. 数据获取

获取数据可以导航后在组件生命周期获取,也可以在导航前,在路由守卫获取。

3.1 导航完成后获取数据

马上导航和渲染组件,在 created 钩子中获取数据。

<script setup lang="ts">import { useRoute } from "vue-router";import { watchEffect, reactive } from "vue";const route = useRoute();watchEffect(() => {fetchData(route.params.id);});const post = reactive(null);const fetchData = (id) => {getPost(id, (err, post) => {post = post;});};
</script><template><div class="post"><div class="content"><h2>{{ post.title }}</h2><p>{{ post.body }}</p></div></div>
</template>

4. 组合式 API

4.1 在 setup 中访问路由和当前路由

不能直接访问 this.$router 或 this.$route。需使用 useRouter 和 useRoute 函数,但在模板中可以访问 $router 和 $route:

<script setup lang="ts">import { useRoute, useRouter } from "vue-router";const route = useRoute();const router = useRouter();const handleGo = () => {router.push({ name: "Home", query: { ...route.query } });};
</script><template><div class="demo" @click="handleGo">{{ $route.params }}</div>
</template>

4.2 导航守卫

onBeforeRouteLeave,onBeforeRouteUpdate 离开和更新守卫:

<script setup lang="ts">import {useRouter,onBeforeRouteLeave,onBeforeRouteUpdate,} from "vue-router";const router = useRouter();onBeforeRouteUpdate(() => {console.log("更新路由");});onBeforeRouteLeave(() => {console.log("离开路由");});const changeRoute = () => {router.push("/demo/2");};
</script><template><div class="demo" @click="changeRoute">改变路由</div>
</template>

4.3 useLink

利用 RouterLink 构建自己的 RouterLink 组件或生成自定义链接:

<script setup lang="ts">import { RouterLink } from "vue-router";import { computed } from "vue";const props = defineProps({// @ts-ignore...RouterLink.props,inactiveClass: String,_target: {type: String,default: "_self",},});const isExternalLink = computed(() => typeof props.to === "string" && props.to.startsWith("http"));
</script><template><a v-if="isExternalLink" :href="to" :target="_target"><slot /></a><router-link v-else v-bind="$props" :to="$props.to"><slot /></router-link>
</template>

5. RouterView 插槽

RotuerView 暴露插槽,用来渲染路由组件:

<router-view v-slot="{ Component }"><component :is="Component" />
</router-view><!-- 等价于 -->
<router-view />

5.1 KeepAlive & Transition

KeepAlive 是保持路由组件活跃,而不是 RouterView 本身。所以需要将 KeepAlive 放置在插槽内:

<router-view v-slot="{ Component }"><keep-alive><component :is="Component" /></keep-alive>
</router-view>

同理 Transition 实现路由组件之间切换的过渡效果:

<router-view v-slot="{ Component }"><transition><component :is="Component" /></transition>
</router-view>

5.2 模板引用

可以将模板引用放置在路由组件上:

<router-view v-slot="{ Component }"><component :is="Component" ref="mainContent" />
</router-view>

6 过渡动效

要实现路由组件切换需要使用 <RouterView> 插槽:

<router-view v-slot="{ Component }"><transition name="fade"><component :is="Component" /></transition>
</router-view>

6.1 单个路由的过渡

使用元信息和动态 name 结合实现每个路由的不同过渡效果:

const routes = [{path: "/custom-transition",component: PanelLeft,meta: { transition: "slide-left" },},{path: "/other-transition",component: PanelRight,meta: { transition: "slide-right" },},
];
<router-view v-slot="{ Component, route }"><!-- 默认过渡效果 `fade` --><transition :name="route.meta.transition || 'fade'"><component :is="Component" /></transition>
</router-view>

6.2 复用视图进行过渡

复用路由组件会忽略过渡,可以添加 key 属性来强制过渡:

<router-view v-slot="{ Component, route }"><transition name="fade"><component :is="Component" :key="route.path" /></transition>
</router-view>

7. 滚动行为

通过 scrollBehavior 实现滚动效果,接收 to 和 from 路由对象:

const router = createRouter({history: createWebHashHistory(),routes: [...],scrollBehavior (to, from) {// return 期望滚动到哪个的位置return { top: 0 }}
})

通过 el 传递 CSS 选择器或 DOM 元素。top 和 left 的偏移量相对于该元素。

const router = createRouter({scrollBehavior(to, from) {// 始终在元素 #main 上方滚动 10pxreturn {// 也可以这么写// el: document.getElementById('main'),el: "#main",// 在元素上 10 像素top: 10,};},
});

模拟 “滚动到锚点” 的行为:

const router = createRouter({scrollBehavior(to, from, savedPosition) {if (to.hash) {return {el: to.hash,};}},
});

如果浏览器支持滚动行为,设置 behavior 让它变得更流畅:

const router = createRouter({scrollBehavior(to, from, savedPosition) {if (to.hash) {return {el: to.hash,behavior: "smooth",};}},
});

7.1 延迟滚动

通过返回一个 Promise,实现延时滚动:

const router = createRouter({scrollBehavior(to, from, savedPosition) {return new Promise((resolve, reject) => {setTimeout(() => {resolve({ left: 0, top: 0 });}, 500);});},
});

8. 路由懒加载

当打包时,JS 包会非常大,影响用户体验,使用动态导入代替静态导入:

// 将
// import UserDetails from './views/UserDetails.vue'
// 替换成
const UserDetails = () => import('./views/UserDetails.vue')const router = createRouter({// ...routes: [{ path: '/users/:id', component: UserDetails }// 或在路由定义里直接使用它{ path: '/users/:id', component: () => import('./views/UserDetails.vue') },],
})

8.1 把组件按组分块

8.1.1 使用 webpack

把某个路由下的所有组件打包到同个异步块 (chunk) 中。使用命名 chunk,特殊的注释语法:

const UserDetails = () =>import(/* webpackChunkName: "group-user" */ "./UserDetails.vue");
const UserDashboard = () =>import(/* webpackChunkName: "group-user" */ "./UserDashboard.vue");
const UserProfileEdit = () =>import(/* webpackChunkName: "group-user" */ "./UserProfileEdit.vue");
8.1.2 使用 Vite

使用 rollupOptions 下定义分块:

// vite.config.js
export default defineConfig({build: {rollupOptions: {// https://rollupjs.org/guide/en/#outputmanualchunksoutput: {manualChunks: {"group-user": ["./src/UserDetails","./src/UserDashboard","./src/UserProfileEdit",],},},},},
});

9. 扩展 RouterLink

自定义 RouterLink 实现导航菜单链接,处理外部链接:

<script setup lang="ts">import { RouterLink } from "vue-router";import { computed } from "vue";const props = defineProps({// @ts-ignore...RouterLink.props,_target: {type: String,default: "_self",},});const isExternalLink = computed(() => typeof props.to === "string" && props.to.startsWith("http"));
</script><template><a v-if="isExternalLink" v-bind="$attrs" :href="to" :target="_target"><slot/></a><router-link v-else v-bind="$props" :to="$props.to"><slot /></router-link>
</template>

10. 导航故障

10.1 检测导航故障

如果导航被阻,可以通过 router.push 返回的 Promise 的解判断是否离开了当前位置:

const navigationResult = await router.push("/my-profile");if (navigationResult) {// 导航被阻止
} else {// 导航成功 (包括重新导航的情况)this.isMenuOpen = false;
}

10.2 全局导航故障

使用 router.afterEach() 检测全局导航故障:

router.afterEach((to, from, failure) => {if (failure) {sendToAnalytics(to, from, failure);}
});

11. 动态路由

11.1 添加/移除 路由

使用 router.addRoute() 和 router.removeRoute() 实现动态路由的添加和删除。

router.addRoute({ path: "/about", component: About });
router.removeRoute("About"); // 使用 name 移除

11.2 在导航守卫中添加路由

通过返回新的位置来触发重定向:

router.beforeEach((to) => {if (!hasNecessaryRoute(to)) {router.addRoute(generateRoute(to));// 触发重定向return to.fullPath;}
});

11.3 添加嵌套路由

将路由的 name 作为第一个参数传递给 router.addRoute():

router.addRoute({ name: "admin", path: "/admin", component: Admin });
router.addRoute("admin", { path: "settings", component: AdminSettings });
// 等价于
router.addRoute({name: "admin",path: "/admin",component: Admin,children: [{ path: "settings", component: AdminSettings }],
});

11.4 查看现有路由

Vue Router 提供了两个功能来查看现有的路由:

| router.hasRoute(routerName):检查路由是否存在。

| router.getRoutes():获取一个包含所有路由记录的数组。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/687044.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7.1 Qt 中输入行与按钮

目录 前言&#xff1a; 技能&#xff1a; 内容&#xff1a; 参考&#xff1a; 前言&#xff1a; line edit 与pushbotton的一点联动 当输入行有内容时&#xff0c;按钮才能使用&#xff0c;并能读出输入行的内容 技能&#xff1a; pushButton->setEnabled(false) 按钮不…

166基于matlab的通过峭度指标与互相关系数筛选IMF进行SVD分解去噪

基于matlab的通过峭度指标与互相关系数筛选IMF进行SVD分解去噪&#xff0c;分辨虚假imf&#xff0c;提取最大峭度imf图。输出去噪前后时域及其包络谱结果。程序已调通&#xff0c;可直接运行。 166 matlab SVD去噪 IMF筛选 包络谱 (xiaohongshu.com)

Android安卓架构MVC、MVP、MVVM模式的概念与区别

目录 MVC框架 MVP框架 MVVM框架 MVVM与MVP区别 MVVM与MVC区别 MVC、MVP、MVVM模式哪个要好一些 MVC&#xff08;Model-View-Controller&#xff09;、MVP&#xff08;Model-View-Presenter&#xff09;、MVVM&#xff08;Model-View-ViewModel&#xff09;是三种常见的软…

Json格式文件

1.把Java对象转换成Json格式 1.1.导入依赖 这里推荐一个插件Jackson&#xff0c;其提供的类可以让Java的类转换成Jason格式文件 <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><vers…

Camtasia2023破解版下载

Camtasia2023破解版是一款简单好用的屏幕录像软件&#xff0c;它结屏幕录制和录制视频编辑等功能于一身&#xff0c;不管是用来创建视频教程还是相关教学视频或者操作演示等都是不二的选择&#xff0c;软件支持录制全屏或者部分区域进行选择录制&#xff0c;可录制网络摄像头、…

安卓实现简单砸地鼠游戏

效果 布局 <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto"xmlns:tools"http://schemas.android.com/tools"android:layout_width"match_parent"a…

Muse专业版教程:制作简谱,制作吉他谱

UP教你制作吉他谱,muse专业版吉他谱制作过程分享_哔哩哔哩_bilibili教学讲解-小宁视频制作-狂奔的琴弦软件-Muse专业版后面会分集录从零开始制作吉他谱,感兴趣的小伙伴点一波关注, 视频播放量 15457、弹幕量 1、点赞数 208、投硬币枚数 127、收藏人数 424、转发人数 59, 视频…

函数 栈帧

函数 引用式声明(函数原型):简称声明&#xff0c;指定了函数的返回值类型、函数的接受参数类型&#xff0c;这些叫函数的签名。与变量相同&#xff0c;在函数声明时编译器不会为函数分配内存。函数的返回值类型在C99之前若不指明则默认隐式转换为为int类型&#xff0c;但在C99…

DNS服务正反解析

1.正向解析 1.配置基本 1.1防火墙配置 二者都要关闭 setenforce 0 systemctl stop firewalld #关闭防火墙 yum install bind -y #下载bind软件 客户端可以不用下 1.2服务端配置静态ip&#xff0c; ip a 查看网卡 nmcli c modify ens33 ipv4.method manual ipv4.addresses …

OpenAI发布Sora模型,可根据文字生成逼真AI视频

早在2022年11月30日&#xff0c;OpenAI第一次发布人工智能聊天机器人ChatGPT&#xff0c;随后在全世界掀起了人工智能狂潮&#xff0c;颠覆了一个又一个行业。在过去的一年多的时间里&#xff0c;chatGPT的强大功能改变了越来越多人的工作和生活方式&#xff0c;成为了世界上用…

Python教程(26)——Python迭代器和生成器详解

迭代器 Python中的迭代器是一种对象&#xff0c;它可以迭代&#xff08;遍历&#xff09;一个可迭代对象&#xff08;比如列表、元组或字符串&#xff09;的元素。迭代器用于实现迭代器协议&#xff0c;即包含 __iter__() 方法和 __next__() 方法。 迭代器的工作原理是每次调…

vue watch监听

在 Vue.js 中&#xff0c;watch 是一个对象&#xff0c;用于观察和响应 Vue 实例上的数据变化。当被监视的属性发生变化时&#xff0c;watch 里的回调函数将被调用。 &#xff08;vue2&#xff09;官方文档&#xff1a;计算属性和侦听器 — Vue.js (vuejs.org) 下面是一个简单…

数据的力量:构筑现代大型网站之数据库基础与应用

目录 数据库基础知识--前言 大型网站架构特点 DBA数据库管理员 什么是数据? 数据存储 什么是数据库 数据表的概念 为什么需要mysql这样的数据库管理工具&#xff1f;★ DBMS 收费数据库与免费数据库 运维和数据库 开发与运维的不同阶段 数据库类别 数据库具体应用…

【机器学习笔记】11 支持向量机

支 持 向 量 机 &#xff08; Support Vector Machine,SVM &#xff09; 支 持 向 量 机 是 一 类 按 监 督 学 习 &#xff08; supervisedlearning&#xff09;方式对数据进行二元分类的广义线性分类器&#xff08;generalized linear classifier&#xff09;&#xff0c;其…

C#安装CommunityToolkit.Mvvm依赖

这里需要有一定C#基础&#xff0c; 首先找到右边的解决方案&#xff0c;右键依赖项 然后选择nuget管理 这里给大家扩展一下nuget的国内源&#xff08;https://nuget.cdn.azure.cn/v3/index.json&#xff09; 然后搜自己想要的依赖性&#xff0c;比如CommunityToolkit.Mvvm 再点…

学习Android的第十四天

目录 Android DatePicker 日期选择器 DatePicker 属性 和 事件 DatePicker 事件 获得 DatePicker 的值 Android TimePicker 时间选择器 TimePicker 属性 TimePicker 事件 获得 TimePicker 的值 Android CalendarView 日历视图 CalendarView 属性 CalendarView 事件 …

Linux超详细笔记

文章目录 Linux学习笔记操作系统Linux初识Linux的诞生Linux内核Linux发行版 虚拟机VMware安装远程连接Linux系统FinalShellFinalShell连接Linux WSL配置UbuntuLinux常用命令1.入门2.ls命令cd命令3.pwd命令4.相对路径和绝对路径5.mkdir命令6.文件操作命令&#xff08;1&#xff…

vue打包优化,webpack的8大配置方案

vue-cli 生成的项目通常集成Webpack &#xff0c;在打包的时候&#xff0c;需要webpack来做一些事情。这里我们希望它可以压缩代码体积&#xff0c;提高运行效率。 文章目录 &#xff08;1&#xff09;代码压缩&#xff1a;&#xff08;2&#xff09;图片压缩&#xff1a;&…

[AIGC] Kafka 消费者的实现原理

在 Kafka 中&#xff0c;消费者通过订阅主题来消费数据。每个消费者都属于一个消费者组&#xff0c;消费者组中的多个消费者可以共同消费一个主题&#xff0c;实现分布式消费。每个消费者都会维护自己的偏移量&#xff0c;用于记录已经读取到的消息位置。消费者可以选择手动提交…

17.3.2.9 像素处理与内存处理之比较

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 通过第17.3.2.1节到第17.3.2.8节&#xff0c;相信读者对通过锁定内存来处理图像有了一定认识。与第17.3.1节相比较&#xff0c;可以…