Vuex学习笔记
初始化
-
html引入
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/vuex@3.0.1/dist/vuex.js"></script> <!-- 或者下载下来文件夹引入 -->
-
NPM
npm install vuex --save
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)
what?
- Vuex是一个专为Vue.js应用程序开发的状态管理模式, 采用集中式存储管理应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化
- 我的理解: Vuex就是提取各个组件共同需要用的数据和函数, 类似共享属性和方法, 方便在各个组件间的数据通信
- 状态自管理应用包含三个部分:
3.1 state: 驱动应用的数据源;(data)
3.2 view: 以声明方式将state映射到视图;(template)
3.3 actions: 响应在view上的用户输入导致的状态变化;(methods)
3.4 单向数据流: View --> Actions --> State --> View;(多组件共享状态会破坏单向数据流的简洁性) -
Vuex基本思想: 把组件的共享状态抽取出来, 以一个全局单例模式管理, 组件树构成一个巨大的"视图"
核心
- Store
- 每一个Vuex应用的核心就是store(仓库), 基本上就是一个容器, 包含应用的大部分状态(state)
1.1.1 Vuex的状态存储是响应式的
1.1.2 改变store中的状态的唯一途径就是显式提交(commit) mutation
var store = new Vuex.Store({ state: { count: 0 }, mutations: { increment: function(state){ state.count++ } } }) // store.commit('increment') // console.log(store.state.count) // -> 1 // 最基本的Vuex计数应用Demo // https://jsfiddle.net/n9jmu5v7/1269/
- 每一个Vuex应用的核心就是store(仓库), 基本上就是一个容器, 包含应用的大部分状态(state)
- State
- 单一状态树: 用一个对象包含全部的应用层级状态, 唯一数据源(每一个应用仅仅包含一个store实例)
- 在Vue组件中获取Vuex状态(获取state里面的数据)的方法:
- 计算属性: computed
组件中 computed: { count: function(){ return this.$store.state.count } }
- 辅助函数: mapState(一个组件获取多个状态时候)
在组件中 computed: Vuex.mapState({ count: function(state){ return state.count }, countAlias: 'count', countPlusLocalState: function(state){ return state.count+this.localCount } }) 对象展开运算符 computed: { ...mapState(['state里面的参数','state里面的参数']) }, mounted: function(){ console.log(this.上面...mapState里面的参数就可以获取到) }
- 注意: 封装成组件的局部状态
- 计算属性: computed
- Getter
- 需要从store中的state中派生出一些状态, 例如对列表进行过滤并计数;
- 相当于store的计算属性, 接受state作为第一个参数, 通过store.getters访问;
- getters做为第二个参数,调用getters;
- 组件中使用: this.$store.getters.参数名
- 通过方法访问, 让getter返回一个函数, 实现给getter传参 (在对store里面的数组进行查询时非常有用)
- 注意, getter通过方法访问, 每次都会去进行调用, 而不会缓存结果
- mapGetters辅助函数: 映射局部计算属性
- Mutation
- 更改Vuex的store的状态唯一方法是提交mutation, 每一个mutation都有一个字符串的事件类型和一个回调函数, 接受state第一个参数;
- 提交载荷: 第二个参数以后为载荷参数或者载荷对象
- 遵守响应规则:
- 最好提前在store中初始化所需属性;
- 添加新属性时, 用Vue.set(obj, 'newProp', 123) 或者以新对象替换老的 state.obj = {...state.obj, newProp:123}
- Mutation 必须是同步函数
- 组件提交mutation this$store.commit('xxx'), 或者使用mapMutations辅助函数
- Action
- Action类似于mutation, 不同在于Action提交的是mutation, 而不是直接修改状态, 可以异步操作;
- context对象, context.commit , context.state, context.getters
- 分发Action: store.dispatch('increament')
- 组件分发: this.$store.dispatch('...')
- Module
- 将store分割成模块, 每个模块拥有自己的state, mutation, action, getter
const moduleA = { state: {...}, mutations: {...}, actions: {...}, getters: {...} } const moduleB = { state: {...}, mutations: {...}, actions: {...} } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a /* -> moduleA 的状态 */ store.state.b /* -> moduleB 的状态 */
- 模块的局部状态:对于模块内部的mutation和getter, 接收的都是模块的局部状态对象;而action通过context.state & context.rootState; getter的根节点状态作为第三个参数暴露
const moduleA = { state: { count: 0 }, mutations: { increment(state){ /* state 对象是模块的局部状态 */ state.count++ } }, getters: { doubleCount(state){ return state.count*2 }, sumWithRootCount(state, getters, rootState){ return state.count+rootState.count } }, actions: { incrementIfOddOnRootSum({state, commit, rootState}){ if((state.count + rootState.count) %2===1){ commit('increment') } } } }
- 命名空间:默认情况下,模块内部的action、mutation和getter是注册在全局命名空间的——多模块对同一mutation或action做出响应;如果需要封装和复用,添加 namespaced: true, 对getters、actions、mutations有影响
const store = new Vuex.Store({ modules: { account: { namespaced: true, state: {...}, /* 模块内的状态已经是嵌套的了,使用 namespaced 属性不会对其产生影响 */ getters: { isAdmin(){...0} /* -> getters['account/isAdmin'] */ }, actions: { login () { ... } /* -> dispatch('account/login') */ }, mutations: { login () { ... } /* -> commit('account/login') */ }, /* 嵌套模块 */ modules: { /* 继承父模块的命名空间 */ myPage: { state: { ... }, getters: { profile () { ... } /* -> getters['account/profile'] */ } }, /* 进一步嵌套命名空间 */ posts: { namespaced: true, state: { ... }, getters: { popular () { ... } /* -> getters['account/posts/popular'] */ } } } } } })
- 带命名空间的模块内访问全局内容:
- 全局state和getter, rootState和rootGetter传入getter, 通过context对象属性传入action
- 全局命名空间内分发action或者提交mutation,将{root: true} 作为三参传给dispatch或commit
commit('somMutation', null, {root: true})
- 带命名空间模块注册全局action: 添加root:true, 放在函数handler中
{ actions: { someOhterAction({dispatch}){ dispatch('someAction') } }, modules: { foo: { namespaced: true, actions: { someAction: { root: true, handler(namespacedContext, payload){ ... } } } } } }
- 带命名空间的绑定函数
computed: { ...mapState({ a: state => state.some.nested.module.a, b: state => state.some.nested.module.b, }) }, methods: { ...mapActions([ 'some/nested/module/foo', 'some/nested/module/bar', ]) } /* 简化 */ computed: { ...mapState('some/nested/module', { a: state => state.a, b: state => state.b }) }, methods: { ...mapActions('some/nested/module', [ 'foo', 'bar' ]) } createNamespacedHelpers('some/nested/module')
- 模块重用: 把state写成函数形式, 避免状态对象模块间数据互相污染
- 将store分割成模块, 每个模块拥有自己的state, mutation, action, getter