Vue-VueRouter

news2025/1/15 23:42:11

前言

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

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

官网地址:https://v3.router.vuejs.org/zh/

安装

1、安装依赖

已有项目使用如下安装

npm install vue-router


import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

2、使用脚手架

新建项目,可以使用vueCLI,脚手架安装,具体使用步骤可以查看:Vuex

在这里插入图片描述

展示

在使用前,先来将一下如何展示匹配到的路由,这里借用了官网vue-router4版本的文档

在这里插入图片描述

当你成功创建好一个使用vue-router的vue项目后,打开App.vue就会看到上面默认的有几个标签

<template>
  <div id="app">
    <nav>
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </nav>
    <router-view />
  </div>
</template>

基础使用

1、动态路由匹配

准备:

  • 先创建好包含vue-Router依赖的项目
  • 然后创建一个user组件

假如,我们想让访问/user/foo 或者/user/aaa的时候,都跳转到user组件。

有两种办法

  • 带参数跳转(1.1节)可以匹配到/user/...内路由
  • 不带参数跳转(1.2节)可以匹配到/user/.../...内路由 范围更广

1.1、响应路由的参数变化

使用这种方式既可以指定跳转路径,又可以获取路由中匹配到的路由参数,注重的是带参数跳转

首先我们要在router/index.is中声明路由跳转,然后再组件中编写页面内容。最后访问页面显示

设置路由跳转

index.js

import Vue from "vue";
import VueRouter from "vue-router";
import user from "../views/User.vue";

Vue.use(VueRouter);

const routes = [
  {
    // 访问/user/1 /user/foo 都会跳转到user组件中
    // 动态路径参数 以冒号开头
    path: "/user/:data",
    component: user,
  },
];

const router = new VueRouter({
  routes,
});

export default router;

user.vue

<template>
  <div class="home">
    User:
  </div>
</template>

<script>

export default {
  name: "HomeView",
};
</script>

结果

当我们访问/user/1 或者 /user/foo下,就会打开User页面

在这里插入图片描述

使用路由参数

只需要修改User.vue中内容即可,使用this.$route...获取路由中信息

User.vue

<template>
  <div class="home">
    <!-- dom文档中this可以省略  最后一个属性为路由中 : 后面定义的属性名 -->
    User: {{ $route.params.data }}
  </div>
</template>

<script>

export default {
  name: "HomeView",
  created() {
    console.log("$route",this.$route)
  }
};
</script>

结果

在这里插入图片描述

官网拓展

在这里插入图片描述

注意(坑)

当使用路由参数时,例如从 /user/foo 导航到 /user/bar原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用

解决办法一


复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象

<template>
  <div class="home">
    <!-- dom文档中this可以省略  最后一个属性为路由中 : 后面定义的属性名 -->
    User: {{ $route.params.data }}
  </div>
</template>

<script>

export default {
  name: "HomeView",
  watch: {
    $route(to, from) {
      console.log("watch to",to)
      console.log("watch from",from)
    }
  },
  created() {
    console.log("created",this.$route)
  }
};
</script>

在这里插入图片描述

解决办法二


或者使用 2.2 中引入的 beforeRouteUpdate 导航守卫,后面进阶->导航守卫会仔细讲解使用

<template>
  <div class="home">
    <!-- dom文档中this可以省略  最后一个属性为路由中 : 后面定义的属性名 -->
    User: {{ $route.params.data }}
  </div>
</template>

<script>

export default {
  name: "HomeView",
  beforeRouteUpdate(to, from, next) {
    console.log("beforeUpdate to",to)
    console.log("beforeUpdate from",from)
    next()
  },
  created() {
    console.log("created",this.$route)
  }
};
</script>

在这里插入图片描述

1.2、捕获所有路由

使用这种方式,注重的为页面跳转,对路由中的参数并不注重

常规参数(1.1节)只会匹配被 / 分隔的 URL 片段中的字符。如果想匹配任意路径,我们可以使用通配符 (*)

设置路由跳转

需要先修改index.js中的匹配规则

然后创建一个NotFindPage.vue的404页面

index.js

import Vue from "vue";
import VueRouter from "vue-router";
import user from "../views/User.vue";
import notFindPage from "../views/NotFindPage.vue";

