导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1.formily
    • 1.1 安装
    • 1.2 vite.config.ts
  • 2. @formily/reactive
    • 2.1 observable
    • 2.2 Reaction
    • 2.3 autorun
    • 2.4 创建深度可观察对象
      • 2.4.1 src\main.jsx
      • 2.4.2 reactive\index.jsx
      • 2.4.3 observable.jsx
      • 2.4.4 handlers.jsx
      • 2.4.5 environment.jsx
      • 2.4.6 checkers.jsx
      • 2.4.7 internals.jsx
      • 2.4.8 externals.jsx
      • 2.4.9 autorun.jsx
    • 2.5 实现autorun
      • 2.5.1 src\main.jsx
      • 2.5.2 autorun.jsx
      • 2.5.3 environment.jsx
      • 2.5.4 handlers.jsx
      • 2.5.5 reaction.jsx
      • 2.5.6 checkers.jsx
    • 2.6 实现define
      • 2.6.1 src\main.jsx
      • 2.6.2 reactive\index.jsx
      • 2.6.3 model.jsx
      • 2.6.4 internals.jsx
      • 2.6.5 externals.jsx
      • 2.6.6 environment.jsx
      • 2.6.7 observable.jsx
      • 2.6.8 annotations\index.jsx
      • 2.6.9 observable.jsx
      • 2.6.10 shallow.jsx
    • 2.7 实现Tracker
      • 2.7.1 src\main.jsx
      • 2.7.2 tracker.jsx
      • 2.7.3 reactive\index.jsx
      • 2.7.4 reaction.jsx
  • 3. @formily/reactive-react
    • 3.1 src\main.tsx
    • 3.2 src\Counter.jsx
    • 3.3 reactive-react\index.tsx
    • 3.4 observer.jsx
    • 3.5 useObserver.jsx
  • 4. @formily/core
    • 4.1 src\main.jsx
    • 4.2 core\index.jsx
    • 4.3 externals.jsx
    • 4.4 models\index.jsx
    • 4.5 Form.jsx
    • 4.6 Field.jsx
    • 4.7 path\index.jsx
    • 4.8 shared\index.jsx
    • 4.9 path.jsx
  • 5.@formily/antd
    • 5.1 src\main.jsx
    • 5.2 Form.jsx
    • 5.3 Field.jsx
    • 5.4 externals.jsx
    • 5.5 reactive\index.jsx
    • 5.6 internals.jsx
    • 5.7 antd\index.jsx
    • 5.8 form-item\index.jsx
    • 5.9 input\index.jsx
    • 5.10 react\index.jsx
    • 5.11 Field.jsx
    • 5.12 FormProvider.jsx
    • 5.13 components\index.jsx
    • 5.14 ReactiveField.jsx
    • 5.15 hooks\index.jsx
    • 5.16 useField.jsx
    • 5.17 useForm.jsx
    • 5.18 connect.jsx
    • 5.19 context.jsx
    • 5.20 shared\index.jsx
  • 6.字段验证

1.formily #

  • formilyjs
  • formily
  • 内容大纲
    • 实现@formily/reactive核心
    • 实现@formily/reactive-react核心
    • 实现@formily/core核心
    • 实现@formily/react核心
    • 实现@formily/antd核心

1.1 安装 #

pnpm create vite
pnpm install @formily/reactive @formily/reactive-react @formily/core @formily/react @formily/antd antd moment less --save

1.2 vite.config.ts #

vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: [
      { find: /^~/, replacement: '' },
      { find: "@", replacement: path.resolve('src') }
    ]
  },
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
      }
    }
  }
})

2. @formily/reactive #

  • 依赖@formily/reactive响应式解决方案,构建响应式表单的领域模型实现精确渲染

Reaction

2.1 observable #

  • observable主要用于创建不同响应式行为的 observable 对象
  • 一个observable对象,字面意思是可订阅对象,我们通过创建一个可订阅对象,在每次操作该对象的属性数据的过程中,会自动通知订阅者
  • @formily/reactive 创建 observable 对象主要是通过 ES Proxy 来创建的,它可以做到完美劫持数据操作

