vue系列-vuex
Vuex官网:https://vuex.vuejs.org/zh/
基础
vuex是什么
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
状态自管理应用
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
单向数据流
使用场景
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式 就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
核心内容
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的**状态 (state)**。Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
store
store 就是一个数据仓库,为了更方便的管理仓库,我们把一个大的 store 拆成一些 modules ,整个 modules 是一个树型结构。每个 module 又分别定义了 state、getters、mutations、actions,通过递归遍历模块的方式完成了它们的初始化。
state
从 store 实例中读取状态最简单的方法就是在 计算属性 中返回某个状态:
如每当 store.state.count
变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。
然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入,并且在测试组件时需要模拟状态。
Vuex 通过 store
选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)
)。
通过在根实例中注册 store
选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store
访问到。
getters
从 state 中派生出一些状态,可以认为是 store 的计算属性。例如对列表进行过滤并计数:
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
注意,在 Vue computed 中使用 getter时, getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。
mutations
mutation 是同步进行 state 数据操作的。
更改 store 中的状态。
每个 mutation 都有一个字符串的事件类型(type)和一个回调函数(handler)
注意:
- 最好提前在 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,应该建立起响应式关系:
- 使用
Vue.set(obj, 'newProp', 123)
- 以新对象替换老对象
- 使用
actions
action 可以进行异步操作。action 提交的是 mutation。
内部会进行 Promise 化,最终调用结果返回是一个 promise 。
使用 store.dispatch
的方法进行派发 action。
dispatch (_type, _payload) {
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
// ...
const result = entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
// ...
}
promise.all 可以最大限度进行异步操作并发
在多个action中修改同一个state,因为很有可能每个action赋给state的新值都有所不同,并且不能保证最后一个有返回结果action是哪一个action,所以最后赋予state的值可能是错误的
modules
可以将 Vuex
分为多个模块甚至嵌套子模块,但是需要注意命名空间的使用。
API
mapState
mapGetters
mapActions
mapMutations
createNamespacedHelpers
通过使用 createNamespacedHelpers
创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// 在 `some/nested/module` 中查找
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// 在 `some/nested/module` 中查找
...mapActions([
'foo',
'bar'
])
}
}
Vuex 提供这些 API 都是方便我们在对 store 做各种操作来完成各种能力,尤其是 mapXXX 的设计,让我们在使用 API 的时候更加方便,这也是我们今后在设计一些 JavaScript 库的时候,从 API 设计角度中应该学习的方向。
插件
Vuex 从设计上支持了插件,让我们很好地从外部追踪 store 内部地变化,Logger 插件在我们地开发阶段也提供了很好地指引作用。当然我们也可以自己去实现 Vuex 的插件,来帮助我们实现一些特定的需求。
源码
工具函数
深拷贝
https://datura35422.github.io/posts/57752/#Vuex%E6%BA%90%E7%A0%81
循环对象键值对
/**
* forEach for object
*/
export function forEachValue (obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key))
}
判断数据类型
判断数据是否是对象应该还要考虑数组的情况 typeof [] === 'object'
判断数据是否是 promise
类型,其实就是判断 thenable
类型
export function isObject (obj) {
return obj !== null && typeof obj === 'object'
}
export function isPromise (val) {
return val && typeof val.then === 'function'
}
高级函数
使用闭包的方式将函数和参数存储起来,延迟执行。
export function partial (fn, arg) {
return function () {
return fn(arg)
}
}
面试题
基于Vue设计一个购物车(组件结构,vuex state 数据结构)
更多阅读
https://github.com/zero2one3/Vuex-Analysis
学习 vuex 源码整体架构,打造属于自己的状态管理库 - 若川视野:https://github.com/lxchuan12/vuex-analysis
https://ustbhuangyi.github.io/vue-analysis/v2/vuex/#vuex-%E6%A0%B8%E5%BF%83%E6%80%9D%E6%83%B3