firstore
v1.0.5
Published
A global state management tool for mini-programs, Vue, React, etc.
Downloads
5
Maintainers
Readme
firstore
firstore
是一个全局状态管理工具,可用于小程序
、Vue
、React
等。firstore
的核心部分由state
、actions
、getters
组成(参考了Pinia
):state
中的每一层都由Proxy
代理;- 可以分别对
state
、actions
、getters
进行监听; - 可以为
state
创建还原点。
使用说明
安装
npm install firstore -S
Store
调用
createStore
函数可返回一个store
实例。- 接收参数:
storeName
store
的名称字符串,用以区分不同store
,如果传入已存在的storeName
,则会报错。
config
store
的初始状态对象;- 如果
config
中存在state
、actions
、getters
对象,则createStore
会读取它们作为初始状态,否则它们会被视为{}
。
- 接收参数:
通过
createStore
创建一个store
:// fooStore.js const { createStore } = require('firstore') const fooStore = createStore('foo',{ state:{ // state 中定义状态 name: 'zzc6332', age: 26, isAdmin: true }, actions:{ // actions 中定义一些方法,可以是异步操作 changeName(name){ this.name = name } }, getters:{ // getters 中定义一些计算属性 introduction: (state) => `我是${state.name}, 今年${state.age}岁。` } }) module.exports = fooStore
state
state
中保存store
的状态,在创建store
时定义初始state
,并可以通过多种方式修改。
读取 state
通过
store
实例可直接读取state
中的内容:const fooStore = require('./fooStore') console.log(fooStore.name) // 'zzc6332' console.log(fooStore.age) // 26
通过
store
实例的state
属性可读取整个state
:const fooStore = require('./fooStore') console.log(fooStore.state) // { name: 'zzc6332', age: 26, isAdmin: true }
修改 state
通过
store
实例可直接修改state
中的内容:const fooStore = require('./fooStore') fooStore.name = 'Joie' console.log(fooStore.name) // 'Joie' fooStore.age -= 8 console.log(fooStore.age) // 18
通过
store
实例的$patch
方法可批量修改state
中的内容:const fooStore = require('./fooStore') fooStore.$patch({ name: 'Joie', age: 18 }) console.log(fooStore.state) // { name: 'Joie', age: 18, isAdmin: true }
替换 state
通过
store
实例的$set
方法可替换整个state
:const fooStore = require('./fooStore') fooStore.$set({ name: 'Joie', age: 18 }) console.log(fooStore.state) // { name: 'Joie', age: 18 }
重置 state
通过
store
实例的$reset
方法可重置整个state
至初始状态:const fooStore = require('./fooStore') delete fooStore.name fooStore.name2 = 'Joie' console.log(fooStore.state) // { age: 26, isAdmin: true, name2: 'Joie' } fooStore.$reset() console.log(fooStore.state) // { name: 'zzc6332', age: 26, isAdmin: true }
actions
actions
中定义与当前store
相关的业务逻辑。
定义 actions
action
必须定义为函数;- 如果将一个
action
定义为非箭头函数,则其中的this
指向其所属的store
实例。
使用 actions
通过
store
实例可直接调用actions
中的函数:const fooStore = require('./fooStore') fooStore.changeName('Joie') console.log(fooStore.name) // 'Joie'
getters
getters
中定义一些计算属性。
定义 getters
getter
必须定义为函数;getter
接收state
作为第一个参数;getter
需要将计算的结果作为返回值;- 如果将一个
getter
定义为非箭头函数,则其中的this
指向其所属的store
实例。
读取 getters
当读取一个
getter
时,会得到该getter
函数执行的返回值;通过
store
实例可直接读取getters
中的计算属性:const fooStore = require('./fooStore') console.log(fooStore.introduction) // '我是zzc6332, 今年26岁。'
注意:
如果通过解构赋值的方式将
getters
中的计算属性取出,则取出的为当前的计算值,当依赖发生改变时不会再重新计算:const fooStore = require('./fooStore') const { introduction } = fooStore fooStore.age++ console.log(introduction) // '我是zzc6332, 今年26岁。' console.log(fooStore.introduction) // '我是zzc6332, 今年27岁。'
监听
监听 state
可通过
store
实例的$onState
方法监听state
的变化。接收参数:
identifier
- 需要监听的数据的标识符;
- 例如需监听
store.personList
,则传入'personList'
; - 例如需监听
store.personList[0].name
,则传入'personList[0].name'
; - 如需监听整个
state
,则传入'*'
,此时每一次试图修改state
的操作,若引起了数据变化,都会触发且每次仅触发一次callback
(不论此次操作改变了多少项数据); - 如果需要批量监听,则传入需要批量监听的数据的标识符组成的数组:
- 例如需要批量监听
store.name
和store.age
,则传入[ 'name', 'age' ]
; - 数组中也可包括
'*'
。
- 例如需要批量监听
callback
监听的数据发生变化时执行的回调函数;
定义参数:
mutation
形参会接收一个对象,包含所监听的getter
返回值变化的信息,包括:storeName
- 该监听所属的
store
的名称。
- 该监听所属的
chain
- 监听的目标的标识符,即
$onState
方法中传入的第一个参数identifier
(如果是批量监听,则返回identifier
数组中对应的标识符); - 如果监听的标识符是
'*'
,则mutation
对象中不包含此项。
- 监听的目标的标识符,即
value
- 监听的目标变化后的值;
- 如果监听的标识符是
'*'
,则mutation
对象中不包含此项。
preValue
- 监听的目标变化前的值;
- 如果监听的标识符是
'*'
,则mutation
对象中不包含此项; - 注意:
deep
模式下,如果监听的数据是一个函数对象,且它的对象属性被改变了,则preValue
是它作为对象的快照;- 如果是监听的函数的引用地址改变了,则
preValue
是之前的函数本身。
preState
- 监听的目标变化前的
state
对象的快照; - 注意:
- 如果
state
中存在函数对象,则其在preState
中将只保留对象部分的快照。
- 如果
- 监听的目标变化前的
type
- 造成此次数据变化的方式,值为以下之一:
'direct'
通过store
实例直接操作数据;'$patch'
通过$patch
方法改变数据;'$set'
通过$set
方法改变数据;'$reset'
通过$reset
方法改变数据;'$load'
通过$load
方法还原数据。
- 造成此次数据变化的方式,值为以下之一:
byAction
(实验性)参与本次数据变化的
action
:- 如果此次数据变化不是由
action
的调用造成,则该值为false
; - 如果此次数据变化由
action
的调用造成,则该值为一个包含了参与此次数据变化的action
的描述对象的数组,描述对象的storeName
属性表示该action
所属的store
的名称,actionName
属性表示该action
的名称;
- 如果此次数据变化不是由
异步
action
的捕获:默认情况下,如果一个
action
调用时启动了一个异步任务,且该异步任务造成了监听的数据的变化,该action
不会被byAction
捕获;如果监听的数据的变化存在于
action
中异步调用的回调函数中,则可将该回调函数传入this.$cb()
在action
中同步调用,this.$cb(callback)
将返回一个新的回调函数供异步调用,此时该action
可以被byAction
捕获:const { createStore } = require('firstore') const fooStore = createStore('foo', { state: { name: 'zzc6332', }, actions: { changeNameAsync(name, delay) { const callback = this.$cb(() => { this.name = name }) setTimeout(callback, delay) /* setTimeout(this.$cb(() => { this.name = name }), delay) // 这种方式不可行,this.$cb必须在 action 中被同步调用 */ } } }) fooStore.$onState('name', (mutation) => { console.log('name: ' + mutation.value + ', byAction: ' + JSON.stringify(mutation.byAction)) }) fooStore.changeNameAsync('Joie', 1000) // 1000ms后控制台输出: // - 'name: Joie, byAction: [{"storeName":"foo","actionName":"changeNameAsync"}]'
如果需要在使用
async/await
时被byAction
捕获,可将需要在await
之后修改数据的操作放入函数中传给this.$cb
在action
中同步调用,将返回的函数在await
之后使用:const { createStore } = require('firstore') const fooStore = createStore('foo', { state: { name: 'zzc6332', }, actions: { async changeNameAsync(promise1, promise2) { const modify = this.$cb(res => { this.name = res }) const res1 = await promise1 console.log('获取 rest1 之后的操作') modify(res1) const rest2 = await promise2 console.log('获取 rest2 之后的操作') modify(rest2) } } }) fooStore.$onState('name', (mutation) => { console.log('name: ' + mutation.value + ', byAction: ' + JSON.stringify(mutation.byAction)) }) const promiseJoie = new Promise((resolve) => { setTimeout(() => { resolve('Joie') }, 1000) }) const promiseMocha = new Promise((resolve) => { setTimeout(() => { resolve('Mocha') }, 500) }) fooStore.changeNameAsync(promiseJoie, promiseMocha) // 1000ms后控制台输出: // - '获取 rest1 之后的操作' // - 'name: Joie, byAction: [{"storeName":"foo","actionName":"changeNameAsync"}]' // - '获取 rest2 之后的操作' // - 'name: Mocha, byAction: [{"storeName":"foo","actionName":"changeNameAsync"}]'
如果一个
action
返回了一个promise
,且该promise
在其调用的then
方法的回调中(不支持在被await
时被捕获),或在action
监听函数的after/onError
函数的回调中修改了监听的数据,则该action
会被byAction
捕获:const { createStore } = require('firstore') const fooStore = createStore('foo', { state: { name: 'zzc6332', }, actions: { changeName(name) { this.name = name }, getNamePromise(name, delay) { return new Promise(resolve => { setTimeout(() => resolve(name), delay) }) } } }) fooStore.$onState('name', (mutation) => { console.log('name: ' + mutation.value + ', byAction: ' + JSON.stringify(mutation.byAction)) }) fooStore.$onAction('getNamePromise', (_, after) => { after((res, _this) => { _this.changeName(res) }) }) fooStore.getNamePromise('Joie', 1000) // 1000ms后控制台输出: // - 'name: Joie, byAction: [{"storeName":"foo","actionName":"getNamePromise"},{"storeName":"foo","actionName":"changeName"}]'
isImmediately
- 是否在发起监听时立即调用一次
callback
; - 默认为
false
。
- 是否在发起监听时立即调用一次
deep
- 是否深度监听;
- 默认为
true
; - 如果开启了深度监听,则对于引用数据类型,只关注其结构和内容是否变化,不关注其引用地址是否改变;
- 如果关闭了深度监听,则对于引用数据类型,则只关注其引用地址是否改变,不关注其结构和内容是否变化,这种模式下如果调用了
store
实例的$patch
、$set
、$reset
、$load
方法,任何监听的数据都会被认为发生了变化。
返回值:
$onState
方法调用成功后会返回一个新的函数;调用该函数则可以关闭该次
$onState
调用产生的所有监听,若关闭成功则该函数返回true
;例:
const fooStore = require('./fooStore') const unSubscribe = fooStore.$onState(['name', 'age'], (mutation) => { const { chain, value, preValue } = mutation console.log(`${chain} 产生了变化,之前的值为 ${preValue},新的值为 ${value}`) }) fooStore.name = 'Joie' // 控制台输出:'name 产生了变化,之前的值为 zzc6332,新的值为 Joie' fooStore.age = 18 // 控制台输出:'age 产生了变化,之前的值为 26,新的值为 18' console.log(unSubscribe()) // 关闭监听成功,控制台输出:true console.log(unSubscribe()) // 监听已被关闭,控制台输出:false fooStore.name = 'Mocha' // 监听已被关闭,控制台不输出内容 fooStore.age = 1 // // 监听已被关闭,控制台不输出内容
监听 action
可通过
store
实例的$onAction
方法监听action
的调用。接收参数:
actionName
- 需要监听的
action
名的字符串; - 如果需要批量监听,则传入需要批量监听的
action
名的字符串组成的数组; - 如果需要监听所有
action
,则传入'*'
。
- 需要监听的
callback
监听的
action
被调用时执行的回调函数;定义参数:
info
一个对象,包含所监听的
action
调用的信息,包括:name
- 监听的
action
名的字符串。
- 监听的
storeName
该监听所属的
store
的名称。args
- 该次
action
被调用时传入的参数。
- 该次
preState
- 该次
action
执行前的state
对象的快照; - 注意:
- 如果
state
中存在函数对象,则其在preState
中将只保留对象部分的快照。
- 如果
- 该次
after
- 一个函数,调用时将一个回调函数作为参数传入;
- 如果
action
的返回值是一个非Promise
值:- 该值将作为第一个参数
result
传入该回调函数; - 该次
action
被调用并执行完毕后会执行这个回调函数;
- 该值将作为第一个参数
- 如果
action
的返回值是一个Promise
值:- 当
Promise
的状态转变为fulfilled
时,其结果(the fulfillment value
)将作为第一个参数result
传入该回调函数并执行; - 当
Promise
的状态转变为rejected
时,该回调函数不会执行; - 回调函数的第二个形参接收
store
实例;
- 当
- 如果回调函数是非箭头函数,那么它的
this
指向store
实例。
onError
- 一个函数,调用时将一个回调函数作为参数传入;
- 如果
action
执行时抛出错误,则会被onError
捕获并作为 第一个参数error
传入该回调函数并执行; - 如果
action
执行完毕,返回值是一个Promise
值:- 当
Promise
的状态转变为rejected
时,其结果(rejection reason
)将作为第一个参数error
传入该回调函数并执行; - 当
Promise
的状态转变为fulfilled
时,该回调函数不会执行;
- 当
- 回调函数的第二个形参接收
store
实例;
- 如果
- 如果回调函数是非箭头函数,那么它的
this
指向store
实例。
返回值:
$onAction
方法调用成功后会返回一个新的函数;- 调用该函数则可以关闭该次
$onAction
调用产生的所有监听,若关闭成功则该函数返回true
;
监听 getter
- 可通过
store
实例的$onGetter
方法监听getter
返回值的变化。 - 接收参数
getterName
- 需要监听的
getter
名的字符串; - 如果需要批量监听,则传入需要批量监听的
getter
名的字符串组成的数组。
- 需要监听的
callback
- 监听的
getter
返回值发生变化时执行的回调函数; - 定义参数:
mutation
形参会接收一个对象,包含所监听的getter
返回值变化的信息,包括:storeName
- 该监听所属的
store
的名称。
- 该监听所属的
name
- 监听的
getter
名的字符串。
- 监听的
value
- 监听的
getter
返回值变化后的值。
- 监听的
preValue
- 监听的
getter
返回值变化前的值。
- 监听的
preState
- 监听的
getter
返回值发生变化前的state
对象的快照; - 注意:
- 如果
state
中存在函数对象,则其在preState
中将只保留对象部分的快照。
- 如果
- 监听的
type
- 本次
getter
返回值变化时,其依赖的数据变化的方式,同$onState
。
- 本次
byAction
(实验性)- 参与本次数据变化的
action
,同$onState
。
- 参与本次数据变化的
- 监听的
isImmediately
- 是否在发起监听时立即调用一次
callBack
; - 默认为
false
。
- 是否在发起监听时立即调用一次
- 返回值
$onGetter
方法调用成功后会返回一个新的函数;- 调用该函数则可以关闭该次
$onState
调用产生的所有监听,若关闭成功则该函数返回true
;
清空监听
- 可通过
store
实例的$clearListeners
方法清空监听。 $clearListeners
方法接收一个参数,可选值为:'state'
- 清空所有
$onState
产生的监听;
- 清空所有
'actions'
- 清空所有
$onAction
产生的监听;
- 清空所有
'getters'
- 清空所有
$onGetter
产生的监听;
- 清空所有
'*'
- 清空所有类型的监听。
还原点
state
中的数据可以保存到还原点中,并可以随时通过还原点查看或恢复数据。通过
store
实例中的一些方法可以实现还原点功能:$save
$save
方法执行后,会将当前state
保存到一个还原点中,并返回该还原点的id
。
$get
- 将一个还原点
id
传入$get
方法执行后,将返回该对应原点中的state
对象; - 如果传入的
id
对应的还原点不存在,则返回undefined
。
- 将一个还原点
$load
- 将一个还原点
id
传入$load
方法执行后,会将对应还原点中的state
加载到当前store
实例中,并返回true
; - 如果传入的
id
对应的还原点不存在,则返回false
。
- 将一个还原点
$delSave
- 将一个还原点
id
传入$delSave
方法执行后,将删除对应的还原点,如果删除成功则返回true
,如果找不到对应还原点则返回false
; - 将
'*'
传入$delSave
方法执行后,将清空所有还原点。
- 将一个还原点
示例:
const fooStore = require('./fooStore') fooStore.$set({ name: 'Joie', age: 18 }) const rp1 = fooStore.$save() console.log(fooStore.state) // { name: 'Joie', age: 18 } fooStore.$patch({ name: 'Mocha', age: 1 }) console.log(fooStore.state) // { name: 'Mocha', age: 1 } console.log(fooStore.$get(rp1).name) // 'Joie' fooStore.$load(rp1) console.log(fooStore.state) // { name: 'Joie', age: 18 } fooStore.$delSave(rp1)