2.2 Reaction #

  • reaction在响应式编程模型中,它就相当于是可订阅对象的订阅者
  • 它接收一个 tracker 函数,这个函数在执行的时候,如果函数内部有对 observable 对象中的某个属性进行读操作会进行依赖收集,那当前 reaction 就会与该属性进行一个绑定(依赖追踪),该属性在其它地方发生了写操作,就会触发 tracker 函数重复执行
  • 从订阅到派发订阅,其实是一个封闭的循环状态机,每次 tracker 函数执行的时候都会重新收集依赖,依赖变化时又会重新触发tracker执行

2.3 autorun #

  • autorun可以创建一个自动执行的响应器
  • 接收一个 tracker 函数,如果函数内部有消费 observable 数据,数据发生变化时,tracker 函数会重复执行

2.4 创建深度可观察对象 #

  • observable主要用于创建不同响应式行为的 observable 对象,同时可以作为 annotation 给 define 用于标记响应式属性
  • autorun接收一个 tracker 函数,如果函数内部有消费 observable 数据,数据发生变化时,tracker 函数会重复执行
  • reaction接收一个 tracker 函数,与 callback 响应函数,如果 tracker 内部有消费 observable 数据,数据发生变化时,tracker 函数会重复执行,但是 callback 执行必须要求 tracker 函数返回值发生变化时才执行
  • define手动定义领域模型,可以指定具体属性的响应式行为,也可以指定某个方法为 batch 模式
  • toJS深度递归将 observable 对象转换成普通 JS 对象
  • tracker主要用于接入 React/Vue 的手动追踪依赖工具,在依赖发生变化时不会重复执行 tracker 函数,需要用户手动重复执行,只会触发 scheduler

2.4.1 src\main.jsx #

src\main.jsx

import { observable, autorun } from '@/@formily/reactive'
const values = { username: 'zhufeng', home: { name: 'beijing' } }
const observableValues = observable(values)
console.log(observableValues);
console.log(observableValues.username);
console.log(observableValues.home);
console.log(observableValues.home);
/* autorun(() => {
  console.log(observableValues.username);
})
observableValues.username = 'jiagou'; */

2.4.2 reactive\index.jsx #

src\@formily\reactive\index.jsx

export * from './observable'
export * from './autorun'

2.4.3 observable.jsx #

src\@formily\reactive\observable.jsx

import { createObservable } from './internals';
export function observable(target) {
    return createObservable(null, null, target)
}

2.4.4 handlers.jsx #

src\@formily\reactive\handlers.jsx

import { isObservable } from './externals'
import { createObservable } from './internals'
import { RawProxy } from './environment'
export const baseHandlers = {
    get(target, key) {
        const result = target[key]
        const observableResult = RawProxy.get(result)
        if (observableResult) {
            return observableResult
        }
        if (!isObservable(result)) {
            return createObservable(target, key, result)
        }
        return result;
    },
    set(target, key, value) {
        const newValue = createObservable(target, key, value)
        target[key] = newValue
        return true;
    }
}

2.4.5 environment.jsx #

src\@formily\reactive\environment.jsx

//RawProxy.set(target, proxy) 普通对象=>代理对象
export const RawProxy = new WeakMap()
//ProxyRaw.set(proxy, target) 代理对象=>原生对象
export const ProxyRaw = new WeakMap()

2.4.6 checkers.jsx #

src\@formily\reactive\checkers.jsx

const toString = Object.prototype.toString
export const isPlainObj = (val) => toString.call(val) === '[object Object]'
export const isNormalType = (target) => {
    return isPlainObj(target)
}

2.4.7 internals.jsx #

src\@formily\reactive\internals.jsx

import { baseHandlers } from './handlers'
import { isNormalType } from './checkers';
import { ProxyRaw, RawProxy } from './environment';
export const createObservable = (target, key, value) => {
    if (typeof value !== 'object') return value;
    const raw = ProxyRaw.get(value)
    if (raw) {
        return value
    }
    if (isNormalType(value)) return createNormalProxy(value)
    return value
}
const createNormalProxy = (target) => {
    const proxy = new Proxy(target, baseHandlers)
    ProxyRaw.set(proxy, target)
    RawProxy.set(target, proxy)
    return proxy
}

2.4.8 externals.jsx #

src\@formily\reactive\externals.jsx

import { ProxyRaw } from './environment'
export const isObservable = (target) => {
    return ProxyRaw.has(target)
}

