Vue2 第四弹 —— Vuex、Vue-router

Vuex

  1. 概念
    在 Vue 中实现集中式数据管理的一个 Vue 插件,对 vue 应用中多个组件共享数据进行集中式管理(读/写),也是一种组件间通信方式,且适用于任意组件间通信。

  2. 何时使用
    多个组件需要共享数据时。

  3. 搭建 vuex 环境
    创建文件 src/store/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 引入 vue 核心库
import Vue from 'vue'
// 引入 vuex
import Vuex from 'vuex'
// 应用 vuex 插件
Vue.use(Vuex)



// 准备 actions 对象——相应组件中用户的动作
const actions = {}
// 准备 mutations 对象——修改 state 中的数据
const mutations = {}
// 准备 state 对象——保存数据
const state = {}

// 创建并暴露 store
export default store = {
actions,
mutations,
state
}

在 main.js 中,创建 vm 时传入 store 配置项

1
2
3
4
5
6
7
8
9
// 引入 store
import store from './store'
...
// 创建 vm
new Vue({
el:'#app',
render:h=>h(App),
store
})
  1. 基本使用
    初始化数据,配置actions、配置mutations、操作文件store.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 引入 Vue 核心库
import Vue from 'vue'
// 引入 Vuex
import Vuex from 'vuex'
// 应用 Vuex 插件
Vue.use(Vuex)

const actions = {
// 响应组件中加的请求
jia(context,value){
// console.log('action 中的 jia 被调用了')
context.commit('JIA',value)
}
}

const mutations = {
// 执行加
JIA(state,value){
// console.log('mutation 中的 JIA 被调用了')
state.sum += value
}
}

const state = {
sum:0
}

export default new Vuex.Store({
actions,
mutations,
state,
})

组件中读取 vuex 中的数据:this.$store.state.sum
组件中修改 vuex 中的数据:
this.$store.dispatch('actions 中的方法名',数据)this.$store.commit('mutations 中的方法名',数据)

备注:若没有网络请求或其他业务逻辑,组件中也可以越过 actions,即不写 dispatch,直接编写 commit

  1. getters 的使用
    概念:当 state 中的数据需要经过加工后再使用时,可以使用 getters 加工。
    store.js 中追加 getters 配置
1
2
3
4
5
6
7
8
9
10
11
const getters = {
bigSum(state){
return state.sum * 10
}
}

// 创建并暴露 store
export default new Vuex.Store({
...
getters
})

组件中读取数据:this.$store.getters.bigSum

  1. mapXxx 的使用
    mapState 方法:用于帮助我们映射 state 中的数据为计算属性。
1
2
3
4
5
6
7
computed:{
// 借助 mapState 生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),

// 借助 mapState 生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
}

mapGetters 方法:用于帮助我们映射 getters 中的数据为计算属性。

1
2
3
4
5
6
7
computed:{
// 借助 mapGetters 生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),

// 借助 mapGetters 生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
}

mapActions 方法:用于帮助我们生成与 actions 对话的方法,即包含 this.$store.dispatch(xxx) 的函数

1
2
3
4
5
6
7
methods:{
// 靠 mapActions 生成:incrementAdd、incrementWait(对象形式)
...mapActions({incrementAdd:'jiaAdd',incrementWait:'jiaWait'}),

// 靠 mapActions 生成:incrementAdd、incrementWait(数组形式)
...mapActions(['jiaAdd','jiaWait'])
}

mapMutations 方法:用于帮助我们生成与 mutations 对话的方法,即:包含 this.$store.commit(xxx) 的函数

1
2
3
4
5
6
7
methods:{
// 靠 mapMutations 生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),

// 靠 mapMutations 生成:increment、decrement(数组形式)
...mapMutations(['JIA','JIAN'])
}

备注:mapActions 与 mapMutations 使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

路由

理解:一个路由(route)就是一组映射关系(key-value),多个路由需要路由器(router)进行管理。对 Vue-router 而言,此处的 key 是 url 路径,value 是组件。

一、基本使用

  1. 安装 vue-router,命令:npm i vue-router
  2. 应用插件 Vue.use(VueRouter)
  3. 编写 router 配置项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 引入 VueRouter
import VueRouter from 'vue-router'
// 引入 layout 组件
import About from '../components/About'
import Home from '../components/Home'

// 创建 router 实例对象,去管理一组一组的路由规则。
const router = new VueRouter({
routes:[
{
path:'/about',
component:'About'
},
{
path:'/home',
component:'Home'
}
]
})
  1. 实现切换(active-class 可配置高亮样式)
    <router-link to="/about" active-class="active">About</router-link>

  2. 指定展示位置
    <router-view></router-view>

