状态管理模式Vuex安装与应用
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
注意: 并不所项目都需要Vuex,只有vue没有办法解决情况下或者是存储一些被认为是Vue应用中全局变量数据域时你才需要使用Vuex。滥用Vuex会导致全局数据污染不利于维护等问题。所以要记住一句话:“当你不知道是否需要使用vuex的时候就不要使用vuex”
<center>「课堂练习」</center>
了解vuex
在学习Vuex之前,我们尝试不使用Vuex进行一些组件传参的操作
定义四个数据 "user" 用户名、 "location" 当前用户的位置信息、"age" 当前用户的年龄、"theme"主题
创建三个组件 GECHeader GECBody GECFooter
要求
- GECHeader 中包含一个子组件 HeaderMenu
- GECBody 中包含两个子组件 ComponentA ComponentB
- GECFooter 中包含两个子组件 FooterAuthentic FooterPageControl
- 除了HeaderMenu组件自身以外其他所有组件都需要获取 "user" 数据
- 除了 FooterPageControl 和 ComponentB 都要使用 "location"数据
- 除了GECheader组件自身其他组件都要使用 "age"数据
- 除了ComponentA 与 FooterAuthentic组件以外都要使用 "theme"数据
const data = {
user: 'user_185j7sklo', // 除了HeaderMenu组件自身以外其他所有组件都需要获取 "user" 数据
location: '珠吉路60号', // 除了 FooterPageControl 和 ComponentB 都要使用 "location"数据
age: 19, // 除了GECheader组件自身其他组件都要使用 "age"数据
theme: '夜间' // 除了ComponentA 与 FooterAuthentic组件以外都要使用 "theme"数据
}
总结: 通过上面的练习案例,我们发现将一些公共数据使用props传参将数据一级级的传递给多个组件是十分繁琐的。为了解决这些被认为是全局数据的数据共享传参问题,我们引入了Vuex的概念。vuex可以将这些在Vue项目中适合作为全局变量的数据存储起来,在每个需要该数据的组件中通过直接访问vuex使用
Vue 安装
# 直接通过npm安装
npm i -S vuex
#使用Vue cli 搭建项目时在Manually选择自动配置vuex
(*) Choose Vue version
(*) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support
( ) Router
>(*) Vuex
( ) CSS Pre-processors
(*) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
#请将Router 选中,后面一系列的询问直接确认就好
Vuex配置
//文件路径 /src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex) // 将vuex 的提供的配置原型方法注册到 Vue中
export default new Vuex.Store({ // 实例化 Vuex.Store对象 并公开出去
state: {
value: '存放在vuex store中的全局状态'
}
})
// main.js Vue应用入口文件将 store 配置到Vue实例对象中
import Vue from 'vue'
import store from './store' // 引入vuex 的store对象
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
store, // 将vuex 的 store配置到vue实例中
render: h => h(App),
}).$mount('#app')
Vuex的核心概念
State
概念: 是Vuex用来存储数据的地方,存储在state中数据可以看做是当前应用的全局变量可以在当前应用的任何地方访问。
语法:
- 在vue的任何组件中都可以使用this.$store.state.属性名访问
let store = new Vuex.Store({
state: {
value: '存放在vuex store中的全局状态value'
}
})
let vm = new Vue({
el: '#app',
store
})
vm.$store.state.value // '存放在vuex store中的全局状态value'
- 在开发中我们推荐将store中的state赋值给需要使用该状态的组件的计算属性中(一定不能把state赋值给data,state发生改变时不会重新给data赋值)
export default {
components: {
HeaderMenu
},
data() {
return {
// 错误state.user 发生改变时my user不会更新
myUser: this.$store.state.user
}
},
computed: {
user() {
// 正确 state.user会作为当前计算属性的依赖,当state.user发生改变时计算属性user将会重新计算当前值
return this.$store.state.user
}
}
}
- Vuex 为了简化 state与计算属性配合使用时的代码,提供了一个辅助函数mapState 可以简化上面的写法:
使用方法一: mapState可以接收一个字符串数组作为参数,数组中的字符串会自动编程计算属性的属性值并且与vuex 中同名state建立映射对应关系.
<template>
<div>我是组件Authentic 11 {{user}} / {{age}} / {{theme}}</div>
</template>
<script>
// 首先引入辅助函数mapState
import {mapState} from 'vuex'
export default {
/*
computed: {
user() {
return this.$store.state.user
},
age() {
return this.$store.state.age
},
theme() {
return this.$store.state.theme
}
}*/
// 下面的写法等价于上面的写法
computed: mapState(['user','age','theme'])
}
</script>
使用方法二: mapState可以接收一个对象作为参数,对象的key会作为当前组件计算属性名。
value可以是state属性同名字符串,这样state属性就会与对应的key建立映射关系.
value还可以是一个函数,函数会接收当前vuex的state作为参数并返回你想获取的state值(普通函数内部可以通过this访问当前组件实例,而箭头函数不行)
<template>
<div>我是组件A {{myUser}} / {{myLocation}}! / {{isAdult}} / {{theme}}</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
val: 'Hello'
}
},
computed: mapState({
myLocation: 'location',
theme(state) {
// 普通函数内部可以通过this拿到当前组件实例对象的
return state.theme + this.val
},
// 箭头函数无法通过this访问当前组件实例的
myUser: state => state.user,
isAdult: state => state.age >= 18 ? '已成年' : '未成年'
})
}
注意: 如何在使用了辅助函数的组件中创建组件自己的计算属性? 其实mapState辅助函数调用后会返回一个对象 {key: () => {return this.$store.state.key}}
。
ES6的...
扩展运算符就可以将这个对象与普通计算属性对象进行合并,这样就解决了在一个组件中即使用辅助函数又创建一些自身的普通计算属性问题
<template>
<div>我是组件Authentic {{reverseStr}} {{user}} / {{age}} / {{theme}}</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
data() {
return { str: "hello world" };
},
computed: {
reverseStr() {
return this.str.split("").reverse().join("");
},
...mapState(['user','age','theme'])
}
};
</script>
<center>「课堂练习」</center>
使用Vuex实现练习一
请将我们第一个练习使用vuex实现
const data = {
user: 'user_185j7sklo', // 除了HeaderMenu组件自身以外其他所有组件都需要获取 "user" 数据
location: '珠吉路60号', // 除了 FooterPageControl 和 ComponentB 都要使用 "location"数据
age: 19, // 除了GECheader组件自身其他组件都要使用 "age"数据
theme: '夜间' // 除了ComponentA 与 FooterAuthentic组件以外都要使用 "theme"数据
}
Getter
概念: getter就是Vuex的计算属性,开发人员可以将state 或其他getter 计算后的的返回值存放在指定getter中,当前getter会将这些依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
语法: getter会接收一个函数作为参数,该函内部有多个参数,并返回当前getter的值
参数一 state 所有state
参数二 getters 所有getter
在任何组件中都可以通过this.$store.getter.属性名访问
//文件路径 /src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
person: [
{
name: '小明',
age: 18
},
{
name: '小东',
age: 11
},
{
name: '老张',
age: 28
},
{
name: '小华',
age: 17
},
{
name: '小文',
age: 22
},
{
name: '小凯',
age: 19
},
],
sort: 1 // 0 升序 1降序
},
getters: {
sortPerson(state) {
return [...state.person].sort((p1, p2) => {
if(state.sort === 0) { // 升序
return p1.age - p2.age
}
return p2.age - p1.age // 降序
})
}
}
})
注意: getter在组件中依然存放在组建的计算属性中
<template>
<div>
我是组件D
<h3 v-for="(p, i) in person" :key="i">
{{p.name}} // {{p.age}}
</h3>
</div>
</template>
<script>
export default {
computed: {
person() {
return this.$store.getters.sortPerson
}
}
}
</script>
注意: Vuex为 getters 同样提供了辅助函数 mapGetters
使用方法一: mapState可以接收一个字符串数组作为参数,数组中的每一项字符串都会成为当前组件的计算属性并且与Vuex中的同名getter建立映射对应关系。
<template>
<div>
我是组件D
<h3 v-for="(p, i) in sortPerson" :key="i">
{{p.name}} // {{p.age}}
</h3>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
person() {
return this.$store.getters.sortPerson
},
...mapGetters(['sortPerson'])
}
}
</script>
使用方法二: mapGetters可以接收对象作为参数,对象的每一个key都会成为当前组件的计算属性名,value必须是一个字符串并且与Vuex中的同名getter建立映射对应关系。
<template>
<div>
我是组件D
<h3 v-for="(p, i) in p1" :key="i">
{{p.name}} // {{p.age}}
</h3>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
person() {
return this.$store.getters.sortPerson
},
...mapGetters(['sortPerson']),
...mapGetters({
p1: 'sortPerson'
})
}
}
</script>