Vue.use(VueRouter);

const routes = [
  {
    // 会匹配以 `/user-` 开头的任意路径
    path: "/user-*",
    component: user,
  },
  {
    // 当上面所有路径都没有匹配到的时候,就跳转到404页面
    path: "/*",
    component: notFindPage
  }
];

const router = new VueRouter({
  routes,
});

export default router;

NotFindPage.vue

<template>
  <div>
    <h1>抱歉页面未找到...</h1>
  </div>
</template>
<script>
  export default {
    name: "NotFindPage",
  };
</script>

结果

在这里插入图片描述

注意

  1. 当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。路由 { path: '*' } 通常用于客户端 404 错误。

  2. 当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分

    在这里插入图片描述

1.3、高级匹配模式

官网搬运…

vue-router 使用 path-to-regexp (opens new window)作为路径匹配引擎,所以支持很多高级的匹配模式,例如:可选的动态路径参数、匹配零个或多个、一个或多个,甚至是自定义正则匹配。查看它的文档 (opens new window)学习高阶的路径匹配,还有这个例子 (opens new window)展示 vue-router 怎么使用这类匹配。

1.4、匹配优先级

官网搬运…

有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:路由定义得越早,优先级就越高。

2、嵌套路由

实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:

在这里插入图片描述

这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>。例如,在 User 组件的模板添加一个 <router-view>

User.vue

<template>
  <div class="home">
    <!-- dom文档中this可以省略  最后一个属性为路由中 : 后面定义的属性名 -->
    User: {{ $route.params.id }}
    <!-- 这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件 用于显示匹配到的子路由 -->
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: "User",
  created() {
    console.log("created",this.$route)
  }
};
</script>

要在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置:

要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。

index.js

import Vue from "vue";
import VueRouter from "vue-router";
import user from "../views/User.vue";
import userProfile from '../views/UserProfile';
import userposts from "../views/UserPosts";


Vue.use(VueRouter);

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

const router = new VueRouter({
  routes,
});

export default router;

UserPosts.vueUserProfile.vue文件

在这里插入图片描述

结果

在这里插入图片描述

基于上面的配置,当你访问 /user/foo 时,User 的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个 空的 子路由:

const routes = [
  {
    path: "/user/:id",
    component: user,
    children: [
      {
        // 当 /user/:id/profile 匹配成功,
        // UserProfile 会被渲染在 User 的 <router-view> 中
        path: "profile",
        component: userProfile,
      },
      {
        // 当 /user/:id/posts 匹配成功
        // UserPosts 会被渲染在 User 的 <router-view> 中
        path: "posts",
        component: userposts,
      },
      {
        // 当 /user/foo 匹配成功,
        path: "",
        component: NotFindPage,
      },
    ]
  },
];

在这里插入图片描述

3、编程式导航

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

3.1、router.push

注意:在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

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

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

在这里插入图片描述

语法

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

参数使用

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

// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

注意

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

const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

同样的规则也适用于 router-link 组件的 to 属性。

在 2.2.0+,可选的在 router.pushrouter.replace 中提供 onCompleteonAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。在 3.1.0+,可以省略第二个和第三个参数,此时如果支持 Promise,router.pushrouter.replace 将返回一个 Promise。

注意: 如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息)。

案例

依旧使用上面的案例,然后稍微修改一下User.vue

User.vue

<template>
  <div class="home">
    <!-- dom文档中this可以省略  最后一个属性为路由中 : 后面定义的属性名 -->
    <router-link to="/user/520/profile">声明式导航</router-link> |
    <button @click="addRouter">编程式导航</button>
    <h1>User: {{ $route.params.id }}</h1>
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: "User",
  created() {
    console.log("created",this.$route)
  },
  methods: {
    addRouter() {
      this.$router.push("/user/520/posts",
              ()=>{
        console.log("onComplete")
      },
              ()=>{
                console.log("onAbort")
              })
    }
  },
};
</script>

结果

在这里插入图片描述

3.2、router.replace

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

在这里插入图片描述

语法

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

3.3、router.go

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

语法

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

router.go(n)

参数使用

// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)

// 后退一步记录,等同于 history.back()
router.go(-1)

// 前进 3 步记录
router.go(3)

// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)

案例

User.vue

