◆

Vue Router 路由导航与守卫完整用法详解

2023-02-02 01:370

Api 文档地址

[https://next.router.vuejs.org/zh/api/]

router-view

router-view 将显示与 url 对应的组件。

测试示例

当路由配置如下时,代表进入根路径 / 会重定向到 /home 路径

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    redirect: '/home',
  },
  /** 主页 */
  {
    path: '/home',
    name: 'Home',
    component: Home,
  },
];

App.vue 文件的代码如下

<template>
  <router-view />
</template>
  • 当 App.vue 中加入 router-view 标签即可渲染 /home 路径对应的组件 Home
  • 去掉时会什么也不显示
  • router-link 内部会立刻把 to 的值传到 router.push()

    • active-class:链接激活时,应用于渲染的 a 标签的 class。默认值:router-link-active (或者全局 linkActiveClass)
    • exact-active-class:链接精准激活时,应用于渲染的 a 标签的 class。默认值:router-link-exact-active (或者全局 linkExactActiveClass)
  • router.push 和所有其他导航方法都会返回一个 Promise,让我们可以等到导航完成后才知道是成功还是失败。

  • push:向 history 栈添加一个新的记录。

  • replace:通过替换历史堆栈中的当前 entry,它在导航时不会向 history 添加新记录。

  • 函数签名:

to	RouteLocationRaw	要导航到的路由地址
push/replace(to: RouteLocationRaw): Promise&lt;NavigationFailure | void | undefined>

示例

<router-link to="/">跳转至根路径</router-link>
<br />

createRouter

创建一个可以被 Vue 应用程序使用的路由实例。

示例

const router = createRouter({
  /** 用于路由实现历史记录 */
  history: createWebHistory(),
  linkActiveClass: 'par-custom-active-class',
  linkExactActiveClass: 'par-custom-exact-active-class',
  /** 应该添加到路由的初始路由列表 */
  routes,
  /** 在页面之间导航时控制滚动的函数 */
  scrollBehavior: () => ({
    top: 0,
    left: 0,
  }),
});

参数 RouterOptions

history: 用于路由实现历史记录 createWebHistory | createWebHashHistory(基于 hash 的历史记录) linkActiveClass: 用于激活的 RouterLink 的默认类。如果什么都没提供,则会使用 router-link-activelinkExactActiveClass: 用于精准激活的 RouterLink 的默认类。如果什么都没提供,则会使用 router-link-exact-active。 routes:应该添加到路由的初始路由列表。 scrollBehavior:在页面之间导航时控制滚动的函数。可以返回一个 Promise 来延迟滚动

scrollBehavior 参数详解

在页面之间导航时控制滚动的函数。可以返回一个 Promise 来延迟滚动,可以自定义路由切换时页面如何滚动。

  • 始终滚动到左上角
scrollBehavior: () => ({
  top: 0,
  left: 0,
});
  • 通过 el 传递一个 CSS 选择器或一个 DOM 元素。在这种情况下,top 和 left 将被视为该元素的相对偏移量。
scrollBehavior(to, from, savedPosition);
{
  // 始终在元素 #main 上方滚动 10px
  return {
    // 也可以这么写
    // el: document.getElementById('main'),
    el: '#main',
    top: -10,
  };
}
  • 模拟 滚动到锚点 + 滚动行为 + 延迟滚动 路由访问的路径如:http://localhost:3000/pushDemo#pushDemo 在测试的 vue 页面需要增加如下代码
<a id="pushDemo">这是一个 pushDemo 锚点</a>
// 路由不能用 hash 模式
history: (createWebHistory(), scrollBehavior(to, from, savedPosition));
{
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (to.hash) {
        console.log(to.hash, 999);
        resolve({
          el: to.hash,
          behavior: 'smooth',
        });
      }
    }, 2500);
  });
}

params

  • 使用 params 携带参数
  • params 不能与 path 一起使用
<p>使用 params 携带参数</p>
<p @click="$router.push('/users/mio')">跳转到 user/mio 页面</p>
<p @click="$router.push({ path: '/users/miofly' })">跳转到 user/miofly 页面</p>
<p
  @click="$router.push({ name: 'User', params: { id: 'wfd', sex: 'boy' } })"
>跳转到 user/wfd 页面</p>
<p
  @click="$router.push({ name: 'User', params: { id: 'fnn', sex: 'girl' } })"
>跳转到 user/fnn 页面</p>

next

  • next():进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed(确认的)。
  • next(false):中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
  • next('/') 或者 next({path:'/'}):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace:true、name:'home' 之类的选项以及任何用在 router-link 的 toProp 或 router.push 中的选项。
  • next(error) :如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

路由导航失败的情况

  • router.push 返回的 promise,vue 的路由导航是异步的
  • 有三种情况会中断导航,可以用 isNavigationFailure 和 NavigationFailureType 来区分。
    • aborted:在导航守卫中返回 false 中断了本次导航。
    • cancelled: 在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了 router.push。
    • duplicated:导航被阻止,因为我们已经在目标位置了。

