最近公司项目中使用Vuex做状态管理,就全面温习了一遍文档,然后在项目使用中遇到一些常见问题就一一总结下。

一、由来

我们知道Vue中数据是自顶向下单向流动的,但是以下两种情况单向数据流实现起来十分繁琐且不易维护:

  1. 多个视图依赖同一状态;
  2. 来自不同视图需要变更统一状态。

因此,Vuex诞生了。我们需要把不同组件的共享状态抽离出来,放在全局单例中管理,这样我们的组件树构成一个巨大的“视图网”,任何组件都可以获取共享状态并且以相同的规则变更状态。

Vuex都有一个“store”,包含应用中大部分状态。Vuex和全局对象有以下两点不同

  1. Vuex中的状态是响应式的,只要store中的状态发生改变,其他组件也会得到高效更新;
  2. store中的状态不能直接改变,只能显式的提交mutation来更新。

二、概念

State

Vuex使用单一状态树,state作为应用的唯一数据源而存在。当我们需要从store获取状态时,只需在组件计算属性中直接返回即可。使用mapState辅助函数可以更方便我们生成计算属性。

state.js

const state = {
count: 0
} export default state

component.js

import { mapState } from 'vuex'
export default {
name: 'Vuex',
data() {
return {
}
},
computed: {
localComputed: () => {},
...mapState({
count: state => state.count
})
},
methods: {
}
}

Vuex并不意味着所有状态都必须放在store中,若有些状态属于单个组件,最好作为组件的局部状态存在为好。

Getters

getter通俗来讲就是state的计算属性,方便我们从state中派生出一些状态出来。getter接受state、getter作为其第一个、第二个参数。

const getters = {
derCount: state => {
return state.count+1
},
doneLists: (state, getters) => {
return state.todoLists.filter(list => list.status)
},
todoCount: (state, getters) => {
return state.todoLists.length - getters.doneLists.length
},
} export default getters

获取getter对象可通过属性访问this.$store.getters.doneLists,同样我们可以通过mapGetters辅助函数获取:

...mapGetters([
'doneLists',
'todoCount'
])

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,只有mutation中才能直接操作state。Vuex 中的 mutation 非常类似于事件,不能直接调用mutation handler。你需要先在mutattion中注册handler,然后在action或组件中调用此函数。每个mutation接受state,payload作为其第一个、第二个参数。

const mutations = {
addCount: (state) => {
state++
},
updateList: (state, index) => {
let updateList = state.todoLists[index]
let status = updateList.status
status = status?0:1
state.todoLists[index].status = status
}
} export default mutations

Vuex的store是响应式的,因此使用mutation时注意以下事项:

  1. 最好提前在你的 store 中初始化好所有所需属性;
  2. mutation必须是同步函数;
  3. 当需要在对象上添加新属性时,你应该:
  • 使用 Vue.set(obj, 'newProp', 123), 或者
  • 以新对象替换老对象。例如,利用 stage-3 的对象展开运算符我们可以这样写:
state.obj = { ...state.obj, newProp: 123 }

你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit

  methods: {
localMethods() { },
...mapMutations([
'updateList',
'addCount'
])
}

Action

action类似于mutation,不同之处在于:

  1. action提交的是mutation,而不是直接变更状态,无法直接操作state;
  2. action支持异步操作。

action函数接受和store相同属性、方法的context对象,同样支持提交载荷方式。action通过store.dispatch的方式来分发。

const actions = {
addCountAsync: ({commit}) => {
commit('addCount')
},
deleteListAsync: ({commit}, index) => {
setTimeout(() => {
commit('deleteList',index)
},1000)
}
} export default actions

你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store

...mapActions([
'addCountAsync',
'deleteListAsync' //将this.deleteListAsync()映射为this.$store.dispatch('deleteListAsync')
]),

Module

Vuex是单一状态树,应用所有状态都集中在一个比较大的对象中。当项目足够大时,store对象会变得很臃肿。Vuex允许我们分割store为子模块,每个modules都拥有自己的state、getters、mutations、actions、mudules。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。

  • getter接受参数依次为局部state、getters、根节点状态rootState、根节点rootGetter;
  • mutation局部状态state、payload作为第一、第二个参数;
  • action局部状态为context.state,根节点状态为context.rootState。{state,commit,rootState,rootGetters};