2.4.9 autorun.jsx #

src\@formily\reactive\autorun.jsx

export const autorun = (tracker) => {}

2.5 实现autorun #

2.5.1 src\main.jsx #

src\main.jsx

import { observable, autorun } from '@/@formily/reactive'
const values = { username: 'zhufeng', home: { name: 'beijing' } }
const observableValues = observable(values)
+autorun(() => {
+  console.log(observableValues.username);
+})
+observableValues.username = 'jiagou';

2.5.2 autorun.jsx #

src\@formily\reactive\autorun.jsx

import { ReactionStack } from './environment'
export const autorun = (tracker) => {
    const reaction = () => {
        ReactionStack.push(reaction)
        tracker()
        ReactionStack.pop()
    }
    reaction()
}

2.5.3 environment.jsx #

src\@formily\reactive\environment.jsx

//RawProxy.set(target, proxy) 普通对象=>代理对象
export const RawProxy = new WeakMap()
//ProxyRaw.set(proxy, target) 代理对象=>原生对象
export const ProxyRaw = new WeakMap()
+export const RawReactionsMap = new WeakMap()
+export const ReactionStack = []

2.5.4 handlers.jsx #

src\@formily\reactive\handlers.jsx

import { isObservable } from './externals'
import { createObservable } from './internals'
import { RawProxy } from './environment'
+import { bindTargetKeyWithCurrentReaction, runReactionsFromTargetKey } from './reaction'
export const baseHandlers = {
    get(target, key) {
        const result = target[key]
+       bindTargetKeyWithCurrentReaction({ target, key })
        const observableResult = RawProxy.get(result)
        if (observableResult) {
            return observableResult
        }
        if (!isObservable(result)) {
            return createObservable(target, key, result)
        }
        return result;
    },
    set(target, key, value) {
        const newValue = createObservable(target, key, value)
        target[key] = newValue
+       runReactionsFromTargetKey({ target, key })
        return true;
    }
}

2.5.5 reaction.jsx #

src\@formily\reactive\reaction.jsx

import { isFn } from './checkers'
import { ReactionStack, RawReactionsMap } from './environment'
const addRawReactionsMap = (target, key, reaction) => {
    const reactionsMap = RawReactionsMap.get(target)
    if (reactionsMap) {
        const reactionSet = reactionsMap.get(key)
        if (reactionSet) {
            reactionSet.add(reaction)
        } else {
            let reactionSet = new Set();
            reactionSet.add(reaction);
            reactionsMap.set(key, reactionSet);
        }
        return reactionsMap
    } else {
        let reactionSet = new Set();
        reactionSet.add(reaction);
        const reactionsMap = new Map([[key, reactionSet]])
        RawReactionsMap.set(target, reactionsMap)
        return reactionsMap
    }
}

export const bindTargetKeyWithCurrentReaction = (operation) => {
    let { key, target } = operation
    const current = ReactionStack[ReactionStack.length - 1]
    if (current) {
       addRawReactionsMap(target, key, current)
    }
}

export const runReactionsFromTargetKey = (operation) => {
    let { key, target } = operation
    runReactions(target, key)
}
const runReactions = (target, key) => {
    const reactions = getReactionsFromTargetKey(target, key)
    if(reactions){
        for (let reaction of reactions) {
            reaction();
        }
    }
}
const getReactionsFromTargetKey = (target, key) => {
    const reactionsMap = RawReactionsMap.get(target);
    if (reactionsMap) {
        return reactionsMap.get(key)
    }
}

2.5.6 checkers.jsx #

src\@formily\reactive\checkers.jsx

const toString = Object.prototype.toString
export const isPlainObj = (val) => toString.call(val) === '[object Object]'
export const isNormalType = (target) => {
    return isPlainObj(target)
}
+export const isFn = (val) => typeof val === 'function'

2.6 实现define #

2.6.1 src\main.jsx #

src\main.jsx

+import { observable, autorun, define } from '@/@formily/reactive'
+const form = {
+  values: { username: { value: 'zhufeng' } },
+  fields: { username: { name: '用户名' } }
+}
+define(form, {
+  values: observable,
+  fields: observable.shallow
+});
autorun(() => {
+ console.log(form.values, form.values.username, form.values.username.value);
+ console.log(form.fields, form.fields.username, form.fields.username.name);
})
+form.values.username.value = 'jiagou'
+form.fields.username.name = '密码'

