安装
NPM
npm install vue-router
Vue CLI
如果你有一个正在使用 Vue CLI的项目,你可以以项目插件的形式添加 Vue Router。CLI 可以生成上述代码及两个示例路由。它也会覆盖你的 App.vue
,因此请确保在项目中运行以下命令之前备份这个文件:
vue add router
这里我们使用第二中方式安装,执行该命令后结果如下
C:\Gitee\vue-route-demo>vue add router
📦 Installing @vue/cli-plugin-router...
+ @vue/cli-plugin-router@5.0.8
updated 1 package in 4.418s
94 packages are looking for funding
run `npm fund` for details
✔ Successfully installed plugin: @vue/cli-plugin-router
? Use history mode for router? (Requires proper server setup for index fallback in production) No
🚀 Invoking generator for @vue/cli-plugin-router...
📦 Installing additional dependencies...
added 1 package from 1 contributor in 4.568s
94 packages are looking for funding
run `npm fund` for details
✔ Successfully invoked generator for plugin: @vue/cli-plugin-router
无论用何种方式,安装好以后, 在package.json中就可以看到安装的vue-router的版本了
"dependencies": {
"vue-router": "^3.5.1"
},
基础
起步
用 Vue.js + Vue Router 创建单页应用,感觉很自然:使用 Vue.js ,我们已经可以通过组合组件来组成应用程序,当你要把 Vue Router 添加进来,我们需要做的是,将组件 (components) 映射到路由 (routes),然后告诉 Vue Router 在哪里渲染它们。下面是个基本例子:
- 创建路由组件:
我们创建两个组件, 一个首页, 一个关于页面。
首页
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<h2>这是Home主页</h2>
<div>欢迎来到home主页</div>
<button @click="goBack">返回上一级</button>
</div>
</template>
<script>
export default {
name: "HomeView",
methods: {
goBack() {
window.history.length > 1 ? this.$router.go(-1) : this.$router.push("/");
},
},
};
</script>
关于页面
<template>
<div class="about">
<h2>关于页面</h2>
<div>欢迎来到关于页面</div>
</div>
</template>
- 配置路由映射。 即:组件和路由的关系
组件创建好了, 接下来要构建组件和路由的关系。 构建路由关系,我们通常定义在router/index.js文件
import Vue from 'vue'
import VueRouter from 'vue-router'
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
Vue.use(VueRouter)
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
import HomeView from '../views/HomeView.vue'
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes
})
export default router
- 使用路由, 通过和来展示组件
现在组件有了, 路有关系也有了。 那么接下来就是要有个入口来展示组件或者路由了。 我们的入口只有一个, 那就是App.vue, 所以, 我们直接在这里定义两个入口即可。
<template>
<div id="app">
<nav>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view />
</div>
</template
main.js中启动
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
Vue.config.productionTip = false;
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
// 现在,应用已经启动了!
通过注入路由器,我们可以在任何组件内通过 this.$router
访问路由器,也可以通过 this.$route
访问当前路由:
<script>
export default {
methods: {
goBack() {
window.history.length > 1 ? this.$router.go(-1) : this.$router.push("/");
},
},
};
</script>
路由的默认配置
现在我们进入首页显示的只有导航信息, 在页面必须点击某一个按钮,才能渲染出对应组件的内容。通常我们会有一个默认组件的展示。 否则首页内容就是空的了。如何设置默认展示的路由呢?
在路由表中增加一个重定向路由
{
path: "/",
redirect: "/home",
},
这是我们进入页面将默认选中首页并展示其内容
动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”来达到这个效果:
下面就以用户为例, 来实现动态路由
创建一个UserView.vue
<template>
<div class="about">
<h2>用户界面</h2>
<div>欢迎你来到用户界面</div>
</div>
</template>
<script>
export default {
name: "UserView",
};
</script>
添加路由映射关系
const routes = [
// 动态路径参数 以冒号开头
{ path: "/user/:userId", component: UserView},
];
这里path使用了:userId占位, 表示这里是一个占位符, 将被真实的userId替换. 后面在路由传递的时候变量需要和这里保持一致.
使用user路由
当我们动态路由到user的时候, 需要使用变量userId, 我们可以在data中定义一个变量
<template>
<div id="app">
<nav>
<router-link to="/home">首页</router-link> |
<router-link to="/about">关于</router-link>|
<router-link v-bind:to="'/user/' + userId">用户</router-link>
</nav>
<router-view />
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
userId: "721",
};
},
};
</script>
在上面使用了v-bind:to="’/user/’+userId"路由到/user/:userId, 而userId是我们在组件中定义的一个变量.
我们看到当点击用户的时候, 浏览器的url路径变为了/user/721.
http://10.133.46.6:8080/#/user/721
将参数传递到组件
我们希望在user组件中显示, 欢迎{{用户名}}来到用户页面, 如何实现呢?
要想实现这个效果, 需要使用到this.$route
对象. 这是获取路由信息的对象
一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID:
<template>
<div>
<h2>用户界面</h2>
<div>欢迎{{ userId }},来到用户界面</div>
</div>
</template>
<script>
export default {
name: "UserView",
data() {
return {
userId: this.$route.params.userId,
};
},
};
</script>
你可以在一个路由中设置多段“路径参数”,对应的值都会设置到$route.params
中。例如:
模式 | 匹配路径 | $route.params |
---|---|---|
/user/:username | /user/evan | { username: 'evan' } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: 'evan', post_id: '123' } |
除了$route.params
外,$route
对象还提供了其它有用的信息,例如,$route.query
(如果 URL 中有查询参数)、$route.hash
等等。你可以查看 API 文档 的详细说明。
响应路由参数的变化
复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象:
const User = {
template: '...',
watch: {
$route(to, from) {
// 对路由变化作出响应...
}
}
}
或者使用 2.2 中引入的 beforeRouteUpdate
导航守卫:
const User = {
template: '...',
beforeRouteUpdate(to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
捕获所有路由或 404 Not found 路由
常规参数只会匹配被 / 分隔的 URL 片段中的字符。如果想匹配任意路径,我们可以使用通配符 (*):
{
// 会匹配所有路径
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}
当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。路由 { path: '*' }
通常用于客户端 404 错误。如果你使用了History 模式,请确保正确配置你的服务器。
当使用一个通配符时,$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'
匹配优先级
有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:路由定义得越早,优先级就越高。
嵌套路由
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如
/user/foo/profile /user/foo/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种关系。
接着上节创建的 app:
<div id="app">
<router-view></router-view>
</div>
这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>。
我们的路由映射规则是: 一个路径映射一个组件. 访问两个路径, 会分别渲染两个组件.
下面来实现嵌套路由,
第一步: 创建组件,创建两个组件
HomeBanner.vue组件
<template>
<div>
<h2>首页banner图</h2>
<ul>
<li>banner图1</li>
<li>banner图2</li>
<li>banner图3</li>
<li>banner图4</li>
</ul>
</div>
</template>
<script>
export default {
name: "HomeBanner",
};
</script>
<style scoped>
</style>
HomeNews.vue组件:
<template>
<div>
<h2>首页新闻</h2>
<ul>
<li>第一条新闻</li>
<li>第二条新闻</li>
<li>第三条新闻</li>
<li>第四条新闻</li>
</ul>
</div>
</template>
<script>
export default {
name: "HomeNews",
};
</script>
<style scoped>
</style>
第二步: 创建组件路由
我们要在Home也添加子路由, 需要在路由里面增加一个children属性配置.
{
path: "/home",
name: "home",
component: HomeView,
children: [
{
path: "",
redirect: "HomeNew",
},
{
path: "HomeNew", //注意: 这里面没有/
component: () => import("../components/HomeNews"),
},
{
path: "HomeBanner",
component: () => import("../components/HomeBanner"),
},
],
}
里面的路径依然是有两个部分:
一个是path路由路径: 这里需要注意的是,不需要在路径名的前面加/
另一个是component: 指定组件名称
第三步: 增加
我们要在Home页面展示子组件, 因此需要将子组件的展示放在页面上
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<h2>这是Home主页</h2>
<div>欢迎来到home主页</div>
<button @click="goBack">返回上一级</button>
<nav>
<router-link to="/home/homeNew">新闻</router-link>
<router-link to="/home/homeBanner">图片</router-link>
</nav>
<router-view></router-view>
</div>
</template>
要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。
你会发现,children 配置就是像 routes 配置一样的路由配置数组,所以呢,你可以嵌套多层路由。
编程式的导航
除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
router.push(location, onComplete?, onAbort?)
注意:在 Vue 实例内部,你可以通过 $router
访问路由实例。因此你可以调用 this.$router.push
。
想要导航到不同的 URL,则使用 router.push
方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="...">
等同于调用 router.push(...)
。
- 声明式
<router-link :to="...">
- 编程式
router.push(...)
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了 path,params 会被忽略,上述例子中的 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
在 2.2.0+,可选的在 router.push
或 router.replace
中提供 onComplete
和 onAbort
回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。在 3.1.0+,可以省略第二个和第三个参数,此时如果支持 Promise,router.push
或 router.replace
将返回一个 Promise。
注意: 如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1
-> /users/2
),你需要使用 beforeRouteUpdate
来响应这个变化 (比如抓取用户信息)。
router.replace(location, onComplete?, onAbort?)
跟 router.push
很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
- 声明式
<router-link :to="..." replace>
- 编程式
router.replace(...)
router.go(n)
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似
window.history.go(n)
例子
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
操作 History
你也许注意到 router.push
、 router.replace
和 router.go
跟 window.history.pushState
、 window.history.replaceState
和 window.history.go
好像, 实际上它们确实是效仿 window.history
API 的。
因此,如果你已经熟悉 Browser History APIs (opens new window),那么在 Vue Router 中操作 history 就是超级简单的。
还有值得提及的,Vue Router 的导航方法 (push
、 replace
、 go
) 在各类路由模式 (history
、 hash
和 abstract
) 下表现一致。
命名路由
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。
{
path: '/user/:userId',
name: 'user',
component: User
}
要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:
<router-link :to="{ name: 'user', params: { userId: 123 }}">用户</router-link>
这跟代码调用 router.push() 是一回事:
router.push({ name: 'user', params: { userId: 123 } })
这两种方式都会把路由导航到 /user/123 路径。
命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 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 router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
嵌套命名视图
我们也有可能使用命名视图创建嵌套视图的复杂布局。这时你也需要命名用到的嵌套 router-view 组件。我们以一个设置面板为例:
/settings/emails /settings/profile
+-----------------------------------+ +------------------------------+
| UserSettings | | UserSettings |
| +-----+-------------------------+ | | +-----+--------------------+ |
| | Nav | UserEmailsSubscriptions | | +------------> | | Nav | UserProfile | |
| | +-------------------------+ | | | +--------------------+ |
| | | | | | | | UserProfilePreview | |
| +-----+-------------------------+ | | +-----+--------------------+ |
+-----------------------------------+ +------------------------------+
- Nav 只是一个常规组件。
- UserSettings 是一个视图组件。
- UserEmailsSubscriptions、UserProfile、UserProfilePreview 是嵌套的视图组件。
注意:我们先忘记 HTML/CSS 具体的布局的样子,只专注在用到的组件上。
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 配置来完成,下面例子是从 /a 重定向到 /b:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
重定向的目标也可以是一个命名的路由:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
甚至是一个方法,动态返回重定向目标:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 /a
路由添加一个 beforeEnter
守卫并不会有任何效果。
别名
“重定向”的意思是,当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,那么“别名”又是什么呢?
/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。
上面对应的路由配置为:
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
“别名”的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
路由组件传参
vue-router参数传递有两种方式: 第一种是: param的方式. 第二种是: query的方式
- param方式
创建一个MapView.vue组件
<template>
<div>
<h2>地图页面</h2>
<div>欢迎来到地图页面</div>
</div>
</template>
<script>
export default {
name: "Map",
};
</script>
创建动态路由的时候, 定义变量
{
path: "/map/:city",
name: "map",
component: () => import("../views/MapView.vue"),
},
我们定义了一个map/:city, 这样的动态路由. 路由可以是/map/beijng, 或者/user/shanghai
在App.vue中定义动态路由跳转的组件
<router-link v-bind:to="'/map/' + city" replace active-class="active"
>地图</router-link
>|
<script>
export default {
name: "App",
data() {
return {
userId: "721",
city: "beijing",
};
},
};
</script>
修改MapView.vue组件
首先, 在MapView.vue脚本中获取传递过来的路由参数. 我们使用[this.$route.params.变量名]的方式获取路径参数
<template>
<div>
<h2>地图页面</h2>
<div>欢迎来到{{ city }}地图页面</div>
<div>{{ $route.params.city }}</div>
</div>
</template>
<script>
export default {
name: "Map",
data() {
return {
city: this.$route.params.city,
};
},
};
</script>
以上是使用参数的方式传递变量.
- query方式
下面来举个案例:
创建一个组件ProfileView.vue
<template>
<div>
<h2>这是一个Profile组件</h2>
</div>
</template>
<script>
export default {
name: "Profile",
};
</script>
<style scoped>
</style>
配置路由
{
path: "/profile",
component: () => import("../views/ProfileView.vue"),
},
渲染组件
<!-- 配置动态路由 -->
<router-link
v-bind:to="{
path: '/profile',
query: { name: 'lily', sex: '男', age: 12 },
}"
>档案</router-link>
来看一下效果
![](https://img.haomeiwen.com/i8077710/e804cb893ec7448e.png)
我们看到路径上带了?参数
http://10.133.46.6:8080/profile?name=lily&sex=%E7%94%B7&age=12
组件中获取参数
<template>
<div>
<h2>这是一个Profile组件</h2>
<h4>{{ $route.query }}</h4>
<h4>{{ $route.query.name }}</h4>
<h4>{{ $route.query.age }}</h4>
<h4>{{ $route.query.sex }}</h4>
</div>
</template>
查看效果
![](https://img.haomeiwen.com/i8077710/1fe012958d0599a5.png)
- 通过 props 进行传递
在组件中使用$route
会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
取代与$route
的耦合,通过 props 解耦
使用 props 将组件和路由解耦:
- 布尔模式
案例:
创建StudentView.vue
props: ["id"]
用来接收收路由传来的参数
<template>
<div>
<h2>学生页面</h2>
<div>欢迎{{ id }}来到学生页面</div>
</div>
</template>
<script>
export default {
name: "Student",
props: ["id"],
};
</script>
配置路由:
当 props 设置为 true 时,route.params 将被设置为组件的 props
{
path: "/student/:id",
component: () => import("../views/StudentView.vue"),
props: true,
},
渲染组件:
这里为了简化,写死传递了593066063
作为id.
<router-link to="/student/593066063">学生</router-link>
看一下效果
![](https://img.haomeiwen.com/i8077710/b676e9b3512a5d8c.png)
2.对象模式
如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。
举例如下:
创建VideoView.vue组建
<template>
<div>
<h2>视频页面</h2>
<div>欢迎来到[{{ name }}]视频页面</div>
</div>
</template>
<script>
export default {
name: "Video",
props: {
name: {
type: String,
default: "Vue",
},
},
};
</script>
定义路由并传递参数
{
path: "/video",
component: () => import("../views/VideoView.vue"),
props: { name: "逃学威龙" },
},
增加视图渲染
<router-link to="/video">视频</router-link>
查看效果图
![](https://img.haomeiwen.com/i8077710/4ae73b393fb11389.png)
函数模式
你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。
举例说明:
定义组件
<template>
<div>
<h2>图片页面</h2>
<div>欢迎来到[{{ name }}]图片页面</div>
</div>
</template>
<script>
export default {
name: "Image",
props: {
name: {
type: String,
default: "Vue",
},
},
};
</script>
配置路由
定义路由中的函数:
const dynamicPropsFn = (route) => {
return { name: route.query.type + "of png" };
};
路由配置
{
path: "/image",
component: () => import("../views/ImageView.vue"),
props: dynamicPropsFn,
},
渲染组件
<router-link to="/image?type=girl">图片</router-link>
当 URL 为/image?type=girl
时, 将传递 {name: ‘girl of girl’} 作为 props 传给组件。
效果如下
![](https://img.haomeiwen.com/i8077710/b85331d49303ef57.png)
History 模式
- 修改静态路由的模式为history
我们之前都是采用hash的方式来静态路由跳转的, 但hash方式有一个缺点, 即带有#
例如:我们跳转都Home页, 他的路径是
http://localhost:8080/#/home
http://10.133.46.6:8080/#/user/123
带有一个#, 这不符合我们通常路径的使用方法,所以,我们可以考虑将其替换为history的模式。 如何替换呢? 在router/index.js文件中
const router = new VueRouter({
routes,
mode: "history",
});
我点击页面切换则没有#
http://10.133.46.6:8080/about
http://10.133.46.6:8080/map
http://10.133.46.6:8080/user/123
![](https://img.haomeiwen.com/i8077710/108b4d92e40f624e.png)
进阶
导航守卫
- 这里的导航,指的就是路由的跳转或者取消跳转
- 守卫指的是: 有很多钩子方法, 允许我们在某一个过程中植入代码逻辑.
- 常见的导航守卫有:
全局导航守卫(包括三个: 全局前置守卫, 全局解析守卫, 全局后置守卫), 路由独享导航守卫, 组件内的守卫-
全局前置守卫
-
可以使用 router.beforeEach 注册一个全局前置守卫:
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数:
-
to: Route
: 即将要进入的目标 路由 -
from: Route
: 当前导航正要离开的路由 -
next: Function
: 一定要调用该方法来 resolve 这个钩子。
也就是说, 代码这至少是这样的
router.beforeEach((to, from, next) =>{
next();
})
案例:全局导航守卫的方式更新title属性
在路由index.js文件中增加元数据属性, 并设置title属性值
const routes = [
{
path: "/",
name: "home",
component: HomeView,
children: [
{
path: "HomeNew", //注意: 这里面没有/
component: () => import("../components/HomeNews"),
},
{
path: "HomeBanner",
component: () => import("../components/HomeBanner"),
},
],
meta: {
title: "首页",
},
},
{
path: "/about",
name: "about",
component: () => import("../views/AboutView.vue"),
meta: {
title: "关于",
},
},
// 动态路径参数 以冒号开头
{
path: "/user/:userId",
name: "user",
component: UserView,
meta: {
title: "用户",
},
},
{
path: "/map/:city",
name: "map",
component: () => import("../views/MapView.vue"),
meta: {
title: "地图",
},
},
{
path: "/profile",
component: () => import("../views/ProfileView.vue"),
meta: {
title: "档案",
},
},
{
path: "/student/:id",
component: () => import("../views/StudentView.vue"),
props: true,
meta: {
title: "学生",
},
},
{
path: "/video",
component: () => import("../views/VideoView.vue"),
props: { name: "逃学威龙" },
meta: {
title: "视频",
},
},
{
path: "/image",
component: () => import("../views/ImageView.vue"),
props: dynamicPropsFn,
meta: {
title: "图片",
},
},
];
后面在全局导航路由中设置title属性
router.beforeEach((to, from, next) => {
console.log(to);
console.log(from);
document.title = to.matched[0].meta.title;
next();
});
效果图如下:
![](https://img.haomeiwen.com/i8077710/59b59584e2a7aa44.png)
-
全局解析守卫
在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
-
全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
router.afterEach((to, from) => {
// ...
})
-
路由独享的守卫
你可以在路由配置上直接定义 beforeEnter 守卫:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
这些守卫与全局前置守卫的方法参数是一样的。
-
组件内的守卫
最后,你可以在路由组件内直接定义以下路由导航守卫:
- beforeRouteEnter
- beforeRouteUpdate (2.2 新增)
- beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,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)
}
}
-
完整的导航解析流程
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 的回调函数,创建好的组件实例会作为回调函数的参数传入。
网友评论