相关文章推荐
慷慨大方的薯片  ·  react ...·  1 月前    · 
潇洒的伤疤  ·  設定 CORS Proxy ...·  1 周前    · 
力能扛鼎的筷子  ·  环境新闻速览 | ...·  1 年前    · 
失落的木瓜  ·  xlwings——Python for ...·  1 年前    · 
  • 依赖于内部 @formily/reactive 提供的数据管理,可以精准收集依赖数据的组件,修改数据后,做到精准打击,更新对应的组件。类似 vue 模板 template data 依赖收集的过程
  • 支持 React、Vue
  • 阿里团队出品,有足够的的后盾维护
  • 官方架构图:

    以 React 为例:

  • Modules - 数据模型。用于描述表单。
  • Reactive - 数据响应。绑定依赖某个数据的表单和数据。
  • React - 渲染胶水层。绑定数据模型和具体组件。
  • Ant Design - 组件库。将 Ant Design 组件注册到 formily 中。
  • 以一个 React Demo 为例,介绍整体的执行过程。

    formily 在渲染一个表单时,formily 执行过程示意图:

    对应的代码:

    import React from 'react'
    import { createForm } from '@formily/core'
    import { Field, } from '@formily/react'
    import {
      Form,
      FormItem,
      Input,
    } from '@formily/antd'
    const form = createForm({
      validateFirst: true,
    export default () => {
      return (
          form={form}
          labelCol={5}
          wrapperCol={16}
          onAutoSubmit={console.log}
          <Field name="username"
            title="用户名"
            decorator={[FormItem]}
            component={[Input]}
        </Form>
    

    下面针对执行阶段介绍下实现原理

    createForm

    创建一个 Form 实例,作为 ViewModel 给 UI 框架层消费。 文档源码

    class Form {
        constructor () {
            this.values = {} // 所有表单字段值
            this.fields = {} // 所有表单字段组件
    

    Field 组件

    FormItem 包裹 Input 渲染, 并传入 valueonChange ,使组件受控,更方便管理组件的状态。

    import { Field } from '@formily/core'
    import { observer } from '@formily/reactive-react'
    const field = new Field(); // field 数据模型。为 component 提供 props
    const props = {
        value: field.value,
        onChange() {
            field.onInput(...args)
    // observer 返回一个新组件。observer 主要做了两件事
    // 1. 提供一个 forceUpdate 的方法,可以主动触发组件更新
    // 2. 绑定 组件 和 field 的关系,让 field 的数据修改时,可能组件更新
    const finallyComponent = observer(<FormItem>
        <Input {...props} />
    </FormItem>)
    

    @formily/core - Field

    Field 的数据模型 文档源码

    class Field {
      constructor(...) {
        this.makeObservable()
      protected makeObservable() {
        define(this, {
          value: observable.computed // 使数据模型的 value 属性实现读取和设置的拦截和自定义
      // 组件的 onChange 会触发 field.onInput 方法,同步 value 属性
      onInput = async (...args: any[]) => {
        const values = getValuesFromEvent(args) // 获取输入的 value 值
        const value = values[0]
        this.value = value
    

    observable.computed 是通过 Proxy 代理 Fieldvalue 属性。

    export const computed: IComputed = createAnnotation(
      ({ target, key, value }) => {
        const store: IValue = {}
        const proxy = {}
        const context = target ? target : store
        function get() {
          // 将当前的 数据对象 和 正在执行渲染的组件 进行关系的绑定
          bindTargetKeyWithCurrentReaction({
            target: context,
            key: property,
            type: 'get',
          return store.value
        function set(value: any) {
          try {
            // 这里修改数据时,会根据 数据对象获取依赖该数据的组件,进行更新。
            batchStart()
            setter?.call?.(context, value)
          } finally {
            batchEnd()
        if (target) {
          Object.defineProperty(target, key, {
            enumerable: true,
            configurable: false,
          return target
        } else {
        return proxy
    

    @formily/reactive-react - observer

    observer 是一个高阶组件,使组件可以将数据和组件关系进行绑定。主要依赖的是两个函数 TrackeruseObserver

    Tracker 主要的两个函数:

  • 绑定数据需要跟踪的组件 track;
  • 提供更新的组件的方法_scheduler(为了兼容不同框架,实际的更新方法通过 callback 方式实现)。
  • export class Tracker {
      private results: any
      constructor(
        scheduler?: (reaction: Reaction) => void,
        name = 'TrackerReaction'
        // 此方法会触发组件的渲染, callback 为更新组件的函数
        this.track._scheduler = (callback) => {
          if (this.track._boundary === 0) this.dispose()
          if (isFn(callback)) scheduler(callback)
      // 参数 tracker 为一个组件
      track: Reaction = (tracker: Reaction) => {
        if (ReactionStack.indexOf(this.track) === -1) {
          try {
            // 添加当前的跟踪对象到 ReactionStack
            ReactionStack.push(this.track)
            // 渲染组件,读取 field 的属性时,触发属性读取操作的捕捉器。即上文提到的 computed
            this.results = tracker()
          } finally {
            ReactionStack.pop()
        return this.results
    

    useObserver 使组件可被追踪

    export const useObserver = (
      view: T,
      options?: IObserverOptions
    ) => {
      const forceUpdate = useForceUpdate() // 强制更新的方案
      const trackerRef = React.useRef<Tracker>(null)
      if (!trackerRef.current) {
        // 将组件更新的方法传给跟踪函数 Tracker
        trackerRef.current = new Tracker(() => {
          if (typeof options?.scheduler === 'function') {
            options.scheduler(forceUpdate)
          } else {
            forceUpdate()
        }, options?.displayName)
      // 将组件传给跟踪函数,返回一个组件直接结果
      return trackerRef.current.track(view)
    
    // 模拟 一个组件
    const component = (props) => {
      console.log(`组件更新:${props.name}`);
    // 模拟 ReactionStack 储存渲染的组件列表
    const components = [];
    // 模拟 Field 数据模型
    const values = {
      name: "初始数据",
    // 模拟 RawReactionsMap 储存数据和组件依赖关系
    const dataWithComponentMap = new WeakMap();
    // 模拟 computed 为数据添加自定义捕捉器
    const proxyValues = new Proxy(values, {
      get(target, key) {
        // 获取数据时,绑定关系
        dataWithComponentMap.set(target, components[components.length - 1]);
        return target[key];
      // 更新数据时,根据绑定关系对象查找组件并更新
      set(target, key, value) {
        target[key] = value;
        const component = dataWithComponentMap.get(target);
        component(target);
        return true;
    // 模拟 observer
    const Wrapper = ({ component }) => {
      return () => {
          components.push(component);
          component(proxyValues);
    const newComponent = Wrapper({
      component,
    newComponent()
    proxyValues.name = "更新数据";
    

    React

    Provider

    官方介绍。在进行第三方插件开发时,可以利用它很方便实现内部拓展。

    三方库 Field

    provider.js

    import React from 'react';
    export const ComponentProvider = React.createContext();
    
    import React, { useContext } from 'react';
    import { ComponentProvider } from './provider';
    const ComponentProvider = React.createContext();
    const Field
    
    
    
    
        
     = ({ type }) => {
      const components = useContext(ComponentProvider)
      const component = components?.[type] || <div>内部组件</div>
      return component
    
    import React, { useContext } from 'react';
    import Field, { ComponentProvider } from 'Field';
    export default () => {
      // 这里就可以将自定义组件注入到三方库中
      return <ComponentProvider.Provider value={{
        customize: <div>自定义组件</div>
        <Field type="customize" />
      </ComponentProvider.Provider>
    

    Javascript

    Proxy

    轻松实现数据的依赖收集。MDN

    weekMap

    WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意。它的键被弱保持,也就是说,当其键所指对象没有其他地方引用的时候,它会被GC回收掉。WeakMap提供的接口与Map相同。

    let obj = {}
    const normalMap = new Map();
    const weakMap = new WeakMap();
    normalMap.set(obj, 'normalMap存在')
    weakMap.set(obj, 'weakMap存在')
    console.log(normalMap.get(obj));
    console.log(weakMap.get(obj));
    obj = null
    console.log(normalMap.size);
    console.log(weakMap.size);
    复制代码
    分类:
    前端
  •