2.6.2 reactive\index.jsx #

src\@formily\reactive\index.jsx

export * from './observable'
export * from './autorun'
+export * from './model'

2.6.3 model.jsx #

src\@formily\reactive\model.jsx

import { getObservableMaker } from './internals';
import { isObservable, isAnnotation } from './externals'
export function define(target, annotations) {
    if (isObservable(target)) return target
    for (const key in annotations) {
        const annotation = annotations[key]
        if (isAnnotation(annotation)) {
            getObservableMaker(annotation)({ target, key })
        }
    }
    return target
}

2.6.4 internals.jsx #

src\@formily\reactive\internals.jsx

import { baseHandlers } from './handlers'
+import { isNormalType, isFn } from './checkers';
+import { ProxyRaw, RawProxy, MakeObservableSymbol, RawShallowProxy } from './environment';
+export const createObservable = (target, key, value, shallow) => {
    if (typeof value !== 'object') return value;
    const raw = ProxyRaw.get(value)
    if (raw) {
        return value
    }
+   if (target) {
+       const parentRaw = ProxyRaw.get(target) || target
+       const isShallowParent = RawShallowProxy.get(parentRaw)
+       if (isShallowParent) return value
+   }
+   if (shallow) return createShallowProxy(value)
    if (isNormalType(value)) return createNormalProxy(value)
    return value
}
+const createShallowProxy = (target) => {
+    if (isNormalType(target)) return createNormalProxy(target, true)
+    return target
+}
+const createNormalProxy = (target, shallow) => {
    const proxy = new Proxy(target, baseHandlers)
    ProxyRaw.set(proxy, target)
+   if (shallow) {
+       RawShallowProxy.set(target, proxy)
+   } else {
+       RawProxy.set(target, proxy)
+   }
    return proxy
}
+export const createAnnotation = (maker) => {
+    const annotation = (target) => {
+        return maker({ value: target })
+    }
+    if (isFn(maker)) {
+        annotation[MakeObservableSymbol] = maker
+    }
+    return annotation
+}
+export const getObservableMaker = (target) => {
+    if (target[MakeObservableSymbol]) {
+        if (!target[MakeObservableSymbol][MakeObservableSymbol]) {
+            return target[MakeObservableSymbol]
+        }
+        return getObservableMaker(target[MakeObservableSymbol])
+    }
+}

2.6.5 externals.jsx #

src\@formily\reactive\externals.jsx

+import { ProxyRaw, MakeObservableSymbol } from './environment'
export const isObservable = (target) => {
    return ProxyRaw.has(target)
}
+export const isAnnotation = (target) => {
+    return target && target[MakeObservableSymbol]
+}

2.6.6 environment.jsx #

src\@formily\reactive\environment.jsx

//RawProxy.set(target, proxy) 普通对象=>代理对象
export const RawProxy = new WeakMap()
//ProxyRaw.set(proxy, target) 代理对象=>原生对象
export const ProxyRaw = new WeakMap()
export const RawReactionsMap = new WeakMap()
+export const ReactionStack = []
+export const MakeObservableSymbol = Symbol('MakeObservableSymbol')
+//RawShallowProxy.set(target, proxy) 原生对象=>代理对象
+export const RawShallowProxy = new WeakMap()

2.6.7 observable.jsx #

src\@formily\reactive\observable.jsx

import { createObservable } from './internals';
+import * as annotations from './annotations'
+import { MakeObservableSymbol } from './environment';
export function observable(target) {
    return createObservable(null, null, target)
}
+observable.shallow = annotations.shallow
+observable[MakeObservableSymbol] = annotations.observable

2.6.8 annotations\index.jsx #

src\@formily\reactive\annotations\index.jsx

export * from './observable'
export * from './shallow'

2.6.9 observable.jsx #

src\@formily\reactive\annotations\observable.jsx

import { createAnnotation, createObservable } from '../internals'
import { bindTargetKeyWithCurrentReaction, runReactionsFromTargetKey } from '../reaction';
export const observable = createAnnotation(
    ({ target, key, value }) => {
        const store = {
            value: createObservable(target, key, target[key]),
        }
        function get() {
            bindTargetKeyWithCurrentReaction({ target, key })
            return store.value
        }
        function set(value) {
            value = createObservable(target, key, value)
            store.value = value
            runReactionsFromTargetKey({ target, key })
        }
        Object.defineProperty(target, key, {
            set,
            get,
            enumerable: true,
            configurable: false
        })
        return store.value
    }
)

