6 vue-router
1 vue-router介绍
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于 Vue.js 过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的 CSS class 的链接
- HTML5 历史模式或 hash 模式,在 IE9 中自动降级
- 自定义的滚动条行为
2 vue-router安装
用 Vue.js + Vue Router 创建单页应用,是非常简单的。使用 Vue.js,我们已经可以通过组合组件来组成应用程序,当你要把 Vue Router添加进来,我们需要做的是,将组件 (components) 映射到路由(routes),然后告诉 Vue Router 在哪里渲染它们
-
安装
npm i vue-router@3 -
main.js
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter)
推荐使用:vue add router 一键添加插件(记得提前提交)
3 基本使用
router.js
import Vue from 'vue'
//1.导入
import Router from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
//2.模块化机制 使用Router
Vue.use(Router)
//3.创建路由器对象
const router = new Router({
routes: [
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
})
export default router;
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
// 4.挂载根实例
router,
render:h => h(App)
}).$mount('#app')
做好以上配置后:
APP.vue
<template>
<div id=" app">
<div id="nav">
<!-- 使用router-link组件来导航-->
<!-- 通过传入to属性指定连接 -->
<!-- router-link默认会 被渲染成一个a标签-->
<router-link to="/" >Home</router-link>|
<router-link to="/about" >About</router-link>|
</div>
<!--路由出口-->
<!--路由匹配的组件将被渲染到这里-->
<router-view/>
</div>
</template>
打开浏览器,切换Home和About超链接,查看效果
4 命令路由
在配置路由的时候,给路由添加名字,访问时就可以动态的根据名字来进行访问
const router = new Router({
routes: [
{
path: '/home',
name: "home",
component: Home
},
{
path: '/about',
name: 'about',
component: About
}
]
})
要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:
<router-link :to="{name:'home'}">Home</router-link> |
<router-link :to="{name:'about'}">About</router-link> |
5 动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果
User.vue
<template>
<div>
<h3>用户页面</h3>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
</style>
路由配置:
const router = new Router({
routes: [
{
path: '/user/:id',
name: ' user',
component: User,
},
]
})
<router-link :to="{name:'user',params:{id:1}}">User</router-link> |
当匹配到路由时,参数值会被设置到this.$route.params,可以在每个组件中使用,于是,我们可以更新 User 的模板,输出当前用户的 ID:
<template>
<div>
<h3>用户页面{{$route.params.id}}</h3>
</div>
</template>
响应路由参数的变化
提醒一下,当使用路由参数时,例如从 /user/1 导航到 /user/2,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。复用组件时,想对路由参数的变化作出响应的话,你可以简单地watch (监测变化) $route 对象:
/*使用watch(监测变化) $route对象
watch: {
$route(to, from) {
console.log(to.params.id);
}
}, */
// 或者使用导航守卫
beforeRouteUpdate(to,from,next){
//查看路由的变化
//一定要调⽤next,不然就会阻塞路由的变化
next();
}
6 404路由
const router = new Router({
routes: [
//....
// 匹配不到理由时,404⻚⾯显示
{
path: '*',
component: () => import('@/views/404')
}
]
})
当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。路由 { path: '*' } 通常用于客户端 404错误
当使用一个通配符时, $route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分:
{
path: '/user-*',
component: () => import('@/views/User-admin.vue')
}
this.$route.params.pathMatch // 'admin'
7 匹配优先级
类似像地址上出现的这种:http://localhos:8080/page?id=1&title=foo
const router = new Router({
routes:[
//....
{
name:'/page',
name:'page',
component:()=>import('@/views/Page.vue')
}
]
})
<router-link :to="{name:'page',query:{id:1,title:'foo'}}">User</router-link> |
访问 http://localhos:8080/page?id=1&title=foo 查看Page
Page.vue
<template>
<div>
<h3>Page页面</h3>
<h3>{{ $route.query.id }}</h3>
</div>
</template>
<script>
export default {
created() {
//查看路由信息对象
console.log(this.$route);
},
}
</script>
<style lang="scss" scoped>
</style>
8 路由重定向和别名
例子是从 / 重定向到 /home :
const router = new Router({
mode: 'history',
routes: [
// 重定向
{
path: '/',
redirect: '/home'
},
{
path: '/home',
name: 'home',
component: Home
},
]
})
重定向的目标也可以是一个命名的路由:
const router = new VueRouter({
routes: [
{ path: '/', redirect: { name: 'name' }}
]
})
别名:
{
path: '/user/:id',
name: 'user',
component: User,
alias: '/alias'
}
起别名,仅仅起起别名 用户访问 http://loacalhost:8080/alias 的时候,显 示User组件
别名”的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。
9 路由组件传参
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
使用 props 将组件和路由解耦:
取代与 $route 的耦合
{
path: '/user/:id',
name : 'user',
component: User,
props: true
},
User.vue
<template>
<div>
<h3>用户页面{{ $route.params.id }}</h3>
<h3>用户页面{{ id }}</h3>
</div>
</template>
<script>
export default {
props: {
id: {
type: String,
default: ''
},
},
}
</script>
props也可以是个函数
{
path:'/user/:id',
name:' user',
component: User,
props: (route)=>({
id: route.params.id,
title: route.query.title
})
}
User.vue
<template>
<div>
<h3>用户页面{{ id }}-{{ title }}</h3>
</div>
</template>
<script>
export default {
props: {
id: {
type: String,
default: ' '
},
title: {
type: String
}
},
};
</script>
10 编程式导航
除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
注意:在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如
// 字符串
this.$router.push('home')
// 对象
this.$router.push({ path: 'home' })
// 命名的路由
this.$router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
this.$.push({ path: 'register', query: { plan: 'private' }})
前进后退
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
11 嵌套路由
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件
router.js
{
path: '/user/:id',
name: 'user',
component: User,
props: ({params , query})=>({
id: params.id,
title: query. title
}),
children: [
//当/user/ :id/profile 匹配成功,
// Profile 会被渲染在User的<router-view> 中
{
path: "profile",
component: Profile
},
//当/user/ :id/posts 匹配成功,
// Posts 会被渲染在User的<router-view> 中
{
path: "posts",
component: Posts
}
]
}
在 User 组件的模板添加一个 <router-view> :
<template>
<div>
<h3>用户页面{{$route.params.id}}</h3>
<h3>用户页面{{id}}</h3>
<router-view></router-view>
</div>
</template>
App.vue
<template>
<div id='app'>
<!-- 嵌套理由 -->
<router-link to="/user/1/profile">User/profile</router-link> |
<router-link to="/user/1/posts">User/posts</router-link> |
</div>
</template>
12 命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了
{
path: '/home',
name: 'home',
//注意这个key是components
components: {
default: Home, //默认的名字
main: ()=>import('@/views/Main.vue'),
sidebar: () => import('@/views/Sidebar.vue')
}
}
App.vue
<router-view/>
<router-view name='main'/>
<router-view name='sidebar'/>
13 导航守卫
“导航”表示路由正在发生改变。
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用离开守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter 。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter 。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
13-1 全局守卫
你可以使用 router.beforeEach 注册一个全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
有个需求,用户访问在浏览网站时,会访问很多组件,当用户跳转到 /notes ,发现用户没有登录,此时应该让用户登录才能查看,应该让用户跳转到登录页面,登录完成之后才可以查看我的笔记的内容,这个时候全局守卫起到了关键的作用
有两个路由 /notes 和 /login
router.js
const router = new VueRouter({
routes: [
{
path: '/notes',
name: 'notes',
component: () => import('@/views/Notes')
},
{
path: "/login",
name: "login",
component: () => import('@/views/Login')
},
]
})
//全局守卫
router.beforeEach((to, from, next) => {
//用户访问的是' /notes
if (to.path === '/notes') {
//查看一下用户是否保存了登录状态信息
let user = JSON.parse(localStorage.getItem('user'))
if (user) {
//如果有,直接放行
next();
} else {
//如果没有,用户跳转登录页面登录
next('/login')
}
} else {
next();
}
})
Login.vue
<template>
<div>
<input type="text" v-model="username">
<input type=" password" v-model=" pwd">
<button @click="handleLogin">提交</button>
</div>
</template>
<script>
export default {
data() {
return {
username: '',
pwd: ''
};
},
methods: {
handleLogin() {
// 1. 获取用户名和密码
// 2.与后端发生交互
setTimeout(() => {
let data = {
username: this.username
};
//保存用户登录信息
localStorage.setItem("user", JSON.stringify(data));
//跳转我的笔记页面
this.$router.push({name: "notes"});
}, 1000);
},
}
};
</script>
App.vue
<!-- 全局守卫演示-->
<router-link to="/notes">我的笔记</router-link>|
<router-link to="/login">登录</router-link>|
<button @click="handleLogout">退出</button>
<script>
export default {
methods: {
handleLogout() {
//删除登录状态信息
localStorage.removeItem("user");
//跳转到首页
this.$router.push('/')
}
},
}
</script>
13-2 组件内的守卫
你可以在路由组件内直接定义以下路由导航守卫:
- beforeRouteEnter
- beforeRouteUpdate (2.2 新增)
- beforeRouteLeave
<template>
<div>
<h3>⽤户编辑页⾯</h3>
<textarea name id cols="30" rows="10" v-model="content"></textarea>
<button @click="saveData">保存</button>
<div class="wrap" v-for="(item,index) in list" :key="index">
<p>{{ item.title }}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
content: "",
list: [],
confir: true
};
},
methods: {
saveData() {
this.list.push({
title: this.content
});
this.content = "";
}
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
if (this.content) {
alert("请确保保存信息之后,再离开");
next(false);
} else {
next();
}
}
};
</script>
13-3 路由元信息实现权限控制
给需要添加权限的路由设置meta字段
router.js
{
path: '/blog',
name: 'blog',
component: () => import('@/views/Blog'),
meta: {
requiresAuth: true
}
},
{
//路由独享的守卫
path: '/notes',
name: 'notes',
component: () => import('@/views/Notes'),
meta: {
requiresAuth: true
}
},
//全局守卫
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
//需要权限
if (!localStorage.getItem('user')) {
next({
path: ' /login',
query: {
redirect: to.fullPath
}
})
} else {
next();
}
} else {
next();
}
})
login.vue
//登录操作
handleLogin() {
// 1.获取用户名和密码
// 2.与后端发生交互
setTimeout(() => {
let data = {
username: this.username
};
localStorage.setItem("user",
JSON.stringify(data));
// 跳转到之前的页面
this.$router.push({path:
this.$route.query.redirect });
}, 1000);
}
13-4 数据获取
有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:
- 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
- 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
导航完成后获取数据
当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
<template>
<div class="post">
<div v-if="loading" class="loading">Loading...
</div>
<div v-if="error" class="error">{{ error }}</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
</div>
</template>
export default {
name: "Post",
data() {
return {
loading: false,
post: null,
error: null
};
},
//组件创建完后获取数据,
//此时data已经被监视了
created() {
//如果路由有变化,会再次执行该方法
this.fetchData();
},
watch: {
$route: "fetchData"
},
methods: {
fetchData() {
this.error = this.post = null;
this.loading = true;
this.$http.get('/api/post').then((result) => {
this.loading = false;
this.post = result.data;
}).catch((err) => {
this.error = err.toString();
});
}
}
};
自我笔记
vur-router知识
1.跳转
this.$router.push({path: '/'})
this.$router.go(-1) // 0:刷新 1:前进 -1:后退
2.路由信息对象(index.js中的信息)
// http://127.0.0.1/user/1
this.$route.params --> params命名路由匹配
<router-link :to="{name:'user', params:{id: 1}}">user</router-link>
// http://127.0.0.1/user-*
<router-link :to="{name:'admin-'}">user-admin</router-link>
this.$route.params.pathMatch --> 匹配的通配字符
// http://127.0.0.1/page?id=1&title=foo
<router-link :to="{name:'user', query:{id: 1, title: 'foo'}}">user</router-link>
this.$route.query --> query命名路由匹配
// http://127.0.0.1/user/
<router-link :to="{name:'user'}">user</router-link>
this.$route.matched // 获取当前的所有路由信息
3."404"路由
{
path: "*",
component: ()=>import('@/views/404')
// @表示src文件夹
}
4.通配符路由
{
path: '/user-*',
component: () => import('@/views/User-admin')
}
5.history模式
mode: 'history' // 干净的网页地址
6.重定向和别名
{
path: '/',
redirect: '/home'
redirect: {name: 'home'}
}
{
path: '/home',
alias: '/aaa' // 起别名
}
7.路由组件传值
{
path: '/user/:id',
props: true
}
props: ['id']
{
path: '/user/:id',
props: () => ({
id: route.params.id,
title: route.query.title,
})
}
props: ['id', 'title']
8.编程式导航
this.$router.push({path: '/'})
// 声明式
<router-link :to="{name:'user'}">user</router-link>
// 编程式
<div @click="goHome"></div>
methods:{
this.$router.push({path: '/'});
this.$router.push({name: 'user', params:{id:1}});
}
9.嵌套路由
{
path: '/home',
name: 'home',
component: ()=>import('@/views/Home')
children: [
{
path: 'profile',
component: ()=>import('@/views/ProFile')
}
]
}
10.命名视图的使用
<touter-view></router-view>
<touter-view name="main" class="main"></router-view>
<touter-view name="sidBar" class="sidbar"></router-view>
{
path: '/home',
name: 'home',
componemts: {
default: Home, // 默认名字
main: ()=>import('@/views/Main'),
sideBar: ()=>import('@/views/SideBar')
}
}
11.全局守卫做登录操作
<touter-view :to="{name: 'notes'}">笔记</router-view>
{
path: '/login',
name: 'login',
component: ()=>import('@/views/Login')
},
{
path: '/notes',
name: 'notes',
component: ()=>import('@/views/Notes')
}
main.js// 全局守卫
router.beforeEach((to, from, next)=>{
// 用户访问了/notes
if(to.path === '/notes'){
const user = JSON.parse(localStorage.getItem('user'));
if(user){
next();
}else{
next('/login');
}
}
next();
})
12.组件内的守卫
beforeRouteEnter(to, from, next){
// 组件么有加载之前
// 没有实例this
}
beforeRouteUpdate(to, from, next){
// 组件重用时,调用
}
beforRouteLeave(to, from, next){
// 导航离开组件的对应路由时调用
}
13.路由的元信息
{
path: '/notes',
name: 'notes',
component: ()=>import('@/views/Notes'),
meta: {
requireAuth: true; // 相当于加入了黑名单
}
}
main.js // 全局守卫
router.beforeEach((to, from, next)=>{
// 黑名单
if(to.matched.some(record=>record.meta.requireAuth)){
// 需要权限
if(!localStorage.geItem('user')){
next({path: '/login', query:{redirect: to.fullPath}})
}
}else{
next{}
}
// 白名单
next();
})
14 怎么响应路由参数的变化呢?
方式1:
watch监听属性:
watch: {
// to:表示到哪里去,from:表示从哪里来。
$route: (to, from) =>{
console.log(to.params.id);
// 发送ajax请求后端数据
}
}
方式2:
导航守卫的钩子函数:
beforeRouteEnter(to, from, next){
// to:表示到哪里去,from:表示从哪里来。
console.log(to.params.id)
next(); // 放行路由
}

浙公网安备 33010602011771号