<template>
  <div class="home">
    <!-- dom文档中this可以省略  最后一个属性为路由中 : 后面定义的属性名 -->
    <router-link to="/user/520/profile">声明式导航</router-link> |
    <button @click="addRouter">编程式导航</button>
    <h1>User: {{ $route.params.id }}</h1>
    <button @click="$router.go(1)">前进</button>
    <button @click="$router.go(-1)">后退</button>
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: "User",
  created() {
    console.log("$route",this.$route)
  },
  methods: {
    addRouter() {
      this.$router.push("/user/520/posts",
              ()=>{
        console.log("onComplete")
      },
              ()=>{
                console.log("onAbort")
              })
      console.log("$router",this.$router)

    },

  },
};
</script>

结果

在这里插入图片描述

3.4、操作History

官网搬运…

你也许注意到 router.pushrouter.replacerouter.gowindow.history.pushStatewindow.history.replaceStatewindow.history.go (opens new window)好像, 实际上它们确实是效仿 window.history API 的。

因此,如果你已经熟悉 Browser History APIs (opens new window),那么在 Vue Router 中操作 history 就是超级简单的。

还有值得提及的,Vue Router 的导航方法 (pushreplacego) 在各类路由模式 (historyhashabstract) 下表现一致。

命名路由

有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。

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

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

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

router.push({ name: 'user', params: { userId: 123 } })

案例

index.js

import Vue from "vue";
import VueRouter from "vue-router";
import user from "../views/User.vue";
import userProfile from '../views/UserProfile';
import userPosts from "../views/UserPosts";
import NotFindPage from "@/views/NotFindPage";


Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name:"user",
    component: user,
  },
  {
    path: "/profile",
    name:"profile",
    component: userProfile,
  },
  {
    path: "/posts",
    name:"posts",
    component: userPosts,
  },
];

const router = new VueRouter({
  routes,
});

export default router;

UserProfile.vue和UserPosts.vue

在这里插入图片描述

User.vue

<template>
  <div class="home">
    <router-link :to="{ name:'profile', params:{id:'520'}}">声明式导航</router-link> |
    <button @click="addRouter">编程式导航</button>
    <h1>User</h1>
  </div>
</template>

<script>

export default {
  name: "User",
  created() {
    console.log("$route",this.$route)
  },
  methods: {
    addRouter() {
      this.$router.push({name:'posts',params:{ id:'1314' }})
    },

  },
};
</script>

结果

访问http://localhost:8080/#/就会匹配到User页面中,然后点击标签和按钮,就会带着参数匹配到相应的路径

在这里插入图片描述

命名视图

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

<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>

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

const routes = [
  {
    path: "/:id",
    name:"user",
    components: {
      default:user,
      a:UserProfile,
      b:UserPosts
    },
  },
];

案例

index.js

import Vue from "vue";
import VueRouter from "vue-router";
import user from "../views/User.vue";
import userProfile from '../views/UserProfile';
import userPosts from "../views/UserPosts";
import NotFindPage from "@/views/NotFindPage";
import UserProfile from "@/views/UserProfile";
import UserPosts from "@/views/UserPosts";


Vue.use(VueRouter);

const routes = [
  {
    path: "/:id",
    name:"user",
    components: {
      default:user,
      a:UserProfile,
      b:UserPosts
    },
  },
];

const router = new VueRouter({
  routes,
});

export default router;

App.vue

<template>
  <div id="app">
    <router-view />
    <router-view name="a"></router-view>
    <router-view name="b"></router-view>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
</style>

嵌套命名视图

搬运官网…

我们也有可能使用命名视图创建嵌套视图的复杂布局。这时你也需要命名用到的嵌套 router-view 组件。我们以一个设置面板为例:

在这里插入图片描述

  • Nav 只是一个常规组件。
  • UserSettings 是一个视图组件。
  • UserEmailsSubscriptionsUserProfileUserProfilePreview 是嵌套的视图组件。

UserSettings 组件的 <template> 部分应该是类似下面的这段代码:

<!-- UserSettings.vue -->
<div>
  <h1>User Settings</h1>
  <NavBar/>
  <router-view/>
  <router-view name="helper"/>
</div>

然后你可以用这个路由配置完成该布局:

{
  path: '/settings',
  // 你也可以在顶级路由就配置命名视图
  component: UserSettings,
  children: [{
    path: 'emails',
    component: UserEmailsSubscriptions
  }, {
    path: 'profile',
    components: {
      default: UserProfile,
      helper: UserProfilePreview
    }
  }]
}

重定向和别名

重定向

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

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

import Vue from "vue";
import VueRouter from "vue-router";
import user from "../views/User.vue";
import userProfile from '../views/UserProfile';
import userPosts from "../views/UserPosts";
import NotFindPage from "@/views/NotFindPage";
import UserProfile from "@/views/UserProfile";
import UserPosts from "@/views/UserPosts";


Vue.use(VueRouter);

const routes = [
  {
    // 当访问到/404的时候,就会自动跳转到/user路由
    path: "/404",
    redirect: '/user',
    // 重定向的目标也可以是一个命名的路由
    // redirect: { name: 'user' }
    
    // 甚至是一个方法,动态返回重定向目标:
    // redirect: to => 
    //   // 方法接收 目标路由 作为参数
    //   // return 重定向的 字符串路径/路径对象
    // }
  },
  {
    path: "/user",
    name:"user",
    component:user
  }
];

const router = new VueRouter({
  routes,
});

export default router;

别名

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

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

const routes = [
  {
    // 当访问/admin时,虽然地址栏保持/admin,但是路由匹配规则为/user
    path: "/user",
    name:"user",
    component:user,
    alias: '/admin'
  }
];

在这里插入图片描述

路由组件传参

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

使用 props 将组件和路由解耦:

取代与 $route 的耦合

在这里插入图片描述

通过 props 解耦

在这里插入图片描述

布尔模式

如果 props 被设置为 trueroute.params 将会被设置为组件属性。

对象模式

如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。

在这里插入图片描述

函数模式

你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。

URL /user?id=520 会将 {id: 520} 作为属性传递给 User 组件。

请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 Vue 才可以对状态变化做出反应。

在这里插入图片描述

HTML5 History模式

搬运官网…

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。

如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!

不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

具体了解可以查看官网:https://v3.router.vuejs.org/zh/guide/essentials/history-mode.html

进阶使用

导航守卫

“导航”表示路由正在发生改变。

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

记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

全局前置守卫

你可以使用 router.beforeEach 注册一个全局前置守卫:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

案例

index.js

import Vue from "vue";
import VueRouter from "vue-router";
import user from "../views/User.vue";
import userProfile from '../views/UserProfile';
import userPosts from "../views/UserPosts";
import UserProfile from "@/views/UserProfile";
import UserPosts from "@/views/UserPosts";


Vue.use(VueRouter);

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

const router = new VueRouter({
  routes,
});

router.beforeEach((to,from,next) => {
  console.log("beforeEach to",to)
  console.log("beforeEach from",from)
  next()
})

export default router;

User.vue

<template>
  <div class="home">
    <h1>User</h1>
    <router-link :to="{ name:'userProfile' }"> 前往userProfile页面</router-link> |
    <router-link :to="{ name:'userPosts' }"> 前往userPosts页面</router-link>
  </div>
</template>

<script>

export default {
  name: "User",
};
</script>

UserPosts&UserProfile

在这里插入图片描述

结果

在这里插入图片描述

参数解析

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

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

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

全局解析守卫

2.5.0新增

在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

全局后置钩子

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

router.afterEach((to, from) => {
  // ...
})

路由独享的守卫

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

这些守卫与全局前置守卫的方法参数是一样的。

const routes = [
  {
    path: "/user",
    name:"user",
    component:user,
    // 当匹配到/user路由后,会执行当前路由守卫
    beforeEnter: (to, from, next) => {
      console.log("beforeEnter to",to);
      console.log("beforeEnter from",from)
      next()
    }
  },
];

组件内的守卫

最后,你可以在路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave
<template>
  <div class="home">
    <h1>User</h1>
    <router-link :to="{ name:'userProfile' }"> 前往userProfile页面</router-link> |
    <router-link :to="{ name:'userPosts' }"> 前往userPosts页面</router-link>
  </div>
</template>

<script>

export default {
  name: "User",
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  },
};
</script>

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

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

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

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

beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}

这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 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)
  }
}

注意事项

