一、 路由
1. 什么是路由
(1)路由就是一种映射关系。
(2)后端路由:url与后端资源的一 一对应的关系
(3)前端路由:用户事件与事件处理函数一 一对应的关系
2.实现简易前端路由
(1)前端路由是基于hash值的变化实现的,这里的hash值指的是 url的hash值,也就是界面的锚点。
(2)比如点击页面中的菜单或者按钮改变URL的hash值,根据hash值的变化来控制组件的切换。
(3)核心实现依靠 window.onhashchange 事件,监听hash值的变化。location.hash 可以获取到最新的hash值。
window.onhashchange = function(){
location.hash
}
(4)<component> 标签相当于一个组件调用的占位符。通过绑定 v-bind:is 属性,动态控制此处调用哪个组件。 (重点)
这个标签再父组件调用子组件时也可以用。
<component :is="comName"></component>
<div class="box">
<a href="#yule">娱乐</a>
<a href="#caijing">财经</a>
<a href="#keji ">科技</a>
<a href="#junshi">军事</a>
<component v-bind:is="change"></component>
</div>
<script>
// 子组件
var yule = {
template: `<h1>娱乐头条</h1>`,
};
var caijing = {
template: `<h1>财经频道</h1>`,
};
var keji = {
template: `<h1>科技在线</h1>`,
};
var junshi = {
template: `<h1>军事理论</h1>`,
};
// vue实例对象--父组件
var vm = new Vue({
el: '.box',
data: {
change: 'yule',
},
components: {
yule: yule,
caijing: caijing,
keji: keji,
junshi: junshi,
},
});
// 监听页面的hash值的变化
window.onhashchange = function () {
var x = location.hash.slice(1);
switch (x) {
case 'keji':
vm.change = 'keji';
break;
case 'yule':
vm.change = 'yule';
break;
case 'caijing':
vm.change = 'caijing';
break;
case 'junshi':
vm.change = 'junshi';
break;
}
};
</script>
二、 vue-router
vue-router官网:https://router.vuejs.org/zh/
1. 什么是 vue-router
① 它是一个Vue.js官方提供的路由管理器。
② Vue Router和Vue.js非常契合,可以一起方便的实现SPA(single page web application,单页应用程序)应用程序的开发
③ Vue Router依赖于Vue,所以需要先引入Vue.js,再引入Vue Router.js
2. Vue Router的特性
- 支持H5历史模式或者hash模式
- 支持嵌套路由
- 支持路由参数
- 支持编程式路由
- 支持命名路由
3. 路由的基本使用
① 导入js文件。(项目中用import关键字引入)
必须先引入vue.js,再引入vue-router.js。
<!-- 1. 引入文件 -->
<script src="js/vue_2.5.22.js"></script>
<script src="js/vue-router_3.0.2.js"></script>
② 添加 路由链接。(项目中多用编程式导航)
<!-- 2. 添加路由链接 -->
<!-- 整个标签会被渲染成a链接。 -->
<router-link to="/user">User</router-link>
<router-link to="/register">Register</router-link>
③ 添加 路由占位符(最后路由展示的组件就会在占位符的位置显示)
<!-- 3.添加路由占位符 -->
<!-- 将来通过路由规则匹配到的组件,会被渲染到这里 -->
<router-view></router-view>
④ 定义 路由组件(项目中每个.vue文件都是组件)
- 路由组件全局定义,不需要使用 Vue.component,也不用再vue实例对象中挂载。
- 路由组件中的data 数据格式为:data( ) { return { } }。
- 需要在vue实例对象中挂载 路由实例对象。
// 4.创建路由组件
const User = {
data() {
return { name: "吴磊" }
},
template: `<h1>user</h1>`,
};
const Register = {
template: `<h1>register</h1>`,
};
⑤ 创建路由实例并配置 路由规则。(项目中将这部分单独写到一个.js文件)
- 路由规则【routes】是一个数组,每个数组元素都是一个配置对象。
- 每个配置对象至少有两个属性:path 、component。
- path的值是路由链接的hash值;component的值是路由组件的名称。(不能加引号)
// 5. 创建路由实例并配置路由规则
var router = new VueRouter({
// 路由规则【routes】是一个数组,每个数组元素都是一个配置对象。
// 每个配置对象至少有两个属性:path 、component。
// path的值是路由链接的hash值;component的值是路由组件的名称。(不能加引号)
routes: [
{ path: '/user', component: User },
{ path: '/register', component: Register },
],
});
⑥ 将路由挂载到Vue实例对象中
// 6. 将路由挂载到vue实例对象中
var vm = new Vue({
el: '.box',
router: router,
});
⑦ 如果 是在项目中,应该全局注册路由,这样在任何一个vue组件 中都能使用this.$router
Vue.use(VueRouter)
示例如下:
<div class="box">
<!-- 2. 添加路由链接 -->
<!-- 整个标签会被渲染成a链接。 -->
<router-link to="/user">User</router-link>
<router-link to="/register">Register</router-link>
<!-- 3.添加路由占位符 -->
<!-- 将来通过路由规则匹配到的组件,会被渲染到这里 -->
<router-view></router-view>
</div>
<script>
// 4.创建路由组件
const User = {
template: `<h1>user</h1>`,
};
const Register = {
template: `<h1>register</h1>`,
};
// 5. 创建路由实例并配置路由规则
var router = new VueRouter({
// 路由规则【routes】是一个数组,每个数组元素都是一个配置对象。
// 每个配置对象至少有两个属性:path 、component。
// path的值是路由链接的hash值;component的值是路由组件的名称。(不能加引号)
routes: [
{ path: '/user', component: User },
{ path: '/register', component: Register },
],
});
// 6. 将路由挂载到vue实例对象中
var vm = new Vue({
el: '.box',
router: router,
});
</script>
三、路由的更多使用规则
1. 路由重定向
{ path: '/', redirect: '/user' }
在路由规则中,使用 redirect 属性,可以实现路由重定向。当访问 " / " 根路径时,强制跳转到 " /user " 页面。
注意:
① 如果重定向的是一个子路由,则该路由的父级路由组件也会被渲染出来。
② 子路由可以把path属性设置为空,父路由组件渲染时默认显示这个子路由组件。
2 .路由嵌套
① 子路由的 路由链接 和 路由占位符 ,写到父级路由的组件模板中。
② 子路由的 组件 直接全局定义。
③ 子路由的 路由规则数组 ,写到父级路由的规则配置对象中。
注意:
① 一级父路由的路径以 "/" 开头;
② 子路由的path路径可以带上父路由路径 /father/children ,
③ 也可以只写子路由路径,但是不能再以 / 开头。
④ 根路由已经挂载到vue实例对象上了,子组件不需要操作。
<div class="box">
<!-- 2. 父级路由链接 -->
<router-link to="/user">User</router-link>
<!-- 3.父级路由占位符 -->
<router-view></router-view>
</div>
<script>
// 4.父级路由组件
const User = {
template: `<div>
<h1>user</h1>
<router-link to="/user/table1">table1</router-link>
<router-link to="/user/table2">table2</router-link>
<router-view></router-view>
</div>`,
};
// 子级路由组件
const child1 = {
template: '<h3>table1</h3>',
};
const child2 = {
template: '<h3>table2</h3>',
};
// 5. 创建路由实例并配置路由规则
var router = new VueRouter({
routes: [
{
path: '/user',
component: User,
children: [
{ path: '/user/table1', component: child1 },
{ path: '/user/table2', component: child2 },
],
},
],
});
// 6. 将路由挂载到vue实例对象中
var vm = new Vue({
el: '.box',
router: router,
});
</script>
3. 动态匹配路由(重要)
① 如果多个路由链接的 hash值 非常相似,可以在 路由规则 中,让多个 path 值(路由链接的hash值) 对应一个componet (路由组件)。
② 也就是 多个路由链接 对应一个 路由规则。
<router-link to="/user/1">User1</router-link>
<router-link to="/user/2">User2</router-link>
<router-link to="/user/3">User3</router-link>routes: [{ path: '/user/:uid?', component: User }],
动态参数uid后面的 ?表示,访问也面时,这个动态参数可以传递,也可以不传递。
实例:不同频道有各自的文章列表数据,我们根据频道的id生成了多个组件去渲染数据。如果想要用路由跳转到各个组件页面,就要用动态路由。否则多个频道对应对个组件,就要定义多个路由。
4. 动态匹配路由传参(重要)
- 路由规则对象 向 路由组件 传递参数。
(1)路由组件可以通过 $.route.params.uid 获得路由规则对象中的 动态参数 uid。
template: "<h1>路由的id为{{$route.params.uid}}</h1>"
① this.$route.query.id 可以获取路由规则中的查询参数。
② this.$router.options.routes 可以获得路由(规则)对象。
(2)在路由规则对象中,添加 props属性。props属性值为 true 时,路由组件可以通过 props数组 获得路由规则对象中的 动态参数 uid。
// 4.创建路由组件
const User = {
props: ['uid'],
template: `<h1>路由的id为{{uid}}</h1>`,
};
// 5. 创建路由实例并配置路由规则
var router = new VueRouter({
// 在路由规则中,让多个路由链接对应一个路由组件。
routes: [{ path: '/user/:uid', component: User, props: true }],
});
(3)props属性值为 一个对象 时,路由组件可以通过 props数组 获得路由规则中的 静态参数。
// 4.创建路由组件
const User = {
props: ['uname','age'],
template: `<h1>姓名:{{uname}},年龄:{{age}}</h1>`,
};
// 5. 创建路由实例并配置路由规则
var router = new VueRouter({
// 在路由规则中,让多个路由链接对应一个路由组件。
routes: [
{
path: '/user/:uid',
component: User,
props: { uname: '吴磊', age: '22' },
},
],
});
(4)props属性值为 一个函数 时,路由组件可以通过 props数组 获得路由规则中的 静态参数和动态参数。
// 4.创建路由组件
const User = {
props: ['uname', 'age', 'uid'],
template: `<h1>姓名:{{uname}},年龄:{{age}},id:{{uid}}</h1>`,
};
// 5. 创建路由实例并配置路由规则
var router = new VueRouter({
// 在路由规则中,让多个路由链接对应一个路由组件。
routes: [
{
path: '/user/:uid',
component: User,
// 箭头函数只有一句代码,并且是返回值信息,则可以省略花括号。
props: (route) => ({
uname: '吴磊',
age: '22',
uid: route.params.uid,
}),
},
],
});
5. 命名路由
① 在 路由规则对象 中,添加 name 属性,为这个路由规则命名。
② 在 路由链接 中,为 to属性绑定 一个对象, 代替 url地址 。
<router-link v-bind:to="{name:'user',params:{uid:3}}">User3</router-link>
routes: [
{
name: 'user',
path: '/user/:uid',
component: User,
},
],
<div class="box">
<!-- 2. 路由链接 -->
<router-link to="/user/1">User1</router-link>
<router-link to="/user/2">User2</router-link>
<!-- 命名路由 -->
<router-link v-bind:to="{name:'user',params:{uid:3}}">User3</router-link>
<!-- 3.添加路由占位符 -->
<router-view></router-view>
</div>
<script>
// 4.创建路由组件
const User = {
props: ['uname', 'age', 'uid'],
template: `<h1>姓名:{{uname}},年龄:{{age}},id:{{uid}}</h1>`,
};
// 5. 创建路由实例并配置路由规则
var router = new VueRouter({
routes: [
{
// 命名路由
name: 'user',
path: '/user/:uid',
component: User,
},
],
});
// 6. 将路由挂载到vue实例对象中
var vm = new Vue({
el: '.box',
router: router,
});
6. 编程式导航
声明式导航:通过点击链接实现导航的方式,叫做声明式导航 例如:普通网页中的 链接 或 vue 中的 <router-link></router-link>
编程式导航:通过调用JavaScript形式的API实现导航的方式,叫做编程式导航 例如:普通网页中的 location.href
(1)常用的编程式导航的API
this.$router.push('hash地址') 跳转到指定页面
this.$router.go( ) 页面的前进与倒退
(2)push 方法可以接收的参数
router.push('/home') // 字符串(路径名称)
router.push({ path: '/home' }) // 对象
router.push({ name: 'user', params: { userId: 123 }}) // 命名的路由(传递参数)
router.push({ path: '/register', query: { uname: 'lisi' }}) // 带查询参数,变成 /register?uname=lisi
<div class="box">
<!-- 2. 路由链接 -->
<router-link to="/user">User</router-link>
<router-link to="/register">Register</router-link>
<!-- 3.添加路由占位符 -->
<router-view></router-view>
</div>
<script>
// 4.创建路由组件
const User = {
template: `<div>
<h1>User部分</h1>
<button v-on:click="handle">跳转到Register页面</button>
</div>`,
methods: {
handle: function () {
// 用编程的方式控制路由跳转
this.$router.push('/register');
},
},
};
const Register = {
template: `<div>
<h1>Register部分</h1>
<button v-on:click="handle">回退</button>
</div>`,
methods: {
handle: function () {
// 用编程的方式控制路由跳转
this.$router.go(-1);
},
},
};
// 5. 创建路由实例并配置路由规则
var router = new VueRouter({
routes: [
{ path: '/user', component: User },
{ path: '/register', component: Register },
],
});
// 6. 将路由挂载到vue实例对象中
var vm = new Vue({
el: '.box',
router: router,
});
</script>
7. 路由的导航守卫(重要)
作用:路由导航守卫可以判断当前状态下是否有token 令牌。从而限制用户直接输入地址访问页面。
(1)前置守卫
router.beforeEach(function(to, from, next) {}
to 代表要去的路由;
from 代表从哪个路由来;
next 是一个钩子函数,必须进行调用。
(2)后置守卫
router.afterEach(function() {})
注意:这里的router使用的是 路由的实例对象,也就是包含了路由规则的路由对象。
8. 路由的权限处理
(1)登录时路由权限处理(访问权限)
① 动态路由不要直接挂载到router实例对象上。
② 当前登录用户的个人信息里,有一个属性保存了当前用户的 权限标识,这个权限标识和 动态路由对象里的name属性值 一 一对应。
③ 动态路由的权限处理可以在vuex中进行。state数据中保存当前用户所有的路由对象,用于渲染左侧菜单;最重要的是,在actions中定义一个 筛选动态路由对象 的方法。
④ 在 路由导航守卫 的权限拦截处,拿到token令牌后调用actions方法,可以得到用户的 动态路由对象。然后用Vue.router提供的 router.addRoutes(路由规则对象数组) 方法,为router实例对象追加动态路由对象。
注意:用router.addroutes()方法后,要手动 next(to.path)。
(2)登出时重置路由权限(访问权限)
① 我们在登录时为router实例对象添加了动态路由,必须在退出登录时 重置,否则下一个用户登录后会继续使用上一个用户的权限。路由模块 提供了方法,只需要在 退出登录 时调用即可。
// 重置路由。新建一个路由实例,把新路由的路由规则对象(只有个静态路由)赋值给老路由实例。
export function resetRouter () {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
② 左侧菜单的渲染使用的是vuex中的路由数据,退出登录 时也要 重置。
(3)功能权限
① 用户拥有了动态路由,就能访问对应的页面,但是页面中的某些按钮是否能点击等操作,受控于访问权限下的 功能权限。
②用 Vue.mixin( { }) ,给所有的vue组件 混入一个 methods方法,这个方法根据 功能点的权限标识 和用户信息里的 功能权限标识 进行比对,返回一个布尔值。
// 全局的混入对象
// 导入vuex
import store from '@/store'
export default {
methods: {
// 这里的参数key是系统某个功能的【权限标识】
checkPermission (key) {
// 这是vuex中保存的用户数据,里面有当前用户的【功能权限标识】
// store.state.user.userInfo.roles.points
const { userInfo } = store.state.user
// 如果用户有【功能权限标识】数组,并且有值,就进行筛选判断
if (userInfo.roles.points && userInfo.roles.points) {
return userInfo.roles.points.some(item => item === key)
}
return false// 如果连if都进不去,必定没有权限
}
}
}
③ 给组件中的按钮v-bind绑定这个方法,通过返回值判断当前用户是否可以使用这个按钮。
<el-button
size="small"
type="primary"
:disabled="!checkPermission('POINT-USER-ADD')"
>新增员工</el-button>
9.路由传参
(1)编程式导航传参
① params形式 :参数不显示在地址栏
this.$router.push({name: "department",params:{userId:this.num})
② query形式:参数在地址栏内显示
this.$router.push({path:"/department",query:{name:"马冬梅",age:30})
③ 在组件内获取
this.$route.params.userId 或者 this.$route.query.name 进行获取
注意:跳转到 新开页 时,用params传递的参数是获取不到的。可以用 query传参;或者将params参数写到路由对象里。
(2)声明式导航传参
补充
① 有了前端路由,组件就定义在路由里面。最外面是一个根路由组件。
② 为根路由组件 配置路由规则。
③ 把根路由组件 挂载 到vue实例对象上。
④ 最后在vue实例对象控制的HTML结构中 添加路由占位符,渲染出整个根路由组件。
前端路由,就是通过 路由链接 生成一个 a标签,通过 路由组件 生成主体内容,然后用 路由规则 把这两项进行绑定。最后通过 路由占位符 指定渲染的位置。
- 在组件中,this.$router 获取到的是路由对象,可以调用路由的 push() 、go()、back()方法。 this.$route 获取到的是路由规则中的路径参数,可以获取到 path、query 等。