包含所有可能导航失败类型的枚举,可以传递给 isNavigationFailure 来检查某些特定类型的失败。不要使用任何数值,总是使用诸如 NavigationFailureType.aborted 这样的变量。

export declare enum NavigationFailureType
成员值描述
aborted4终止导航是指由于导航守卫返回 false 或调用 next(false) 而失败的导航。
cancelled8取消导航是指由于最近的导航完成启动(不一定是完成)而失败的导航。
duplicated16重复导航是指在启动时已经在同一位置失败的导航。

示例

<p>导航故障测试</p>
<p @click="goPageTestOne">再次跳转当前路由</p>
<p @click="goPageTestTwo">在路由守卫中再次跳转其他路由</p>
<p @click="goPageTestThree">在路由守卫阻止跳转</p>

<script lang="ts">
import { defineComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { NavigationFailureType, isNavigationFailure } from 'vue-router';

export default defineComponent({
  setup(prop) {
    // 返回 router 实例。相当于在模板中使用 $router。必须在 setup() 中调用。
    const router = useRouter();
    // 返回当前路由地址。相当于在模板中使用 $route。必须在 setup() 中调用。
    const route = useRoute();

    async function goPageTestOne() {
      const status = await router.push(route.fullPath);
      if (isNavigationFailure(status, NavigationFailureType.duplicated)) {
        console.log('已经在当前路由中');
      }
    }

    async function goPageTestTwo() {
      const status = await router.push('/test/one');
      if (isNavigationFailure(status, NavigationFailureType.cancelled)) {
        console.log(
          '在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了 router.push。',
        );
      }
    }

    async function goPageTestThree() {
      const status = await router.push('/test/two');
      if (isNavigationFailure(status, NavigationFailureType.aborted)) {
        console.log('在导航守卫中返回 false 中断了本次导航。');
      }
    }

    return {
      goPageTestOne,
      goPageTestTwo,
      goPageTestThree,
    };
  },
});
</script>

路由文件

{
	path: '/test',
	component: Test,
	// beforeEnter: (to, from) => false,
	children: [
		{
			path: 'one',
			name: 'TestOne',
			component: TestOne,
			beforeEnter: (to, from) => {
				router.push('/')
			},
		},
		{
			path: 'two',
			name: 'TestTwo',
			component: TestTwo,
			beforeEnter: (to, from) => false,
		}
	]
},

START_LOCATION

路由所在的初始路由地址。可用于导航守卫中,以区分初始导航。

示例

{
  "path"
:
  "/",
    "params"
:
  {}
,
  "query"
:
  {}
,
  "hash"
:
  "",
    "fullPath"
:
  "/",
    "matched"
:
  [],
    "meta"
:
  {}
}

路由守卫

  • 全局路由守卫
router.beforeEach((to, from, next) => {
  console.log('调用了 router.beforeEach 全局前置守卫');
});

router.beforeResolve((to, from, next) => {
  console.log('调用了 router.beforeResolve 全局解析守卫');
});

// 和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
router.afterEach((to, from) => {
  console.log('调用了 router.afterEach 全局后置钩子');
});
  • 路由独享守卫
{
	path: '/routerGurad/:name',
	component: RouterGurad,
	// beforeEnter 守卫 只在进入路由时触发,不会在 params、query 或 hash 改变时触发。
	// 例如,从 /users/2 进入到 /users/3 或者从 /users/2#info 进入到 /users/2#projects。
	// 它们只有在 从一个不同的 路由导航时,才会被触发。
	beforeEnter: (to, from) => {
		console.log('调用了 beforeEnter 路由独享的守卫');
	},
},
  • 组件内守卫+生命周期 (routerGurad 路由页面)
import {
  defineComponent,
  onBeforeMount,
  onBeforeUnmount,
  onBeforeUpdate,
  onMounted,
  onUnmounted,
  onUpdated,
} from 'vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRouter } from 'vue-router';

export default defineComponent({
  beforeCreate() {
    console.log('beforeCreate()');
  },

  created() {
    console.log('created');
  },

  beforeMount() {
    console.log('beforeMount');
  },

  mounted() {
    console.log('mounted');
  },

  beforeUpdate() {
    console.log('beforeUpdate');
  },

  updated() {
    console.log('updated');
  },

  beforeUnmount() {
    console.log('beforeUnmount');
  },

  unmounted() {
    console.log('unmounted');
  },

  setup(prop) {
    console.log('setUp 触发');
    onBeforeMount(() => {
      console.log('--onBeforeMount');
    });

    onMounted(() => {
      console.log('--onMounted');
    });

    onBeforeUpdate(() => {
      console.log('--onBeforeUpdate');
    });

    onUpdated(() => {
      console.log('--onUpdated');
    });

    onBeforeUnmount(() => {
      console.log('--onBeforeUnmount');
    });

    onUnmounted(() => {
      console.log('--onUnmounted');
    });

    // 对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持 传递回调,因为没有必要了:
    onBeforeRouteLeave((to, from) => {
      // 在导航离开渲染该组件的对应路由时调用
      // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
      console.log('调用了 BeforeRouteLeave 组件守卫');
    });

    onBeforeRouteUpdate((to, from) => {
      // 在当前路由改变,但是该组件被复用时调用
      // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
      // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
      // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
      console.log('调用了 BeforeRouteUpdate 组件守卫');
    });
  },

  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !
    // 因为当守卫执行时,组件实例还没被创建!
    // 不过,你可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数:
    console.log('调用了 beforeRouteEnter 组件守卫');
    next(vm => {
      console.log('beforeRouteEnter守卫中的 next 调用');
    });
  },
});

