目录
2.1 this.$store.state.xxx形式获取状态数据
3.1 $store.getters.xxx形式获取计算属性
4.1 $store.commit( ) 提交mutation
5.1 $store.dispatch( ) 分发action
1. 状态管理
1.1 vue组件共享数据
在单页面应用程序中,路由和组件关联,采用单文件组件组织 vue程序。
➢优点:
✓提高模块的独立性,便于代码重用和维护。
➢缺点:
✓组件与组件(即不同路由页面)之间必然需要进行数据通信,vue中实现组件间共 享数据比较繁琐。
组件间共享数据方式
父子组件间:
父组件向子组件传递props数据,子组件向父组件$emit自定义事件。
➢缺点
✓只能对于父子组件有效
✓状态数据更新位置不统一,不利于代码维护
非父子组件间:
使用全局事件总线(全局的vue实例)
➢缺点
✓操作繁琐(需要在每个组件的生命周期回调方 法中监听事件)
✓状态数据更新零散,不利于代码维护
借助本地存储(如localStorage或sessionStorage)实现数据共享
➢缺点
✓操作繁琐(需要在每个组件的生命周期 回调方法中获取数据)
✓不能实现数据响应式更新
✓状态数据管理零散,不利于代码维护
1.2 vuex状态管理
vue官方推荐使用vuex进行状态管理,实现组件间数据共享
➢vuex把组件间的所有状态数据集中到一处进行统一管理,并提供统一的 操作接口进行状态更新。
✓使用 state 存储状态数据
✓使用 mutations 更新数据
vue官方推荐使用vuex进行状态管理,实现组件间数据共享。
➢vuex也集成到 Vue 的官方调试工具 vue-devtools 中,提供了诸如零配 置的 time-travel 调试、状态快照导入导出等高级调试功能
1.3 安装和使用vuex
1.3.1 安装
vuex安装和使用
➢安装 vuex : vue add vuex
✓自动在 vue-cli 程序中创建 store.js 文件
✓Vuex.Store对象:状态数据管理对象
➢Store对象默认包含三部分内容
✓state:状态数据
✓mutations:更新状态数据的操作方法
✓actions:异步操作方法
安装示例:
vue add vuex
自动生成如下代码:
1.3.2使用
➢使用
✓Store对象定义状态数据:在 state 部分定义状态数据
✓组件中使用状态数据:使用 this.$store 获取状态数据管理对象
• 每个组件都会获取到状态数据
• this.$store.state 获取状态数据信息
使用示例:
在store的state中定义数据
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
message: 'this is state message.'
},
mutations: {
},
actions: {
},
modules: {
}
})
在页面组件App.vue中使用数据
<template>
<div id="app">
<p>
message = {{$store.state.message}}
</p>
</div>
</template>
运行后,使用devtoos查看状态数据:
2. state状态管理
2.1 this.$store.state.xxx形式获取状态数据
在Vuex.Store对象中,使用 state属性存储状态数据
➢在组件中获取state信息时,可以使用 this.$store.state.*** 形式获取状态数据
➢state数据建议在组件的 computed 属性中使用,以实现响应式更新
示例:
定义状态数据
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
message: 'this is state message.',
user: {
name: 'test',
age: 18
},
bookList: [
{bookName: 'java讲义', price: 108.5},
{bookName: '安卓讲义', price: 88.5},
{bookName: 'IOS讲义', price: 77.9}
]
},
mutations: {
},
actions: {
},
modules: {
}
})
App.vue组件和HelloWorld.vue组件的计算属性中使用状态数据
<template>
<div id="app">
<HelloWorld/>
<p>
message = {{$store.state.message}}
</p>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
export default {
name: 'App',
components: {HelloWorld},
computed: {
message(){
return this.$store.state.message
}
}
}
</script>
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
computed: {
msg(){
return this.$store.state.message
}
}
}
</script>
使用devtoos查看状态数据:
2.2 mapState辅助函数
可以使用 mapState 辅助函数,以简写形式在组件内获取state数据
✓使用之前,要首先引入 mapState 辅助函数
✓在计算属性中处理状态数据
✓若直接使用状态数据原始名称,可以直接使用数组形式
示例:
<template>
<div id="app">
<p>
message = {{message}}
</p>
<p>
user = {{user}}
</p>
<p>
bookList = {{bookList}}
</p>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'App',
computed: mapState({
message: function(state){
return state.message
},
user: state => state.user,
bookList: 'bookList'
})
}
</script>
若直接使用状态数据原始名称,可以直接使用数组形式:
<template>
<div id="app">
<p>
message = {{message}}
</p>
<p>
user = {{user}}
</p>
<p>
bookList = {{bookList}}
</p>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'App',
computed: mapState(['message', 'user', 'bookList'])
}
</script>
computed中既有状态数据又可以有自已的计算数据:
<template>
<div id="app">
<p>
message = {{message}}
</p>
<p>
user = {{user}}
</p>
<p>
bookList = {{bookList}}
</p>
<p>
localComputed = {{localComputed}}
</p>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'App',
data() {
return {info: ' this is App'}
},
computed: {
// 当前组件自身计算属性
localComputed(){
return 'hello' + this.info
},
// 使用对象展开运算符引入状态数据
...mapState(['message', 'user', 'bookList'])
}
}
</script>
3. getters计算属性
问题:如果在HelloWorld.vue组件中也需要以上功能,怎么办?是复制代码?
解决办法:
使用getters计算属性
3.1 $store.getters.xxx形式获取计算属性
在vuex中使用 getters 定义状态数据的计算属性,方便为多个组 件提供统一的过虑数据
➢定义getters
➢ 在组件中使用$store.getters.xxx形式获取gettes数据
示例:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
bookList: [
{bookName: 'java讲义', price: 108.5},
{bookName: '安卓讲义', price: 88.5},
{bookName: 'IOS讲义', price: 77.9}
]
},
getters: {
bookNameList: state => {
return state.bookList.filter(book => {
return book.price > 80
})
}
},
mutations: {
},
actions: {
}
})
<template>
<div id="app">
<p>
bookNameList = {{$store.getters.bookNameList}}
</p>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {info: ' this is App'}
},
}
</script>
3.2 mapGetters 辅助函数
组件中使用getters
➢使用 mapGetters 引入 getters 数据
✓首先引入 mapGetters 辅助函数
✓在组件的计算属性中,引入getters数据
在vuex中使用 getters 定义状态数据的计算属性示例:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
message: 'this is state message.',
user: {
name: 'test',
age: 18
},
bookList: [
{bookName: 'java讲义', price: 108.5},
{bookName: '安卓讲义', price: 88.5},
{bookName: 'IOS讲义', price: 77.9}
]
},
getters: {
bookNameList: state => {
return state.bookList.filter(book => {
return book.price > 80
})
}
}
})
组件中使用getters示例:
<template>
<div id="app">
<p>
bookNameList = {{bookNameList}}
</p>
<HelloWorld></HelloWorld>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import {mapGetters} from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
computed: {
// 使用对象展开运算将getter混入computed对象中
...mapGetters([
'bookNameList'
])
}
}
</script>
使用devtools查看组件的计算属性:
使用devtools的状态面板可以看到状态数据中state和getters数据:
同样在HelloWorld.vue视图组件中也可以使用getters计算数据:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>bookNameList= {{bookNameList}}</p>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
name: 'HelloWorld',
computed: {
msg(){
return this.$store.state.message
},
// 使用对象展开运算将getter混入computed对象中
...mapGetters([
'bookNameList'
])
}
}
</script>
参考文档
https://vuex.vuejs.org/zh/guide/state.html
https://vuex.vuejs.org/zh/guide/getters.html
4. mutations更新数据
• 严格模式下,vuex不允许在组件 内部直接修改 $store.state 中的 数据,也不允许直接修改 computed 数据
• strict模式下更改 Vuex.Store 中 的状态的唯一方法是提交 mutation
开启严格模式:
更改 Vuex.Store 中的状态的唯一方法是提交 mutation
➢在 Store 对象中定义 mutations
✓每一个mutation 是函数形式,表示状态数据对外提供的操作接口
✓函数接收回调参数 state ,表示要处理的状态数据集合
示例:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 开启严格模式
strict: true,
state: {
message: 'this is state message.',
user: {
name: 'test',
age: 18
},
bookList: [
{bookName: 'java讲义', price: 108.5},
{bookName: '安卓讲义', price: 88.5},
{bookName: 'IOS讲义', price: 77.9}
]
},
getters: {
bookNameList: state => {
return state.bookList.filter(book => {
return book.price > 80
})
}
},
// 严格模式下,修改状态数据
mutations: {
// 修改状态数据
mutationMessage(state){
// state表示当前的状态数据
state.message = 'mutation updated messge.'
}
}
})
4.1 $store.commit( ) 提交mutation
更改 Vuex.Store 中的状态的唯一方法是提交 mutation
➢在组件中使用 $store.commit( ) 提交mutation更新
直接更新state状态数据则报错:
<template>
<div id="app">
<p>
bookNameList = {{bookNameList}}
</p>
<HelloWorld></HelloWorld>
<button @click="updateMsg">更新</button>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import {mapGetters,} from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
updateMsg(){
// 不允许直接更新状态数据
this.$store.state.message = 'hello world'
}
},
computed: {
// 使用对象展开运算将getter混入computed对象中
...mapGetters([
'bookNameList'
])
}
}
</script>
运行后点击更新发现出错:
示例:
使用提交mutations的方法更新state数据
<template>
<div id="app">
<p>
bookNameList = {{bookNameList}}
</p>
<HelloWorld></HelloWorld>
<button @click="updateMsg">更新</button>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import {mapGetters,} from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
updateMsg(){
// 提交mutation更新状态数据
this.$store.commit('mutationMessage')
}
},
computed: {
...mapGetters([
'bookNameList'
])
}
}
</script>
在devtools中查看state数据的改变:
4.2 mapMutations 辅助函数
➢在组件中使用 mapMutations 辅助函数快速引入 mutation操作到当前 组件的方法中
✓组件中直接调用方法即可
示例:在组件中使用 mapMutations 辅助函数快速引入 mutation操作到当前 组件的方法中
<template>
<div id="app">
<p>
bookNameList = {{bookNameList}}
</p>
<HelloWorld></HelloWorld>
<button @click="updateMsg">更新</button>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import {mapGetters, mapMutations} from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
// 快速引入mutation方法
...mapMutations(['mutationMessage']),
updateMsg(){
// 直接调用mapMutations引入的方法
this.mutationMessage()
}
},
computed: {
...mapGetters([
'bookNameList'
])
}
}
</script>
4.3 提交mutation时附加参数
更改 Vuex.Store 中的状态的唯一方法是提交 mutation
➢ 定义mutation时接收附加参数
➢在组件中传递参数
示例:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 开启严格模式
strict: true,
state: {
message: 'this is state message.',
user: {
name: 'test',
age: 18
},
bookList: [
{bookName: 'java讲义', price: 108.5},
{bookName: '安卓讲义', price: 88.5},
{bookName: 'IOS讲义', price: 77.9}
]
},
getters: {
bookNameList: state => {
return state.bookList.filter(book => {
return book.price > 80
})
}
},
// 严格模式下,修改状态数据
mutations: {
// 修改状态数据
mutationMessage(state, payload){
// state表示当前的状态数据
// payload表示传入的附加参数,建议使用对象形式
state.message = ('mutation updated messge.' + payload.message)
}
}
})
<template>
<div id="app">
<p>
bookNameList = {{bookNameList}}
</p>
<HelloWorld></HelloWorld>
<button @click="updateMsg">更新</button>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import {mapGetters, mapMutations} from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
...mapMutations(['mutationMessage']),
updateMsg(){
// 直接调用mapMutations引入的方法,并传入附加参数
this.mutationMessage({message: 'this is message mutated from App component'})
}
},
computed: {
...mapGetters([
'bookNameList'
])
}
}
</script>
5. actions异步更新数据
mutations只允许实现同步操作,异步操作方法(如ajax获取数 据)应该交给 actions 处理
使用actions异步更新
➢Store中定义actions
✓每一个 action 是函数形式,表示异步更新数据操作接口
✓函数接收回调参数 context ,表示要状态数据管理对象 Vuex.Store
✓action中不允许直接修改 state中的数据,而要使用 mutation 更新数据
示例:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
strict: true,
state: {
message: 'this is state message.',
user: {
name: 'test',
age: 18
},
bookList: [
{bookName: 'java讲义', price: 108.5},
{bookName: '安卓讲义', price: 88.5},
{bookName: 'IOS讲义', price: 77.9}
]
},
getters: {
bookNameList: state => {
return state.bookList.filter(book => {
return book.price > 80
})
}
},
mutations: {
mutationMessage(state, payload){
state.message = ('mutation updated messge.' + payload.message)
}
},
// 更新更新数据(主要使用ajax加载数据)
actions: {
actionMessage(context){
window.setTimeout(function(){
// 更新状态数据
context.commit('mutationMessage', {message: 'this message is from action'})
}, 1000)
}
}
})
5.1 $store.dispatch( ) 分发action
使用actions异步更新
➢组件methods中使用actions
✓直接通过 $store.dispatch( ) 方法使用
示例:在method中分发action:
<template>
<div id="app">
<p>
bookNameList = {{bookNameList}}
</p>
<HelloWorld></HelloWorld>
<button @click="updateMsg">异步更新</button>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import {mapGetters} from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
updateMsg(){
// 分发一个action异步更新数据
this.$store.dispatch('actionMessage')
}
},
computed: {
...mapGetters([
'bookNameList'
])
}
}
</script>
在devtools中查看state改变:
5.2 mapActions 辅助函
✓使用 mapActions 辅助函数引入到当前组件的methods中
示例:
<template>
<div id="app">
<p>
bookNameList = {{bookNameList}}
</p>
<HelloWorld></HelloWorld>
<button @click="updateMsg">异步更新</button>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import {mapGetters, mapActions} from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
...mapActions(['actionMessage']),
updateMsg(){
// 直接调用mapActions引入的方法
this.actionMessage();
}
},
computed: {
...mapGetters([
'bookNameList'
])
}
}
</script>
参考文档
https://vuex.vuejs.org/zh/guide/mutations.html
https://vuex.vuejs.org/zh/guide/actions.html
6. vuex多模块机制和插件
6.1 vuex的模块化
在大型vuex应用程序中,可以使用 modules 定义多模块的状态数据
➢定义模块对象:本质就是 Vuex.Store 对象,包含自己的 state、mutations、 actions 等
➢在Vuex.Store中注册多模块
✓可以为模块修改模块名称
➢在组件中使用多模块
✓使用模块状态数据(state):使用 $store.state.模块名.*** 形式
• 注意:不能直接使用 mapState( ) 辅助函数获取模块内的状态数据
示例:
<template>
<div id="app">
<p>
moduleA name = {{$store.state.ma.name}}
</p>
<p>
moduleB name = {{$store.state.moduleB.name}}
</p>
<HelloWorld/>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
},
computed: {
}
}
</script>
在devtools中查看state数据:
或:
<template>
<div id="app">
<p>
moduleA name = {{ma.name}}
</p>
<p>
moduleB name = {{moduleB.name}}
</p>
<HelloWorld/>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import { mapState } from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
},
computed: {
...mapState({
// 模块的状态数据
ma: state=>state.ma,
moduleB: state=>state.moduleB,
// 全局状态数据
message: state=>state.message
})
}
}
</script>
✓使用getters、mutations和actions:直接和全局模块使用方法相同
• 可以使用 mapGetters、mapMutations和mapActions 辅助函数,获取模块内的指定 类型操作方法
示例:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 定义子模块
const moduleA = {
state: {
name: 'moduleA-name'
},
mutations: {
mutationModuleAName(state){
state.name = 'update A'
}
}
}
const moduleB = {
state: {
name: 'moduleB-name'
},
mutations: {
mutationModuleBName(state){
state.name = 'update B'
}
}
}
export default new Vuex.Store({
strict: true,
// 注册模块
modules: {
ma:moduleA,
moduleB
},
state: {
message: 'this is state message.',
},
getters: {
},
mutations: {
mutationMessage(state, payload){
state.message = payload.message
}
},
actions: {
}
})
<template>
<div id="app">
<p>
moduleA name = {{$store.state.ma.name}}
</p>
<p>
moduleB name = {{$store.state.moduleB.name}}
</p>
<button @click="updateMsg">更新</button>
<HelloWorld/>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld'
import { mapState, mapMutations } from 'vuex'
export default {
name: 'App',
components: {HelloWorld},
data() {
return {info: ' this is App'}
},
methods: {
...mapMutations(['mutationMessage']),
updateMsg(){
// 直接调用mapMutations引入的方法
this.mutationMessage({message: 'this is messsage from App component '})
// 更新模块A的name值
this.$store.commit('mutationModuleAName', {name: 'this is module A name from App componet'})
}
},
computed: {
...mapState({
ma: state=>state.ma,
moduleB: state=>state.moduleB,
message: state=>state.message
})
}
}
</script>
6.2 vuex插件
vuex中允许定义和使用插件,增强vuex的功能
➢vuex状态数据一般要和本地存储结合起来使用
✓vuex状态数据只对当前SPA程序有效,当手动刷新页面时,状态数据会丢失
✓本地存储不能响应式修改状态数据
当页面刷新时state数据又恢复原来了的数据了:
解决办法 :使用vuex插件实现 vuex状态数据 和 本地存储的结合使用
➢安装方法:使用 npm 或 yarn 安装 vuex-persistedstate
➢使用方法
✓引入 vuex-persistedstate ✓在 Store 对象中 使用 plugins
参考文档:
https://vuex.vuejs.org/zh/guide/modules.html
https://vuex.vuejs.org/zh/guide/plugins.html
https://www.npmjs.com/package/vuex-persistedstate
7. 组织vuex程序的目录结构
在大型项目中store非常庞大,这时需要对他进行模块化组织
1. 创建一个mutations.js ,放全局的mutation
2. 创建一个actions.js放全局的action
3. 在index.js中导入mutation.js和action.js
4. 创建module子目录,把不同模块的store放到单独的js中:
在module中创建一个index.js
5. 在store的index.js中,创建Vuex.Store时引入模块的index.js
建议只对action与mutation进入模块分离,state与getters不做分离