next函数在逻辑路径重叠的情况下不能调用两次

确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。这里有一个在用户未能验证身份时重定向到 /login 的示例:

// BAD 错的
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  // 如果用户未能验证身份,则 `next` 会被调用两次
  next()
})
// GOOD 对的
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

完整的导航解析流程

搬运官网…

  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 routes = [
  {
    path: "/user",
    component: user,
    children:[
        {
          path: "foo",
          component: about,
          meta: { requiresAuth: true },
        },
    ]
  },
  {
    path: "/login",
    component: login,
  }
];

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

首先,我们称呼 routes 配置中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可能匹配多个路由记录

例如,根据上面的路由配置,/foo/bar 这个 URL 将会匹配父路由记录以及子路由记录。

一个路由匹配到的所有路由记录会暴露为 $route 对象 (还有在导航守卫中的路由对象) 的 $route.matched 数组。因此,我们需要遍历 $route.matched 来检查路由记录中的 meta 字段。

下面例子展示在全局导航守卫中检查元字段:

router/index.js

import Vue from "vue";
import VueRouter from "vue-router";
import user from "../views/User.vue";
import about from "../views/AboutView.vue";
import login from "../views/Login.vue";
import store from "../store/index.js";

let state = store.state

Vue.use(VueRouter);

const routes = [
  {
    path: "/user",
    component: user,
    children:[
        {
          path: "foo",
          component: about,
          meta: { requiresAuth: true },
        },
    ]
  },
  {
    path: "/login",
    component: login,
  }
];

const router = new VueRouter({
  routes,
});

router.beforeEach((to,from,next) => {
  console.log("meta",to.meta);
  if (to.matched.some(record => record.meta.requiresAuth)) {
    if(state.isLogin) {
      next()
    }else {
      next({
        path:'/login',
        query:{redirect:to.fullPath}
      })
    }
  }else {
    next()
  }


})

export default router;

state/index.js

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    isLogin:false,
  },
  getters: {},
  mutations: {
    login(state){
      state.isLogin = !state.isLogin;
    }
  },
  actions: {},
  modules: {},
});

User.js

<template>
  <div class="home">
    <h1>User</h1>
    <h2>isLogin:{{ $store.state.isLogin }}</h2>
    <button @click="$router.push('/user/foo')">跳转/foo</button>
    <button @click="$store.commit('login')">登录</button>
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: "User",
};
</script>

其他跳转页面则省略。。。

结果

在这里插入图片描述

过渡动效

<router-view> 是基本的动态组件,所以我们可以用 <transition> 组件给它添加一些过渡效果:

<transition>
  <router-view></router-view>
</transition>

单个路由的过渡

上面的用法会给所有路由设置一样的过渡效果,如果你想让每个路由组件有各自的过渡效果,可以在各路由组件内使用 <transition> 并设置不同的 name。

const Foo = {
  template: `
    <transition name="slide">
      <div class="foo">...</div>
    </transition>
  `
}