完整的导航解析流程

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

示例一: index 路由页面

<div @click="$router.push('/routerGurad/wfd')">
跳转到 routerGurad 页面
</div>

<script lang="ts">
import { defineComponent } from 'vue';
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRouter } from 'vue-router';

export default defineComponent({
  setup(prop) {
    const router = useRouter();

    onBeforeRouteLeave((to, from) => {
      console.log('调用了 onBeforeRouteLeave');
    });

    onBeforeRouteUpdate((to, from) => {
      console.log('调用了 onBeforeRouteUpdate');
    });

    return {};
  },
});
</script>

从 index页面 跳转至 routerGurad 页面

vue-router

示例二: routerGurad 自跳转

<template>
  <div>
    <h1>这是测试路由导航守卫的页面</h1>
    <div @click="$router.push('/routerGurad/fnn')">
      跳转到 routerGurad fnn 页面
    </div>

    <div @click="$router.push('/routerGurad/wfd')">
      跳转到 routerGurad wfd 页面
    </div>
  </div>
</template>
vue-router

嵌套路由

  • 配置嵌套路由时,子路由的 path 为空, 则父路由默认访问的就是为空的路由,/child
  • 子路由的 path 为不带 / 的字符串时,路由的访问 path 是父路由 path + 子路由 path
  • 当子路由的 path 为带 / 的字符串时,路由的访问 path 就是子路由 pathrouter 配置
{
	path: '/child',
	component: ChildIndex,
	children: [
		{
			path: '',
			component: ChildFour,
		},
		{
			path: 'one',
			component: ChildOne,
		},
		{
			path: 'two',
			component: ChildTwo,
		},
		{
			path: '/three',
			component: ChildThree,
		},
	]
},

父级页面

<p @click="$router.push('/child')">跳转到 child 页面</p>

childIndex 页面

<template>
  <div>
    <p @click="$router.push('/child')">跳转到 child 页面</p>
    <p @click="$router.push('/child/one')">跳转到 child/one 页面</p>
    <p @click="$router.push('/child/two')">跳转到 child/two 页面</p>
    <p @click="$router.push('/three')">跳转到 /three 页面</p>
    <br />
    <p>下面是 router-view</p>
    <router-view />
  </div>
</template>

router 的一些方法

console.log('当前路由 currentRoute:', unref(router.currentRoute));
console.log('路由配置对象 options:', router.options);
console.log('路由记录的完整列表 getRoutes:', router.getRoutes());
console.log('是否存在指定名称的路由 hasRoute:', router.hasRoute('RouterTest'));
console.log(
  '返回路由地址的标准化版本 resolve:',
  router.resolve(unref(router.currentRoute).fullPath),
);
vue-router

RouteRecordRaw

路由记录

  • path 路由的路径

  • redirect 重定向路径

  • children 当前路由的嵌套路由。

  • alias 路由的别名。

  • name 路由记录的名称。

  • beforeEnter 在进入特定于此记录的守卫之前。注意如果记录有重定向属性,则 beforeEnter 无效。

  • props 允许将参数作为 props 传递给由 router-view 渲染的组件。当传递给一个多视图记录时,它应该是一个与组件具有相同键的对象,或者是一个应用于每个组件的布尔值。

  • meta 在路由上附加自定义数据。

RouteLocationNormalized(对应 route)

没有任何重定向记录的路由地址。在导航守卫中,to 和 from 属于这种类型。

  • fullPath 返回完整的 URL,编码与路由地址有关。包括 path、 query 和 hash。

  • hash 已解码 URL 的 hash 部分。总是以 #开头。如果 URL 中没有 hash,则为空字符串。

  • query 从 URL 的 search 部分提取的已解码查询参数的字典。

  • matched 与给定路由地址匹配的标准化的路由记录数组。

  • meta 附加到从父级到子级合并(非递归)的所有匹配记录的任意数据。

  • name 路由记录的名称。如果什么都没提供,则为 undefined。

  • params 从 path 中提取的已解码参数字典。

  • path 编码 URL 的 pathname 部分,与路由地址有关。

  • redirectedFrom 在找到 redirect 配置或带有路由地址的名为 next() 的导航守卫时,我们最初尝试访问的路由地址,最后到达当前位置。如果没有重定向,则为 undefined

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEVue Router 路由导航与守卫完整用法详解加载中