状态管理模式,依赖Promise
单项数据流
多组件共享状态时,单项数据流会被破坏 ===》抽取组件的共享状态。以一个全局单例模式管理
在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
1、store 仓库
包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
1.1、Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
1.2、你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
2、state
vue使用单一状态树,每个应用将仅仅包含一个 store 实例
Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex))
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
mapState 辅助函数
对象展开运算符
3、getter
可以理解成store的计算属性
4、mutation
改变store中状态的唯一途径就是提交mutation
必须是同步函数
可以在mutation中提交多个参数,第二个参数叫载荷payload
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
//...
store.commit('increment', {
amount: 10
})
//对象风格
store.commit({
type: 'increment',
amount: 10
})
可以 使用常量代替mutation提交类型
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
5、action
类似于mutation,区别:提交的是mutation;异步
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
//接受一个与 store 实例具有相同方法和属性的 context 对象,所以可以commit、getter等
increment (context) {
context.commit('increment')
}
}
})
分发store.dispatch?????
6、module
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 的状态
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。
7、项目结构
8、插件
8.1、就是一个函数,接受store作为唯一的参数
const myPlugin = store => {
// 当 store 初始化后调用
store.subscribe((mutation, state) => {
// 每次 mutation 之后调用
// mutation 的格式为 { type, payload }
})
}
// ...
const store = new Vuex.Store({
// ...
plugins: [myPlugin]
})
注意插件内也不能直接修改store,只能通过mutation来修改,
8.2、有时候插件需要获得状态的“快照”,比较改变的前后状态。想要实现这项功能,你需要对状态对象进行深拷贝
const myPluginWithSnapshot = store => {
let prevState = _.cloneDeep(store.state)
store.subscribe((mutation, state) => {
let nextState = _.cloneDeep(state)
// 比较 prevState 和 nextState...
// 保存状态,用于下一次 mutation
prevState = nextState
})
}
生成状态快照的插件应该只在开发环境中使用,这一点个可以通过webpack来控制
const store = new Vuex.Store({
// ...
plugins: process.env.NODE_ENV !== 'production'
? [myPluginWithSnapshot]
: []
})
8.3、logger插件,日志插件用来一般的调试,会生成状态快照,仅在开发环境使用
import createLogger from 'vuex/dist/logger'
const store = new Vuex.Store({
plugins: [createLogger()]
})
//...
//几个配置项
const logger = createLogger({
collapsed: false, // 自动展开记录的 mutation
filter (mutation, stateBefore, stateAfter) {
// 若 mutation 需要被记录,就让它返回 true 即可
// 顺便,`mutation` 是个 { type, payload } 对象
return mutation.type !== "aBlacklistedMutation"
},
transformer (state) {
// 在开始记录之前转换状态
// 例如,只返回指定的子树
return state.subTree
},
mutationTransformer (mutation) {
// mutation 按照 { type, payload } 格式记录
// 我们可以按任意方式格式化
return mutation.type
},
logger: console, // 自定义 console 实现,默认为 `console`
})
9、严格模式
const store = new Vuex.Store({
// ...
// 不要在发布环境中开启严格模式,在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。有可能造成性能损失
strict: process.env.NODE_ENV !== 'production'
})
10、表单处理
对于希望对vuex中store里数据进行双向绑定时
10.1、vuex的思路
<input :value="message" @input="updateMessage">
//...
computed: {
...mapState({
message: state => state.obj.message
})
},
methods: {
updateMessage (e) {
this.$store.commit('updateMessage', e.target.value)
}
}
//...
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
10.2、使用带有 setter 的双向绑定计算属性
<input v-model="message">
//...
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
11、测试
检测state值是否达到预期,那么我们主要是针对 Vuex 中的 mutation 和 action 进行单元测试,
11.1、motation
比较好测,就是一群一栏参数的函数 ,调用看看是不是符合预期就好
11.2、action
这个链接很棒:https://zhuanlan.zhihu.com/p/75513490
Action 应对起来略微棘手,因为它们可能需要调用外部的 API。当测试 action 的时候,我们需要增加一个 mocking 服务层——例如,我们可以把 API 调用抽象成服务,然后在测试文件中用 mock 服务回应 API 调用。为了便于解决 mock 依赖,可以用 webpack 和 inject-loader 打包测试文件。
12、热加载
对于 mutation 和模块,你需要使用 store.hotUpdate() 方法