2.6.10 shallow.jsx #

src\@formily\reactive\annotations\shallow.jsx

import { createAnnotation, createObservable } from '../internals'
import { bindTargetKeyWithCurrentReaction, runReactionsFromTargetKey } from '../reaction';
export const shallow = createAnnotation(
    ({ target, key, value }) => {
        const store = {
            value: createObservable(target, key, target[key], true),
        }
        function get() {
            bindTargetKeyWithCurrentReaction({ target: target, key: key })
            return store.value
        }

        function set(value) {
            value = createObservable(target, key, target[key], true)
            store.value = value
            runReactionsFromTargetKey({ target, key })
        }
        if (target) {
            Object.defineProperty(target, key, {
                set,
                get,
                enumerable: true,
                configurable: false
            })
            return target
        }
        return store.value
    }
)

2.7 实现Tracker #

2.7.1 src\main.jsx #

src\main.jsx

import { observable, Tracker } from '@/@formily/reactive'
const values = { username: 'zhufeng', home: { name: 'beijing' } }
+const observableValues = observable(values)
+const tracker = new Tracker(() => {
+    console.log('forceUpate');
+})
+tracker.track(() => {
+    console.log(observableValues.username);
+})
+observableValues.username = 'jiagou';

2.7.2 tracker.jsx #

src\@formily\reactive\tracker.jsx

import { ReactionStack } from './environment'
export class Tracker {
    constructor(scheduler) {
        this.track.scheduler = scheduler;
    }
    track = (view) => {
        ReactionStack.push(this.track)
        return view();
    }
}

2.7.3 reactive\index.jsx #

src\@formily\reactive\index.jsx

export * from './observable'
export * from './autorun'
export * from './model'
+export * from './tracker'

2.7.4 reaction.jsx #

src\@formily\reactive\reaction.jsx

import { ReactionStack, RawReactionsMap } from './environment';
/**
 * 把某个对象的某个key和当前的reaction进行绑定
 * @param {*} operation {target,key}
 */
export const bindTargetKeyWithCurrentReaction = (operation) => {
    const { target, key } = operation;
    //最后一个Reaction就是currentReaction
    const currentReaction = ReactionStack[ReactionStack.length - 1];
    if (currentReaction) {
        addRawReactionsMap(target, key, currentReaction)
    }
}
const addRawReactionsMap = (target, key, reaction) => {
    //判断此target对象在RawReactionsMap里有没有值
    const reactionsMap = RawReactionsMap.get(target);
    if (reactionsMap) {
        const reactionSet = reactionsMap.get(key);
        if (reactionSet) {
            reactionSet.add(reaction);
        } else {
            let reactionSet = new Set();
            reactionSet.add(reaction);
            reactionsMap.set(key, reactionSet);
        }
        return reactionsMap;
    } else {
        //ArraySet 元素唯1的数组
        let reactionSet = new Set();//源码里作者自己封装了一个ArraySet
        reactionSet.add(reaction);
        const reactionsMap = new Map([[key, reactionSet]]);
        RawReactionsMap.set(target, reactionsMap);
        return reactionsMap;
    }
}

export const runReactionsFromTargetKey = (operation) => {
    const { target, key } = operation;
    runReactions(target, key);
}
function runReactions(target, key) {
    const reactions = getReactionsFromTargetKey(target, key);
    if(reactions){
      for (let reaction of reactions) {
        if (isFn(reaction.scheduler)) {
           reaction.scheduler(reaction)
        } else {
           reaction()
        }
      }
    }
}
const getReactionsFromTargetKey = (target, key) => {
    const reactionsMap = RawReactionsMap.get(target);
    if (reactionsMap) {
        return reactionsMap.get(key)
    }
}

3. @formily/reactive-react #

  • observer接收一个 Function RenderProps,只要在 Function 内部消费到的任何响应式数据,都会随数据变化而自动重新渲染,也更容易实现局部精确渲染 -在 React 中, observer将 Function Component 变成 Reaction,每次视图重新渲染就会收集依赖,依赖更新会自动重渲染