几个注意点

  1. 路由组件通常存放在 page 文件夹,一般组件通常存放在 components 文件夹。
  2. 通过切换隐藏了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的 $route 属性,里面存储着自己的路由信息。
  4. 整个应用只有一个 router,可以通过组件的 $router 属性来获取到。

二、多级路由(嵌套路由)

  1. 配置路由规则,使用 children 配置项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[ //通过 children 配置子级路由
{
path:'news', //此处一定不要写:/news
component:News
},
{
path:'message', //此处一定不要写:/message
component:Message
},
]
}
]
  1. 跳转(要写完整路径)
    <router-link to="/home/news">News</router-link>

三、路由的 query 参数

传递参数
跳转并携带 query 参数,to 的字符串写法

1
<router-link :to="`/home/message/detail?id=${detail.id}&title=${detail.title}`">跳转</router-link>

跳转并携带 query 参数,to 的对象写法

1
2
3
4
5
6
7
8
9
<router-link>
:to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
}
}"
跳转</router-link>

接收参数
this.$route.query.id
this.$route.query.title

四、命名路由

作用:给路由命名,可以简化路由的跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello', //给路由命名
path:'welcome',
component:Hello
}
]
}
]
}

简化前,需要写完整的路径

1
<router-link to="/demo/test/welcome">跳转</router-link>

简化后,直接通过名字跳转

1
<router-link :to="{name:'hello'}">跳转</router-link>

简化写法配合传递参数

1
2
3
4
5
6
7
8
9
<router-link
:to={
name:'hello',
query:{
id:666,
title:'你好'
}
}
>跳转</router-link>

五、路由的 params 参数

配置路由,声明接收 params 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收 params 参数
component:Detail
}
]
}
]
}

传递参数

跳转并携带 params 参数(to 的字符串写法)

1
<router-link to="/home/message/detail/666/你好啊">跳转</router-link>

跳转并携带 params 参数(to 的对象写法)

1
2
3
4
5
6
7
8
<router-link 
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好啊'
}
}">跳转</router-link>

特别注意:路由携带 params 参数时,若使用 to 的对象写法,则不能使用 path 配置项,必须使用 name 配置!

接收参数
this.$route.params.id
this.$route.params.title

六、路由的 props 配置

作用:让路由组件更方便的收到参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
name:'xiangqing',
path:'detail/:id',
component:Detail,

//第一种写法:props 值为对象,该对象中所有 key-value 的组合最终都会通过 props 传给 Detail 组件(只能传递死数据)
// props:{a:900}

//第二种写法:props 值为布尔值,布尔值为 true,则把路由收到的所有 params 参数通过 props 传给 Detail 组件
// props:true

//第三种写法:props 值为函数,该函数返回的对象中所有 key-value 都会通过 props 传给 Detail 组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
  1. 作用:控制路由跳转时操作浏览器历史记录的模式。
  2. 浏览器的历史记录有两种写入模式:分别为 push 和 replace,push 是追加历史记录,replace 是替换当前记录。路由跳转时默认为 push
  3. 如何开启 replace 模式:<router-link replace ....></router-link>

八、编程式路由导航

作用: 不借助 <router-link> 实现路由跳转,让路由跳转更灵活

具体编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// $router 的两个 API
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})

this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})

this.$router.forward() //前进
this.$router.back() //后退
this.$router.go(数字) //可前进可后退

九、缓存路由组件

作用:让不展示的路由组件保持挂载,不销毁

具体编码

1
2
3
<keep-alive include="News">
<router-view></router-view>
</keep-alive>

缓存多个路由组件

注意:include 中填写组件名!

十、路由守卫

作用:对路由进行权限控制。
分类:全局守卫、独享守卫、组件内守卫。

全局守卫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//全局前置守卫:初始化时执行,每次路由切换前执行
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){// 权限控制的具体规则
next() //放行
}else{
alert('暂无权查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})

//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title
}else{
document.title = 'vue_test'
}
})

独享守卫

1
2
3
4
5
6
7
8
9
10
11
12
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('无权查看')
}
}else{
next()
}
}

组件内守卫

1
2
3
4
5
6
7
8
// 进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){

},
// 离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){

}

十一、路由器的两种工作模式

对于一个 url 来说,什么是 hash 值?—— # 及其后面的内容就是 hash 值
hash 值不会包含在 HTTP 请求中,即:hash 值不会带给服务器。

hash 模式

  1. 地址中永远带着 # 号,不美观。s
  2. 若以后将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不合法。
  3. 兼容性较好

history 模式

  1. 地址干净,美观。
  2. 兼容性和 hash 模式相比略差
  3. 应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题。
Vue2 第三弹 —— 事件总线、插槽