formilyjs快速上手一之了解reactive核心库

1,261 阅读3分钟

formilyjs的核心库——reactive

安装:$ npm install --save @formily/reactive

先看一个例子

image.png

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