store 是通过 mp-store
导出的 createStore
这个方法来创建的,这个方法接受三个参数
store 会把原生的 Page
、Component
函数包装一层,来做一层拦截,这样 store 就可以收集到所有使用到全局状态的组件,也就是收集依赖。这也就是为什么 store 对原有的功能不影响的原因,下面是 store 的一些介绍
每个 store 拥有唯一的 id,可以通过 store.id 拿到
store 的 state 是不可变的,所以你不能这样操作 this.store.state.xx = 1
store 会把用到的全局状态放到所用到的 data 中去,默认的命名空间是 global,所以你可以这样在组件中拿到当前所用到的全局状态 this.data.global,如果与原有的项目有冲突,你可以在 App 初始化之前调用 setNamespace 方法更改为其他的名字
store 会在 onLoad 和 attached 被调用之前被注入进实例中,依赖的收集也是在这个时间进行的
store 会被注入到 page 和 component 示例中,所以你可以通过 this.store 来拿到 store
env: 'develop' | 'product'
: 如果指定为product
,会去掉状态和模块合并时的检测,state 将不会被冻结住,从而提升性能storeNamespace: string
: 可以指定注入到组件内部的 store 命名空间,默认为store
globalNamespace: string
: 可以指定注入到组件 data 中使用的全局状态命名空间,默认为global
import createStore from '@ruslte/mp-store'
const store = createStore(() => {}, {})
store.add
store.dispatch
store.restore
store.forceUpdate
store.use
store.setNamespace
store.getModule
store.addModule
add
方法用于添加一个 reducer
, action
是唯一的,如果有重复,将会报错,reducer
的类型如下
interface Reducer{
namespace?: string
partialState?: Object
setter?: (state: module, payload: any, rootState?: Store['state'] ) : Object
}
partialState
定义的状态将被合并到全局状态,如果里面包含着全局状态已有的字段,是不被允许的,setter
函数可以用来返回一个对象,这个对象将被合并到全局状态中
store.add('action', {
partialState: {
name: 'tao',
},
setter (state, payload) {
return { name: payload }
},
})
console.log(store.state.name) // 'tao'
store.dispatch('action', 'imtaotao')
console.log(store.state.name) // 'imtaotao'
dispatch
方法用于触发一个 action
,他会调用所有的中间件,最后在调用 reducer
的 setter
函数,你将不能在 setter
函数中调用 dispatch
方法,以下的写法将会报错。这样做的愿意是为了避免逻辑过于混乱,但你可以在中间件中调用 dispatch
store.add('action', {
partialState: {
name: 'tao',
},
setter (state, payload) {
store.dispatch('xx') // 这一句将会报错
return { name: payload }
},
})
store.dispatch('action', 'imtaotao')
dispatch
这个行为触发时,对 store 的状态更改是同步的,对视图的更新是异步的。mpStore 对视图的更新是批量的
store.add('action', {
partialState: {
name: 'chen',
},
setter (state, payload) {
return { name: payload }
},
})
Page({
storeConfig: {
useState (store) {
return ({
name: state => state.name,
})
},
},
onLoad() {
this.data.global.name // 'chen'
this.store.dispatch('action', 'tao', () => {
// 或者在回调里面拿到最新的值
this.data.global.name // 'tao'
})
this.data.global.name // 'chen',store 的更新是同步的
this.store.state.name // 'tao'
// 由于视图的更新是异步的,所以可以这样
setTimeout(() => {
this.data.global.name // 'chen'
})
}
})
callback
方法会在 store.state
改变后,所有依赖的组件更新后(包括视图)调用,因为中间件中可能会处理异步行为,所以这个 callback
的存在是必要的
store.add('action', {
partialState: {
name: 'tao',
},
setter (state, payload) {
return { name: payload }
},
})
store.use((payload, next) => {
setTimeout(next(payload))
})
console.log(store.state.name) // 'tao'
store.dispatch('action', 'imtaotao', () => {
console.log(store.state.name) // 'imtaotao'
})
console.log(store.state.name) // 'tao'
restore
会将定义的 reducer 恢复到初始状态。通常的情况下,我们需要在组件卸载时,及时清空相应的状态,为此而定义对应的 reducer,这一步完全可以由 mpStore 来做,所以这也是 restore
这个 api 出现的原因。restore
恢复为初始状态时,也会去异步更新依赖的组件,第二个参数与 dispatch
方法中的 callback 基本一致,除了接受的参数为初始状态之外,用法如下:
store.add('action', {
partialState: { name: 'tao' },
setter (state, payload) {
return { name: payload }
}
})
store.dispatch('action', 'chen', () => {
store.state.name // 'chen'
store.restore('action', initState => {
initState // { name: 'tao' }
store.state.name // 'tao'
})
})
如果你希望在恢复初始状态后,不需要更新视图的变化,可以将 notUpdate
传为 true
forceUpdate
会强制所有依赖的组件走一遍 diff -> patch 的过程,从而更新视图,当你用的这个方法时,99% 都是你自己的代码写的有问题。
store 默认会在组件的 data 中添加 global
来接受用到的全局状态,如果需要更改,可以通过此方法。需要注意的是你必须在 App
初始化之前调用(此方法已经被弃用,请使用 options.globalNamespace 替换)
store.setNamespace('xxx')
App({
// ...
})
use
方法用来添加中间件,中间件的详细文档在这里可以看到。如果只传了一个参数,则默认拦截所有的 action
,也可以传一个数组,选择性的拦截你想要的 action。use 方法会返回一个 remove 函数,用来注销掉当前添加的中间件
// 将会拦截 `changed` 这个 action
// 中间件的添加顺序为执行顺序,所以你必须调用 next,否则,后面添加的中间件将不会执行
const remove = store.use('changed', (payload, next) => {
payload++
next(payload)
})
如果是一个数组,则会选择性的拦截
const remove = store.use(['one', 'two'], (payload, next, action) => {
payload++
next(payload)
})
下面这种语法将会拦截所有的 action
const remove = store.use((payload, next, action) => {
if (action === 'changed') {
payload++
next(payload)
}
})
// 注销掉中间件
remove()
getModule
可以根据 namespace 得到一个 module,如果 namespace 为一个空字符串,则会返回 rootModule(就是全局 state)
store.add('action', {
namespace: 'a.b',
partialState: {
name: 'tao'
},
})
const module = store.getModule('a.b')
console.log(module) // { name: 'tao' }
console.log(module === store.state.a.b) // true
addModule
用于添加一个模块,也是添加多个 reducer,共用一个 namespace,底层也是调用 store.add
方法,所以这只是一个语法糖,你也可以自己封装一套你自己熟悉的语法
const s = Symbol()
store.addModule('a.b', {
// key 作为 action, value 作为 reducer
[s]: {
partialState: { name: 'tao' },
setter (state, payload) {},
},
'action': {
partialState: { age: 24 },
setter (state, payload) {},
},
})
console.log(store.state) // { a: { b: { name: 'tao', age: 24 } } }
整个 store 拥有一个唯一的 state,这个全局 state 是不可变的,所以在 store 中,数据的转换都是通过深拷贝来进行的,这带来了一定的性能消耗,但为了数据不被污染,牺牲一定的性能是值得的,为此,mpstore 暴露出来了一个 clone
方法。需要注意的是,这个方法会深拷贝数据,并允许拷贝循环引用的数据结构
import createStore, { clone } from '@ruslte/mp-store'
const newData = clone({
...
})