最近公司项目中使用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. 委托、Action泛型委托、Func泛型委托、Predicate泛型委托的用法

    一.举一委托场景:天气很热,二狗子想去买瓶冰镇可乐,但是二狗子很懒,于是他想找个人代他去,于是要有个代理人. 创建代理人之前先定义委托:public delegate string BuyColaDe ...

  2. CVE-2019-11517 CSRF in Wampserver 3.1.4-3.1.8

    https://www.cnblogs.com/iAmSoScArEd/ Affected product:WampServer 3.1.4-3.1.8 Offiical description:&q ...

  3. git推送代码Gogs报401错误

    1.git push 报错:RPC failed; HTTP 401 curl 22 The requested URL returned error: 401 The remote end hung ...

  4. webstorm编写react native,代码修改后,重新编译运行没有变化的问题

    w我是拷贝一份react native代码到另一台电脑,发现修改代码运行之后不显示修改后的效果,即仍然与原来的效果一样,暂时不知道什么原因, 后来我运行了npm install 就可以了,不知道是不是 ...

  5. 使用pandoc制作幻灯片

    示例Md % Habits % John Doe % March 22, 2005 # In the morning ## Getting up - Turn off alarm - Get out ...

  6. DBCP连接池原理分析(转载)

    DBCP连接池介绍 ----------------------------- 目前 DBCP 有两个版本分别是 1.3 和 1.4. DBCP 1.3 版本需要运行于 JDK 1.4-1.5 ,支持 ...

  7. 说说你对kubernetes的理解(简单)

    目录 整体概述 pod工作流程 k8s网络 flannel 网络策略,network proxy 几套证书理解 组件 master管理节点上组件 node节点 整体概述 k8s是一个编排工具,是谷歌的 ...

  8. Mycat学习-单独启动mycat

    Mycat下载地址:http://mycat.io/ Mycat安装:解压缩即可.   Mycat作为一个中间件,实现mysql协议,是可以不依赖数据库单独运行的. 对前端应用连接来说就是一个数据库, ...

  9. awk初级教程

    参考:sed & awk 概述 sed & awk指令组成 与sed区别 尽管awk指令与sed指令的结构相同,都由模式和过程两部分组成,但过程本身有很大不同. awk看上去不像编辑器 ...

  10. Spring Boot 2发送邮件手把手图文教程

    原文:http://www.itmuch.com/spring-boot/send-email/ 本文基于:Spring Boot 2.1.3,理论支持Spring Boot 2.x所有版本. 最近有 ...