formilyjs的核心库——reactive
安装:$ npm install --save @formily/reactive
先看一个例子import React from 'react'
import { observable } from '@formily/reactive'
import { observer } from '@formily/reactive-react'
const obs = observable({
value: 'Hello world',
})
// 一个 observable 对象,字面意思是可订阅对象,
// 我们通过创建一个可订阅对象,在每次操作该对象的属性数据的过程中,会自动通知订阅者,
// @formily/reactive 创建 observable 对象主要是通过 ES Proxy 来创建的,
// 它可以做到完美劫持数据操作
export default observer(() => {
return (
<div>
<div>
<input
style={{
height: 28,
padding: '0 8px',
border: '2px solid #888',
borderRadius: 3,
}}
value={obs.value}
onChange={(e) => {
obs.value = e.target.value
}}
/>
</div>
<div>{obs.value}</div>
</div>
)
})
observable及相关重要API
一个 observable 对象,字面意思是可订阅对象,我们通过创建一个可订阅对象,在每次操作该对象的属性数据的过程中,会自动通知订阅者, @formily/reactive 创建 observable 对象主要是通过 ES Proxy 来创建的,它可以做到完美劫持数据操作。
- observable/observable.deep(默认deep劫持) 函数创建深劫持 observable 对象,出于性能考虑推荐使用observable.shallow 函数创建浅劫持 observable 对象
import { observable, autorun } from '@formily/reactive'
const obs = observable.shallow({
aa: {
bb: 111,
},
})
//autorun:接收一个 tracker 函数,如果函数内部有消费 observable 数据,
//数据发生变化时,tracker 函数会重复执行,以下例子中只要被劫持的数据发生变化,就会打印
autorun(() => {
console.log(obs.aa.bb)
})
obs.aa.bb = 222 // 不会响应
obs.aa = { bb: 333 } // 可以响应
- observable.computed创建一个计算缓存器,它可以智能缓存计算结果。 它的缓存策略是:只要 computed 函数内部所依赖的 observable 数据发生变化,函数才会重新执行计算,否则永远读取缓存结果
import { observable, autorun } from '@formily/reactive'
const obs = observable({
aa: 11,
bb: 22,
})
const computed = observable.computed(() => obs.aa + obs.bb)
autorun(() => {
console.log(computed.value)
})
obs.aa = 33
- observable.ref创建引用劫持响应式对象
import { observable, autorun } from '@formily/reactive'
const ref = observable.ref(1)
autorun(() => {
console.log(ref.value)
})
ref.value = 2
- observable.box与ref相似,只是用get,set来读写
import { observable, autorun } from '@formily/reactive'
const ref = observable.ref(1)
autorun(() => {
console.log(ref.value)
})
ref.value = 2
autorun
接收一个 tracker 函数,如果函数内部有消费 observable 数据,数据发生变化时,tracker 函数会重复执行
import { observable, autorun } from '@formily/reactive'
const obs = observable({})
const dispose = autorun(() => {
console.log(obs.aa)
})
obs.aa = 123
//使用 autorun/reaction 的时候,一定记得调用 dispose 释放函数,否则会内存泄漏
dispose()
- autorun.memo在autorun中创建持久引用数据,不要在 If/For 这类语句中使用,因为它内部是依赖执行顺序来绑定当前 autorun 的。
思考下面这个例子加memo和不加memo分别输出什么
import { observable, autorun } from '@formily/reactive'
const obs1 = observable({
aa: 0,
})
const dispose = autorun(() => {
const obs2 = autorun.memo(() =>
observable({
bb: 0,
})
)
console.log(obs1.aa, obs2.bb++)
})
obs1.aa++
obs1.aa++
obs1.aa++
//执行四次,输出结果为
/**
* 0 0
* 1 1
* 2 2
* 3 3
*/
dispose()
reaction
与autorun不同的是,它接收一个 tracker 函数与 callback 响应函数,如果 tracker 内部有消费 observable 数据,数据发生变化时,tracker 函数会重复执行,但是只有 tracker 函数返回值发生变化时才执行 callback 函数。
import { observable, reaction, batch } from '@formily/reactive'
const obs = observable({
aa: 1,
bb: 2,
})
const dispose = reaction(() => {
return obs.aa + obs.bb
}, console.log)
//console.log为callback函数
batch(() => {
//触发tracker函数,不会触发callback打印,因为obs.aa + obs.bb的值(tracker的返回值)没变
obs.aa = 2
obs.bb = 1
})
obs.aa = 4
dispose()
batch 定义批量操作,内部可以收集依赖
尽量多用 batch 模式,这样可以减少 Reaction 执行次数,对于一个函数内部是对多个 observable 属性进行操作的情况,使用bacth合并更新
合并前,合并后会执行几次打印?
import { observable, autorun } from '@formily/reactive'
const obs = observable({})
const handler = () => {
obs.aa = 123
obs.bb = 321
}
autorun(() => {
console.log(obs.aa, obs.bb)
})
handler()
会执行 3 次打印,autorun 默认执行一次,加上 obs.aa 赋值执行一次,obs.bb 赋值执行一次
undefined undefined
123 undefined
123 321
import { observable, autorun, batch } from '@formily/reactive'
const obs = observable({})
const handler = () => {
obs.aa = 123
obs.bb = 321
}
autorun(() => {
console.log(obs.aa, obs.bb)
})
batch(() => {
handler()
})
将handler内部的操作合并更新,只打印2次
undefined undefined
123 321