const state = {
bookLists: [
{name: '三国演义', status: 1},
{name: '西游记', status: 1},
{name: '红楼梦', status: 0},
{name: '水浒传', status: 0}
]
} // getters
const getters = {
/**
* @param {[type]} state、getters [局部状态]
* @param {[type]} rootState、rootGetters [根节点状态]
*/
readBook: (state, getters, rootState, rootGetters) => {
return state.bookLists.filter(list => list.status)
}
} // mutations
const mutations = {
// state 局部状态
readOver (state, { index }) {
state.bookLists[index].status = 1
},
reRead (state, { index }) {
state.bookLists[index].status = 0
},
delete (state, { index }) {
state.bookLists.splice(index, 1)
}
} // actions
const actions = {
/**
* dispatch、commit局部化
* 提交是接受root访问根dispatch、commit
*/
deleteAsync ({ dispatch, commit, state, getters, rootState, rootGetters }, index) {
commit('delete',{index:index})
commit('reduceLast',null,{root:true})
}
} export default {
namespaced: true,
state,
getters,
mutations,
actions
}

若在子模块内部想在全局命名空间提交commit、分发action,将{root:true}作为第三参数传给commit、dispatch即可。

当在组件中使用带命名空间的子模块时,可以将空间名称作为第一参数传给相应map函数:

computed:

...mapState('book', {
bookLists: state => state.bookLists,
}),

methods:

...mapMutations('book', [
'readOver',变更状态
'reRead'
]),
...mapActions('book', [
'deleteAsync',
]),

三、使用技巧

在严格模式下使用Vuex时,使用v-model将state与表单绑定,修改表单值会出现报错。严格模式规定无论何时修改state状态且不是由于mutation引起就会抛出错误。这时我们可以在组件中将所需状态深拷贝一份,进行模板渲染或操作变更,最后再深拷贝一份提交mutation变更状态。这样组件内的操作就不受state影响。

获取深拷贝状态:

stateData() {
let _stateData = JSON.parse(JSON.stringify(this.$store.state.stateData))
this.data = _stateData
return this.$store.state.stateData
}

提交mutation:

setData(data) {
let setData = JSON.parse(JSON.stringify(data))
this.$store.commit('setStateData',setData)
}

Vuex学习心得的更多相关文章

  1. 我的MYSQL学习心得(一) 简单语法

    我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  2. 我的MYSQL学习心得(二) 数据类型宽度

    我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  3. 我的MYSQL学习心得(三) 查看字段长度

    我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  4. 我的MYSQL学习心得(四) 数据类型

    我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(五) 运 ...

  5. 我的MYSQL学习心得(五) 运算符

    我的MYSQL学习心得(五) 运算符 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

  6. 我的MYSQL学习心得(六) 函数

    我的MYSQL学习心得(六) 函数 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...

  7. 我的MYSQL学习心得(七) 查询

    我的MYSQL学习心得(七) 查询 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...

  8. 我的MYSQL学习心得(八) 插入 更新 删除

    我的MYSQL学习心得(八) 插入 更新 删除 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得( ...

  9. 我的MYSQL学习心得(九) 索引

    我的MYSQL学习心得(九) 索引 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...

随机推荐

  1. windows下安装phpredis扩展

    根据phpyinfo获取自己的php信息 x86,php5.6,TS,VC11 在pecl网站上找到对应的版本 5.6 Thread Safe (TS) x86 https://pecl.php.ne ...

  2. MySQL 乱码问题解决

    修改 配置文件 只需留下 my.ini文件,然后修改其编码配置. 配置如下 # Example MySQL config file for large systems. # # This is for ...

  3. oracle 根据时间字段查询

    oracle 根据时间字段查询数据 ROWNUM 是对前面查询的记录做限制,比如查询的记录 > 2000 条,那么只取前面的 2000 条 ''' SELECT * FROM (SELECT C ...

  4. VBA For Each循环

    For Each循环用于为数组或集合中的每个元素执行语句或一组语句.For Each循环与For循环类似; 然而,For Each循环是为数组或组中的每个元素执行的. 因此,这种类型的循环中将不存在步 ...

  5. 安卓开发之利用runOnUiThread在子线程更新UI

    package com.lidaochen.test; import android.graphics.Bitmap; import android.graphics.BitmapFactory; i ...

  6. iOS-CGContextRef

    图形上下文(Graphics Context)---绘制目标 需要在iOS应用程序的屏幕上进行绘制时,需要先定义一个UIView类,并实现它的drawRect:方法,当启动程序时,会先调用loadVi ...

  7. Linux之Vim的使用

    所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是 vim 编辑器. vim 具有程序编辑的能力,可以主动的以字体颜色辨别语法的正 ...

  8. python高并发的详解

    一.什么是高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求. 高并发相关常用的一些指标有响应时间( ...

  9. Linux下制作静态库 & 动态库

    静态库 1.将.c生成.o文件 gcc-cadd.c-o add.o 2.使用ar工具制作静态库 ar rcs lib库名.a add.o sub.o div.o 3.编译静态库到可执行文件中 gcc ...

  10. Linux命令——killall 、kill 、pkill、xkill

    参考:killall .kill .pkill 命令详解 Using kill, killall, and pkill 4 Ways to Kill a Process – kill, killall ...