3.1 src\main.tsx #

src\main.tsx

import React from 'react'
import { createRoot } from 'react-dom/client'
import Counter from './Counter';
createRoot(document.getElementById('root')).render(<Counter />);

3.2 src\Counter.jsx #

src\Counter.jsx

import { observable } from '@formily/reactive'
import { observer } from '@formily/reactive-react'
const counter = observable({
    number: 1
});
const Counter = observer(() => {
    return (
        <div>
            <p>{counter.number}</p>
            <button onClick={() => counter.number++}>+</button>
        </div>
    )
});
export default Counter;

3.3 reactive-react\index.tsx #

src\@formily\reactive-react\index.tsx

export * from './observer'

3.4 observer.jsx #

src\@formily\reactive-react\observer.jsx

import { useObserver } from './hooks/useObserver'
export function observer(component) {
    const wrappedComponent = (props) => {
        return useObserver(() => component(props))
    }
    return wrappedComponent;
}

3.5 useObserver.jsx #

src\@formily\reactive-react\hooks\useObserver.jsx

import { useState, useCallback, useRef } from 'react';
import { Tracker } from '@/@formily/reactive'
export const useObserver = (view) => {
    const [, setState] = useState([])
    const forceUpdate = useCallback(() => setState([]), [])
    const instRef = useRef(null)
    if (!instRef.current) {
        instRef.current = new Tracker(forceUpdate)
    }
    return instRef.current.track(view)
}

4. @formily/core #

  • @formily/core的核心意义是将领域模型从UI框架中抽离出来
  • Formily内核其实是一个 @formily/reactive 领域模型
  • 实际消费领域模型则主要是依赖@formily/reactive的响应器机制做依赖追踪来消费
  • 我们可以在响应器Reactions中消费 Form/Field/ArrayField/ObjectField/VoidField模型中的任意属性,依赖的属性发生变化,响应器就会重复执行

4.1 src\main.jsx #

src\main.jsx

import { createForm } from '@/@formily/core'
const form = createForm({
    values: {
        username: 'zhufeng'
    },
})
console.log(form);
const field = form.createField({ name: 'username', title: '用户名', value: 'zhufeng' });
console.log(field);

4.2 core\index.jsx #

src\@formily\core\index.jsx

export * from './shared/externals';
export * from './models';

4.3 externals.jsx #

src\@formily\core\shared\externals.jsx

import { FormPath } from '@/@formily/shared'
import { Form } from '../models'

const createForm = (options) => {
    return new Form(options)
}

export {
    FormPath,
    createForm
}

4.4 models\index.jsx #

src\@formily\core\models\index.jsx

export * from './Form'
export * from './Field'

4.5 Form.jsx #

src\@formily\core\models\Form.jsx

import { define, observable } from '@/@formily/reactive'
import { Field } from './Field'
import { FormPath } from '@/@formily/shared'
export class Form {
    values={}
    fields = {}
    constructor(props) {
        this.initialize(props)
        this.makeObservable()
        this.makeValues()
    }
    initialize(props) {
        this.props = { ...props }
    }
    makeObservable() {
        define(this, {
            values: observable,
            fields: observable.shallow
        })
    }
    makeValues() {
        this.values = this.props.values
    }
    createField(props) {
        const address = FormPath.parse().concat(props.name)
        new Field(address, props, this)
        return this.fields[address.entire]
    }
}

4.6 Field.jsx #

src\@formily\core\models\Field.jsx

import { define, observable } from '@/@formily/reactive'
export class Field {
    constructor(address, props, form) {
        this.props = { ...props };
        this.form = form;
        this.locate(address)
        this.initialize()
        this.makeObservable()
    }
    initialize() {
        this.value = this.props.value;
    }
    makeObservable() {
        define(this, {
            value: observable
        })
    }
    locate(address) {
        this.form.fields[address.entire] = this
    }
}

4.7 path\index.jsx #

src\@formily\path\index.jsx