const Bar = {
  template: `
    <transition name="fade">
      <div class="bar">...</div>
    </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'
  }
}

数据获取

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

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

导航完成后获取

当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。

就是在create()中发送请求获取数据

created() {
    let params = {}
    this.$axios.post('url',param,res =>{
        // ... 获取数据后的操作
    })
}

导航完成前获取

通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。

beforeRouteEnter (to, from, next) {
    getPost(to.params.id, (err, post) => {
        next(vm => vm.setData(err, post))
    })
},
// 路由改变前,组件就已经渲染完了
// 逻辑稍稍不同
beforeRouteUpdate (to, from, next) {
    this.post = null
    getPost(to.params.id, (err, post) => {
        this.setData(err, post)
        next()
    })
},
methods: {
    setData (err, post) {
        if (err) {
            this.error = err.toString()
        } else {
            this.post = post
        }
    }
}

滚动行为

参考官网:https://v3.router.vuejs.org/zh/guide/advanced/scroll-behavior.html

路由懒加载

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

结合 Vue 的异步组件 (opens new window)和 Webpack 的代码分割功能 (opens new window),轻松实现路由组件的懒加载。

首先,可以将异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身):

const Foo = () =>
  Promise.resolve({
    /* 组件定义对象 */
  })

第二,在 Webpack 2 中,我们可以使用动态 import (opens new window)语法来定义代码分块点 (split point):

import('./Foo.vue') // 返回 Promise

结合这两者,这就是如何定义一个能够被 Webpack 自动代码分割的异步组件。

const Foo = () => import('./Foo.vue')

在路由配置中什么都不需要改变,只需要像往常一样使用 Foo

import Vue from "vue";
import VueRouter from "vue-router";
import store from "../store/index.js";

let state = store.state

Vue.use(VueRouter);

const routes = [
  {
    path: "/user",
    component: () => import('../views/User.vue'),
    children:[
        {
          path: "foo",
          component: () => import('../views/AboutView.vue'),
        },
    ]
  },
  {
    path: "/login",
    component: () => import('../views/Login.vue'),
  }
];

const router = new VueRouter({
  routes,
});

export default router;

有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk (opens new window),一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。

const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

导航故障

参考官网:https://v3.router.vuejs.org/zh/guide/advanced/navigation-failures.html

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

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

相关文章

SPSS聚类分析(含k-均值聚类,系统聚类和二阶聚类)

本篇博客主要是根据1、聚类的基本知识点_哔哩哔哩_bilibili系列视频进行的学习记录一、SPSS聚类分析的基本知识点1、什么是聚类分析?聚类分析(Cluster analysis)又叫做群集分析,通过一些属性将对象或变量分成不同的组别&#xff0c;在同一类下的对象或变量在这些属性上具有一些…

最全面的SpringBoot教程(四)——数据库连接

前言 本文为 最全面的SpringBoot教程&#xff08;四&#xff09;——数据库连接 相关知识&#xff0c;下边将对JDBC连接配置&#xff0c;与使用Druid数据源&#xff0c;从添加依赖到修改配置项再到测试进行详尽介绍~ &#x1f4cc;博主主页&#xff1a;小新要变强 的主页 &…

C语言深度解剖-关键字(5)

目录 if else 组合 if else 的基本用法 注释 深入理解bool float(double)与浮点数的比较 写在最后&#xff1a; if else 组合 if else 的基本用法 #include <stdio.h>int main() {int flag 1;if (flag 1){printf("flag %d\n", flag);}else if (flag…

crmeb pro多门店版二次开发

重启一下swool 新创建的数据库表 ALTER TABLE eb_store_cart ADD price DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 单独改价 AFTER status;ALTER TABLE eb_store_order ADD car_no VARCHAR(255) NOT NULL DEFAULT COMMENT 车牌号 AFTER erp_order_id, ADD frame_no VARCHA…

2023软考系统集成项目管理工程师易混淆知识点~(7)

将2023上半年软考《系统集成项目管理工程师》易混淆知识点&#xff0c;分享给大家&#xff0c;快来跟着一起打卡学习吧&#xff01;概念辨析 1:项目、运营概念:(1)项目:项目是为达到特定目的&#xff0c;使用一定资源&#xff0c;在确定的期间内&#xff0c;为特定发起人而提供…

Linux perf的使用(一)

文章目录前言一、perf简介二、perf子命令简介三、perf工作模式3.1 计数3.2 采样参考资料前言 系统级性能优化通常包括两个阶段&#xff1a;性能剖析&#xff08;performance profiling&#xff09;和代码优化。 &#xff08;1&#xff09;性能剖析的目标是寻找性能瓶颈&#x…

python(13)--字典(Dict)

一、字典的基本操作 1.定义字典 字典也是一个列表型的数据结构&#xff0c;字典的数据是用“{ }”装的&#xff08;列表&#xff1a;[ ]&#xff0c;元组&#xff1a;( )&#xff09;&#xff0c;字典的元素是一一对应的关系“key-value”。 格式&#xff1a; Dictname{ key1…

vue中父子组件的传值

1. 父组件给子组件传值 主要两个步骤&#xff1a; 1. 在父组件调用子组件的时候绑定数据 2. 在子组件里面通过props接收父组件传过来的数据 2. 子组件给父组件传值 主要三个步骤&#xff1a; 1.在子组件中创建一个按钮&#xff0c;给按钮绑定一个点击事件 2.在响应该点击事件…

NSSRound#7

[NSSRound#7 Team]ec_RCE 源码: <?PHPif(!isset($_POST["action"]) && !isset($_POST["data"]))show_source(__FILE__);putenv(LANGzh_TW.utf8); $action $_POST["action"];$data "".$_POST["data"]."…

Java综合实验1题目: 猜心术---猜姓氏游戏

一、 实验内容及要求 假设游戏者共有十人&#xff0c;且有10个不同的姓&#xff1a;张、王、李、赵、刘、于、许、金、钱、孙&#xff0c;魔术师将十个姓写在四张纸牌上&#xff0c;游戏者只需指出那几张纸上有自己的姓&#xff0c;魔术师就能准确的说出游戏者的姓&#xff0c…

ChatGPT 为我制作了一张地图

有人说&#xff1a;一个人从1岁活到80岁很平凡&#xff0c;但如果从80岁倒着活&#xff0c;那么一半以上的人都可能不凡。 生活没有捷径&#xff0c;我们踩过的坑都成为了生活的经验&#xff0c;这些经验越早知道&#xff0c;你要走的弯路就会越少。 今天在刷视频的时候看到了…

Java 并发在项目中的使用场景

1、并发编程的三个核心问题&#xff1a;&#xff08;1&#xff09;分工&#xff1a;所谓分工指的是如何高效地拆解任务并分配给线程&#xff08;2&#xff09;同步&#xff1a;而同步指的是线程之间如何协作&#xff08;3&#xff09;互斥&#xff1a;互斥则是保证同一时刻只允…

本科大数据专业能找到大数据开发的工作么

本科大数据专业能不能找到大数据开发的工作取决于你在校期间大数据学科学习的怎么样~ 目前大二就还有时间去学习&#xff0c;趁着空余时间找个完整的学习路线去学习&#xff0c;争取能够在校招的时候就找到心仪的工作技术过关的话是完全没有问题的&#xff0c;而且加上你还有兴…

PythonWeb开发基础(三)类Flask框架请求封装

课程地址&#xff1a;Python 工程师进阶技术图谱 文章目录类Flask框架请求封装HTTP请求解析的python实现1、解析查询字符串2、多值问题使用webob库解析请求Bug记录bug&#xff1a;AttributeError: module cgi has no attribute parse_qs类Flask框架请求封装 Web服务器 本质是…

【C++提高编程】list 容器详解(附测试用例与结果图)

目录1. list容器1.1 list基本概念1.2 list构造函数&#xff08;初始化&#xff09;1.3 list 赋值和交换1.4 list 大小操作1.5 list 插入和删除1.6 list 数据存取1.7 list 反转&#xff08;reverse&#xff09;、排序&#xff08;sort&#xff09;和去重&#xff08;unique&…

电脑技巧:电脑状态监控神器TrafficMonitor介绍

有的时候我们为了解决某些电脑问题&#xff0c;需要监控电脑的实时网速、CPU、内存等的占用情况。一般情况下我们可以打开电脑任务管理器&#xff0c;就可以实时监控硬件状态&#xff0c;但如果查看频率较高的话&#xff0c;需要每次进入任务管理器就显得比较麻烦。今天小编分享…

【漏洞修复】 CVE Linux 系统应用漏洞修复笔记

这里写自定义目录标题说明SSL/TLS协议信息泄露漏洞(CVE-2016-2183)漏洞信息解决办法验证方法修复步骤说明查询当前使用的openssl版本号下载并安装新版本的openssl替换nginx中使用的openssl到最新版说明 此文章主要记录工作中遇到的漏洞以及修复过程。 SSL/TLS协议信息泄露漏洞…

【LeetCode】员工的重要性 | 图像渲染 | 岛屿问题

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《阿亮爱刷题》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;员工的重…

力扣SQL刷题4

目录1158. 市场分析 I1280. 学生们参加各科测试的次数1174. 即时食物配送 II585. 2016年的投资1158. 市场分析 I 题型&#xff1a;表1和表2连接时&#xff0c;如何把没有对应数据输出来。即表1中所有id列对应的表2数据输出&#xff0c;没用的输出0 解答1&#xff1a;left join…

【Linux】权限

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️小林爱敲代码       &#x1f6f0;️博客专栏&#xff1a;Linux之路       &#x1f6f0;️社区&#xff1a; 进步学堂       …