const parse = (pattern) => {
    if (!pattern) {
        return {
            entire: '',
            segments: []
        }
    }
    return {
        entire: pattern,
        segments: pattern.split('.')
    }
}
export class Path {
    constructor(input = '') {
        const { segments, entire } = parse(input)
        this.entire = entire
        this.segments = segments
    }
    static parse() {
        return new Path();
    }
    concat = (...args) => {
        const path = new Path('')
        path.segments = this.segments.concat(...args)
        path.entire = path.segments.join('.')
        return path
    }
}

4.8 shared\index.jsx #

src\@formily\shared\index.jsx

export * from './path'

4.9 path.jsx #

src\@formily\shared\path.jsx

import { Path as FormPath } from '@/@formily/path'
export { FormPath }

5.@formily/antd #

5.1 src\main.jsx #

src\main.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import { createForm } from "@/@formily/core";
import { FormProvider, Field } from "@/@formily/react";
import { FormItem, Input } from "@/@formily/antd";
const form = createForm();
const App = () => {
    return (
        <FormProvider form={form}>
            <Field
                name="username"
                title="用户名"
                value="jiagou"
                decorator={[FormItem]}
                component={[Input]}
            />
            <button onClick={() => {
                form.submit(console.log)
            }}>提交</button>
        </FormProvider>
    )
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

5.2 Form.jsx #

src\@formily\core\models\Form.jsx

import { define, observable } from '@/@formily/reactive'
import { Field } from './Field'
import { FormPath } from '@/@formily/shared'
+import { batchSubmit } from '../shared/internals'
export class Form {
    values = {}
    fields = {}
    constructor(props) {
        this.initialize(props)
        this.makeObservable()
        this.makeValues()
    }
    initialize(props) {
        this.props = { ...props }
    }
    makeObservable() {
        define(this, {
            values: observable,
            fields: observable.shallow
        })
    }
    makeValues() {
+       this.values = Object.assign({}, this.props.values);
    }
    createField(props) {
        const address = FormPath.parse().concat(props.name)
        new Field(address, props, this)
        return this.fields[address.entire]
    }
+   setValuesIn = (pattern, value) => {
+       this.values[pattern.entire] = value;
+   }
+   getValuesIn = (pattern) => {
+       return this.values[pattern.entire];
+   }
+   submit = (onSubmit) => {
+       return batchSubmit(this, onSubmit)
+   }
}

5.3 Field.jsx #

src\@formily\core\models\Field.jsx

import { define, observable } from '@/@formily/reactive'
export class Field {
    constructor(address, props, form) {
        this.props = { ...props };
        this.form = form;
        this.locate(address)
        this.initialize()
        this.makeObservable()
    }
    initialize() {
        this.value = this.props.value;
+       this.decorator = this.props.decorator
+       this.component = this.props.component
    }
    makeObservable() {
        define(this, {
            value: observable
        })
    }
    locate(address) {
        this.form.fields[address.entire] = this
+       this.path = address;
    }
+   get value() {
+       return this.form.getValuesIn(this.path)
+   }
+   set value(value) {
+       this.form.setValuesIn(this.path, value)
+   }
+   get decorator() {
+       return [this.decoratorType]
+   }
+   set decorator(value) {
+       this.decoratorType = value[0]
+   }
+   get component() {
+       return [this.componentType]
+   }
+   set component(value) {
+       this.componentType = value[0]
+   }
+   onInput = (e) => {
+       const newValue = e.target.value;
+       this.value = newValue;
+       this.form.values[this.props.name] = newValue;
+   };
}

5.4 externals.jsx #

src\@formily\reactive\externals.jsx

import { ProxyRaw, MakeObservableSymbol } from './environment'
+import { isPlainObj } from './checkers';
export const isObservable = (target) => {
    return ProxyRaw.has(target)
}
export const isAnnotation = (target) => {
    return target && target[MakeObservableSymbol]
}
+export const toJS = (values) => {
+    const visited = new Set()
+    const _toJS = (values) => {
+        if (visited.has(values)) {
+            return values
+        }
+        if (isPlainObj(values)) {
+            visited.add(values)
+            const res = {}
+            for (const key in values) {
+                res[key] = _toJS(values[key])
+            }
+            return res
+        }
+        return values
+    }
+    return _toJS(values)
+}

5.5 reactive\index.jsx #

src\@formily\reactive\index.jsx

export * from './observable'
export * from './autorun'
export * from './model'
export * from './tracker'
+export * from './externals'

5.6 internals.jsx #

src\@formily\core\shared\internals.jsx

import { toJS } from '@/@formily/reactive'
export const batchSubmit = (target, onSubmit) => {
    onSubmit(toJS(target.values))
}

5.7 antd\index.jsx #

src\@formily\antd\index.jsx

export * from './form-item'
export * from './input'

5.8 form-item\index.jsx #

src\@formily\antd\form-item\index.jsx

import { connect, mapProps } from '@/@formily/react'
export const BaseItem = ({ children, label }) => {
    return (
        <div>
            <span>{label}</span>
            {children}
        </div>
    )
}

export const FormItem = connect(
    BaseItem,
    mapProps((props, field) => {
        return { label: field.props.title }
    })
)

export default FormItem

5.9 input\index.jsx #

src\@formily\antd\input\index.jsx

import { connect, mapProps } from '@/@formily/react'
import { Input as AntdInput } from 'antd'
export const Input = connect(
    AntdInput,
    mapProps((props) => {
        return { ...props }
    })
)
export default Input

5.10 react\index.jsx #

src\@formily\react\index.jsx

export * from './components'
export * from './hooks'
export * from './shared'

5.11 Field.jsx #

src\@formily\react\components\Field.jsx

import React from 'react'
import { useForm } from '../hooks'
import { ReactiveField } from './ReactiveField'
import { FieldContext } from '../shared'
export const Field = (props) => {
  const form = useForm()
  const field = form.createField(props)
  return (
    <FieldContext.Provider value={field}>
      <ReactiveField field={field}>{props.children}</ReactiveField>
    </FieldContext.Provider>
  )
}

5.12 FormProvider.jsx #

src\@formily\react\components\FormProvider.jsx

import React from 'react'
import { FormContext } from '../shared'
export const FormProvider = (props) => {
  const form = props.form
  return (
    <FormContext.Provider value={form}>{props.children}</FormContext.Provider>
  )
}

5.13 components\index.jsx #

src\@formily\react\components\index.jsx

export * from './FormProvider'
export * from './Field'

5.14 ReactiveField.jsx #

src\@formily\react\components\ReactiveField.jsx

import React from 'react';
import { observer } from '@/@formily/reactive-react'
const ReactiveInternal = (props) => {
    const field = props.field
    const renderDecorator = (children) => {
        return React.createElement(
            field.decoratorType,
            {},
            children
        )
    }
    const renderComponent = () => {
        const value = field.value;
        const onChange = (...args) => {
            field.onInput(...args)
        }
        return React.createElement(
            field.componentType,
            {
                value,
                onChange
            }
        )
    }
    return renderDecorator(renderComponent())
}
export const ReactiveField = observer(ReactiveInternal)

5.15 hooks\index.jsx #

src\@formily\react\hooks\index.jsx

export * from './useForm'
export * from './useField'

5.16 useField.jsx #

src\@formily\react\hooks\useField.jsx

import { useContext } from 'react'
import { FieldContext } from '../shared'

export const useField = () => {
    return useContext(FieldContext)
}

5.17 useForm.jsx #

src\@formily\react\hooks\useForm.jsx

import { useContext } from 'react'
import { FormContext } from '../shared'

export const useForm = () => {
  return useContext(FormContext)
}

5.18 connect.jsx #

src\@formily\react\shared\connect.jsx

import React from 'react';
import { observer } from '@/@formily/reactive-react';
import { useField } from '../hooks'
export function mapProps(...args) {
    return (target) => {
        return observer(
            (props) => {
                const field = useField()
                const results = args.reduce(
                    (props, mapper) => {
                        return Object.assign(props, mapper(props, field))
                    },
                    { ...props }
                )
                return React.createElement(target, results)
            }
        )
    }
}
export function connect(target, ...args) {
    const Target = args.reduce((target, mapper) => {
        return mapper(target)
    }, target)
    return (props) => {
        return React.createElement(Target, { ...props })
    }
}

5.19 context.jsx #

src\@formily\react\shared\context.jsx

import { createContext } from 'react'
export const FormContext = createContext(null)
export const FieldContext = createContext(null)

5.20 shared\index.jsx #

src\@formily\react\shared\index.jsx

export * from './context'
export * from './connect'

6.字段验证 #

访问验证

请输入访问令牌

Token不正确,请重新输入