{"id":23086824,"url":"https://github.com/cgfeel/formily","last_synced_at":"2026-03-13T21:06:24.000Z","repository":{"id":226405098,"uuid":"767752060","full_name":"cgfeel/formily","owner":"cgfeel","description":"按照formily官方文档，按照章节最小化复现","archived":false,"fork":false,"pushed_at":"2026-03-05T10:00:22.000Z","size":2503,"stargazers_count":32,"open_issues_count":1,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-05T14:31:05.753Z","etag":null,"topics":["formily","react"],"latest_commit_sha":null,"homepage":"https://cgfeel.github.io/formily/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cgfeel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-03-05T20:50:08.000Z","updated_at":"2026-03-05T10:00:40.000Z","dependencies_parsed_at":"2026-03-05T12:01:49.859Z","dependency_job_id":null,"html_url":"https://github.com/cgfeel/formily","commit_stats":{"total_commits":308,"total_committers":1,"mean_commits":308.0,"dds":0.0,"last_synced_commit":"4f86a1bcbef5411014283da5e47c32be8dc8b942"},"previous_names":["cgfeel/formily"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cgfeel/formily","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgfeel%2Fformily","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgfeel%2Fformily/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgfeel%2Fformily/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgfeel%2Fformily/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cgfeel","download_url":"https://codeload.github.com/cgfeel/formily/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgfeel%2Fformily/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30178285,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T12:39:21.703Z","status":"ssl_error","status_checked_at":"2026-03-06T12:36:09.819Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["formily","react"],"created_at":"2024-12-16T19:31:25.891Z","updated_at":"2026-03-13T21:06:23.992Z","avatar_url":"https://github.com/cgfeel.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Formily Demo\n\n按照 `formily` 官方文档，最小化复现每个示例。本项目不提供线上预览，请直接在本地运行`npm install \u0026\u0026 npm run start`\n\n**示例包含 4 个文档：**\n\n-   `formily` 主文档：https://formilyjs.org/zh-CN\n-   `@formily/reactive`：https://reactive.formilyjs.org/zh-CN\n-   `@formily/core`：https://core.formilyjs.org/zh-CN\n-   `@formily/react`：https://react.formilyjs.org/zh-CN\n\n其中组件采用 `@formily/antd-v5` 为主文档作演示。不包含 `@formily/vue`，技术栈为 `React`，在 `formily` 中两者大同小异\n\n**演示分两部分：**\n\n-   页面演示：`src/page` 目录下，下载安装安装依赖后 `npm run start`\n-   单元测试：`src/__test__` 目录下，下载安装安装依赖后 `npm run test`\n\n所有示例根据自己理解添加了说明、并根据自己的理解补充示例；在文档最后补充两个作业：\n\n-   一个第三方文档 fishedee 的 `formily` 总结做一套练习\n-   补充一个掘金练习：自增对象\n\n## 工程文件\n\n主要的使用的包：\n\n-   NodeJS：`v18.13.0`\n-   脚手架：`Create React App` + `react-app-rewired` + `customize-cra`\n-   表单和UI库：`formily ` + `antd` + `antd-style`\n-   数据模拟：`mocker-api` + `mockjs`\n\n\u003e 启动项目前需要先安装一次依赖：`npm install`\n\n和官方文档（以下简称“文档”）不同点：\n\n-   采用`antd v5`作为默认演示，文档默认是v4\n-   采用`antd-style`作为`css-in-js`框架，可以在这里查看详细演示 [[查看](https://github.com/cgfeel/ant-design-style)]\n-   由于官方提供的`@formily/antd-v5`采用的版本是 `5.6`，很多 API 已发生变更，因此在演示组件中对部分组件重新修改\n-   根据演示文件，根据自己的理解，重新调整了文件和目录结构\n-   根据阅读的理解，添加个人附加案例\n\n## 阅读说明\n\n-   如上所述，`formily v2`的Api会将个人解读，通过源码注释写在源码\n-   而`README.md`将会每个演示包含的注解，作为索引简要的罗列出来，方便查找\n-   查找方法，每一个内容简介关联着对应示例，运行演示查找对应文件可以找到源码注释\n\n## 示例\n\n### 快速上手\n\n-   URL：`/start`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Start.tsx\n-   包含章节：\n    -   快速开始 [[查看](https://formilyjs.org/zh-CN/guide/quick-start)]\n\n### 场景案例\n\n#### 登录注册\n\n-   URL：`/login`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/LoginRegister.tsx\n-   包含章节：\n    -   登录注册 [[查看](https://formilyjs.org/zh-CN/guide/scenes/login-register)]\n\n**包含：**\n\n通过`Markup Schema`创建登录：\n\n-   验证模式：`createForm.validateFirst`\n-   字段说明：`SchemaField.String`，及相关属性\n    -   关联反应：`SchemaField.reactions`，主动受控和被动受控、受控依赖更新依赖组件状态\n-   创建自定义表单组件\n\n通过`Json Schema`创建登录\n\n-   包含：`SchemaField.schema`，使用JSON配置表单验证及其优点\n\n通过`JSX`创建登录：\n\n-   包括：`jsx`和`schema`的不同，以及优缺点\n\n通过`Markup Schema`创建注册：\n\n-   组件：`ArrayItems`、`Cascader`、`DatePicker`、`Editable`、`FormGrid`、`FormItem`、`FormLayout`、`Password`、`Select`、自定义组件`IDUpload`\n-   关联受控：作用域变量`$deps`、`$self`，受控行为、路径查找\n-   虚拟节点：充当`\u003cForm.Item\u003e`，充当交互组件，虚拟节点和对象节点不同处\n\n通过`Json Schema`创建注册：\n\n-   通过`@emotion/styled`来设置组件样式，从而解决`Json Schema`配置中无法使用`antd-style`的Hooks来配置组件样式\n\n通过`JSX`创建注册：\n\n-   `Jsx` 自增控件路径查找\n-   由于 `ArrayItems` 组件限定 `Schema` 场景，这里由 `ArrayField` + `ArrayBase`代替，包括代替方案存在的问题\n\n通过`Markup Schema`修改密码\n\n通过`Json Schema`修改密码：\n\n-   `Json Schema`中的 `key` 和表单 `name` 的关系\n\n通过`JSX`修改密码\n\n---- 分割线 ----\n\n#### 编辑详情\n\n-   URL：`/edit-detail`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/EditDetail.tsx\n-   包含章节：\n    -   编辑详情 [[查看](https://formilyjs.org/zh-CN/guide/scenes/edit-detail)]\n    -   `PreviewText` [[查看](https://antd.formilyjs.org/zh-CN/components/preview-text)]\n\n**包含：**\n\n通过组件`PreviewText.Placeholder`实现预览：\n\n-   默认全局预览方式\n-   局部预览方式（下方查询列表[[查看](#查询列表)]，个人补充了完整示例）\n-   切换预览和编辑状态\n-   表单组件根据可编辑状态受控响应（个人补充）\n-   使用`useField`获取可编辑状态\n\n巩固（个人补充）：\n\n-   无论组件怎么拆分，每个表单声明`createForm`只能匹配一个`Form`\n-   无法使用`useField`以及无法通过受控获取可编辑状态时，可消费`FormConsumer`获取实时状态\n\n\u003e 无法在`Filed`系列组件中通过`component`或`decorator`包裹的组件均无法受控、也不能使用`useField`，这也包过除此之外的所有 `React` 组件\n\n---- 分割线 ----\n\n#### 查询列表\n\n-   URL：`/table`\n-   目录：\n    -   https://github.com/cgfeel/formily/blob/main/src/page/Grid.tsx\n    -   https://github.com/cgfeel/formily/blob/main/src/page/Table.tsx\n-   包含章节：\n    -   查询列表 [[查看](https://formilyjs.org/zh-CN/guide/scenes/query-list)]\n    -   `ArrayTable` [[查看](https://antd5.formilyjs.org/zh-CN/components/array-table)]\n    -   `FormGrid` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-grid)]\n\n**包含：**\n\n由于查询列表文档只提供了一个概念以及一个参考性的代码，这里按照要求实践：\n\n-   `QueryList`：主要负责在顶层发请求\n-   `QueryTable`：一个`ArrayTable`，主要就是解析 `Schema` 子树，自己拼装出 `Table` 需要的 `Columns` 数\n-   `QueryForm`：负责查询筛选列表\n\n为了实现这个需求，同时参考了两篇文档：\n\n-   `ArrayTable`、`FormGrid`\n\n概括：\n\n-   `FormConsumer`消费数据更新改变提交状态\n-   `ArrayTable`的使用方法及总结、`ArrayTable.Column`的使用方法及总结\n-   `SchemaField`内的组件使用与`React`组件的不同\n-   通过`createForm`实现主动受控和被动受控\n-   受控中使用`when`\n-   自定义组件`PriceInterval`，和之前的不同的是这里还自定义了组件的 `onChange`事件\n\n---- 分割线 ----\n\n#### 弹窗与抽屉\n\n-   URL：`/dialog-drawer`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/DialogDrawer.tsx\n-   包含章节：\n    -   弹窗与抽屉 [[查看](https://formilyjs.org/zh-CN/guide/scenes/dialog-drawer)]\n    -   `FormDialog` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-dialog)]\n    -   `FormDrawer` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-drawer)]\n\n**包含：**\n\n演示了几个能力：\n\n-   快速打开，关闭能力：通过`IFormDialog`对象触发`open`和`close`\n-   中间件能力：`forCancel`、`forConfirm`、`forOpen`（`Drawer`仅支持`forOpen`）\n-   渲染函数内可以响应式能力：`schema`和`Field`\n-   上下文共享能力：仅限`Dialog`\n\n个人补充：\n\n-   性能调优，通过拆分`schema`和`Field`，具体见示例\n-   `Dialog`和`Drawer`行为差异\n-   修复`Drawer`适配问题\n\n\u003e **修复：** 截止于 24.03.18 `FormDrawer` 中使用 `antd v5` 的 `Dom` 结构和 `Api` 已发生更改，影响包括：`FormDrawer.Extra` 和 `FormDrawer.Footer` 无效；可以拷贝这个组件并替换组件路径：https://github.com/cgfeel/formily/blob/main/src/components/drawer/form-drawer/index.tsx\n\n---- 分割线 ----\n\n#### 分步表单\n\n-   URL：`/step-form`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/StepForm.tsx\n-   包含章节：\n    -   分步表单 [[查看](https://formilyjs.org/zh-CN/guide/scenes/step-form)]\n    -   `FormStep` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-step)]\n\n**包含：**\n\n-   `FormStep.createFormStep`和`createForm`一样，每次声明只能匹配一个`form`\n-   在前面登录示例中演示了`createSchemaField`设置`scope`，分步表单演示了后置动态设置`scope`\n\n---- 分割线 ----\n\n#### 选项卡、手风琴表单、自增选项卡、自增折叠表单\n\n-   URL：`/tab-collapse`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/TabCollapse.tsx\n-   包含章节：\n    -   选项卡/手风琴表单 [[查看](https://formilyjs.org/zh-CN/guide/scenes/tab-form)]\n    -   `FormCollapse` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-collapse)]\n    -   `FormTab` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-tab)]\n    -   `ArrayCollapse` [[查看](https://antd5.formilyjs.org/zh-CN/components/array-collapse)]\n    -   `ArrayTabs` [[查看](https://antd5.formilyjs.org/zh-CN/components/array-tabs)]\n\n**包含：**\n\n-   `antd v5`对于选项卡、手风琴表单、自增选项卡、自增折叠表单 API 的调整，以及交互操作\n-   巩固：`Json Schema` 中，对于需要为 `props` 传递上下文的情况，可以通过 `scope` 动态添加上下文对象\n-   拆分 `Markup Schema`组件 及 `Json Schema`\n\n**修复：**\n\n截止于 24.03.20，`@formily/antd-v5`部分组件 API 已废弃，我将其修复包括有：\n\n-   `FormTab`：不再支持 `TabPane` 改为 `items` [[查看](https://github.com/cgfeel/formily/blob/main/src/components/tab/form-tab/index.tsx)]\n-   `FormCollapse`：`Api` 已发生更改，不再支持 `CollapsePane` 改为 `items` [[查看](https://github.com/cgfeel/formily/blob/main/src/components/formCollapse/form-collapse/index.tsx)]\n-   `ArrayCollapse`：不再支持 `CollapsePane` 改为 `items` [[查看](https://github.com/cgfeel/formily/blob/main/src/components/arrayCollapse/array-collapse/index.tsx)]\n\n如果使用过程中官方仍旧没有修复，请查看对应的文件，拷贝修复\n\n---- 分割线 ----\n\n### 进阶指南\n\n#### 实现表单验证\n\n-   URL：`/validate`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Validate.tsx\n-   包含章节：\n    -   表单校验 [[查看](https://formilyjs.org/zh-CN/guide/advanced/validate)]\n\n**包含：**\n\n-   内置规则校验：`props`，`x-validator`对象、`x-validator`数组对象\n-   内置格式校验：`props`，`x-validator`对象（字符、对象、字符数组、对象数组）\n-   自定义规则校验：`registerValidateRules`\n-   自定义格式校验：`registerValidateFormats`\n-   异步校验：自定义规则校验 + `Promise`\n-   联动校验：`reactions`\n-   定义文案：`registerValidateLocale`\n\n**和文档不同：**\n\n-   自定义规则校验，`Json Schema`是通过`scope`这个 `props` 动态添加局部定义规则；这样更符合实际应用场景\n-   联动校验通过 3 种不同的方式进行：`createForm`中的`effects`、`schema`中的`x-reactions`，`Field`中的`reactions` 函数\n\n---- 分割线 ----\n\n#### 实现表单布局\n\n-   URL：`/layout`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Layout.tsx\n-   包含章节：\n    -   实现表单布局 [[查看](https://formilyjs.org/zh-CN/guide/advanced/layout)]\n    -   `FormLayout` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-layout)]\n    -   `FormItem` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-item)]\n    -   `FormGrid` [[查看](https://antd5.formilyjs.org/zh-CN/components/form-grid)]\n    -   `Space` [[查看](https://antd5.formilyjs.org/zh-CN/components/space)]\n\n**包含：**\n\n-   `Schema`中使用自定义非表单组件\n-   根据`antd v5.15.*`的 API 对组件进行修复\n-   使用`antd v5`自带的`css-in-js`添加组件样式\n-   通过联动修改`FormItem`的布局\n-   为`Schema`动态添加`Component`\n\n**组件修复：**\n\n-   `Canscader`：将接口对应至 `antd v5` 最新的 API\n-   `Form`：为 `FormLayout` 提供上下文支持\n-   `FormButtonGroup`：适配 `FormLayout`\n-   `FormItem`：\n    -   适配 `FormLayout`，补充必选样式\n    -   修复 `Select.multiple`、 `Switch` 在布局尺寸变更时的适配\n-   `FormLayout`：\n    -   补充 `layout: inline`布局支持，修复必选等接口\n    -   添加布局样式支持\n\n\u003e 原本是想为 `@formily/antd-v5` 提交 PR，由于当前 `@formily/antd-v5` 依赖的是 `antd v5.6` 导致 API 不兼容，又不能修改工程依赖，所以采用这种方式进行修改\n\n---- 分割线 ----\n\n#### 实现异步数据源\n\n-   URL：`/async`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/AsyncCom.tsx\n-   包含章节：\n    -   实现异步数据源 [[查看](https://formilyjs.org/zh-CN/guide/advanced/async)]\n    -   `Select` [[查看](https://antd5.formilyjs.org/zh-CN/components/select)]\n    -   `TreeSelect` [[查看](https://antd5.formilyjs.org/zh-CN/components/tree-select)]\n    -   `Cascader` [[查看](https://antd5.formilyjs.org/zh-CN/components/cascader)]\n\n**包含：**\n\n-   `Select` 同步获取数据、异步搜索、异步联动的三种模式（下方注1）\n-   `TreeSelect` 同步获取数据、异步联动的三种模式\n-   `Cascader` 异步数据三种模式\n\n\u003e 注1：总结中，所有提到的三种模式分别为：`Markup Schema`、`Json Schema`、`Field Jsx`，如果没有单独说明，二种模式为：`Markup Schema`、`Json Schema`\n\n**个人补充案例：**\n\n-   `TreeSelect` 异步加载数据三种模式\n-   `Cascader` 修复静态数据加载 [[查看](https://github.com/cgfeel/formily/blob/main/src/components/cascader/cascader/index.tsx)]\n    -   适配 `antd v5` 最新版本中已废弃，而 `formily` 中又必须传入的属性\n-   `Cascader` 异步加载数据三种模式\n-   对比异步数据不同的加载方式\n\n**异步数据加载方式：**\n\n-   初始只加载一次\n-   依赖输入内容每次加载：`field.query({path}).value()`\n-   通过 `observable.ref` 创建引用劫持响应式对象\n-   通过 `scope` 初始只加载一次\n-   通过 `scope` 依赖输入内容每次加载\n\n---- 分割线 ----\n\n#### 实现表单受控\n\n-   URL：`/controlled`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Controlled.tsx\n-   包含章节：\n    -   实现表单受控 [[查看](https://formilyjs.org/zh-CN/guide/advanced/controlled)]\n\n**几个受控模式：**\n\n-   普通受控模式，随 React 组件生命周期，演示：只存在控制者、通过 `props` 受控、通过 `ref` 受控\n-   响应式值受控：使用 `observable` 提供数据用于响应受控\n-   `Schema` 整体受控：通过替换整个 `form` 和 `schema` 实现表单整体切换\n-   `Schema` 片段联动：\n    -   通过 `form.clearFormGraph` 回收字段模型，从而实现部分表单更新\n    -   通过 `observer` 包裹函数组件响应字段更新，常用于自定义组件\n\n**包含：**\n\n-   修复文档普通受控模式的问题\n-   最佳响应实践\n-   反模式：和文档不同，这里分别从 3 个案例来演示受控逐步去掉响应的过程\n-   补充 `form.setValues` 包含的 4 个类型的值的区别（文档并没有说明）\n\n**巩固：**\n\n-   在 `React` 组件中要响应表单数据，要么刷新整个表单；要么在受控组件上分别包裹 `observer` 响应对应的表单数据变化\n-   而在受控表单中 (`SchemaField`)，则建议通过“响应式值受控”，这样不会多余消费 `React` 组件性能\n-   关于响应函数\n    -   `observable`：创建响应值，提供给控制者或 `Form.values`，它包含一个 `observable.ref`，在“实现异步数据源”有提到\n    -   `observer`：响应组件的响应，用于在组件内容响应依赖数据的更新，暂且可以把它当作 `memo` 来理解\n    -   `Observer`：用于在组件中提供一个响应区域，暂且可以把它当作 `useMemo` 来理解\n\n**附加：**\n\n在这个演示中，包含 4 个 `@formily/react` 内容：`RecursionField`、`useForm`、`useField`、`observer`，分别在演示备注中说明，稍后在具体章节再演示\n\n---- 分割线 ----\n\n#### 实现联动逻辑\n\n-   URL：`/controlled`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/LinkAges.tsx\n-   包含章节：\n    -   实现联动逻辑 [[查看](https://formilyjs.org/zh-CN/guide/advanced/linkages)]\n\n**总结：**\n\n1.  一对一通过：路径匹配 [[查看](https://core.formilyjs.org/zh-CN/api/entry/form-path#formpathpattern)]\n2.  一对多通过：通配符 `*()`，如：`*(.input1,input2)`\n3.  依赖联动通过 `field.query` 或 `form.values` 或 `schema` 中使用 `dependencies`\n4.  链式联动：依赖的字段可以层层匹配，例如：A 控制 B，B 控制 C，当 A 操作 B 的时候，C 也有可能受到响应变化，反之同理\n5.  循环联动：和依赖联动是一样的，区别在于依赖字段之间可以相互操作，谨慎使用，可能会导致逻辑问题\n6.  自身联动：将匹配的路径设为自身，或在被动联动中去掉路径\n7.  异步联动：在联动 `hook` 回调中，可以接受异步函数作为方法，作为异步联动，例如：`fetch`\n\n**包含：**\n\n-   以上7 个模式的 `Effect` 和 `Schema` 方式，以及被动逻辑和联动逻辑分别演示\n\n**主动和被动联动：**\n\n-   `onFieldValueChange` 主动联动，`onFieldReact` 被动联动\n-   也可以在 `schema` 响应中使用 `reactions`，区别在于：主动联动有 `target` 属性\n\n**因此：**\n\n1. 在 `schema` 中，只有主动联动才需要指定生命周期 `effect`\n2. 主动联动适合监听某一个固定字段，所以路径从监控的指定字段开始匹配\n3. 被动联动适合某一个字段受控于其他多个字段对齐的影响，所以路径从被监控的字段开始\n\n**附加：**\n\n`FormEffectHooks` 内容：`FieldEffectHooks`、`setFormState`、`setFieldState`、`SchemaReactions`、`FieldReaction`，稍后在具体章节再演示，也可以启动演示项目查看相关备注\n\n---- 分割线 ----\n\n#### 实现联动计算器\n\n-   URL：`/calculator`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Calculator.tsx\n-   包含章节：\n    -   实现联动计算器 [[查看](https://formilyjs.org/zh-CN/guide/advanced/calculator)]\n\n**包含：**\n\n联动逻辑运算 + 自增表单的一个练习，提供 `Markup Schema` 和 `Json Schema`\n\n---- 分割线 ----\n\n#### 实现自定义组件\n\n-   URL：`/custom`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Custom.tsx\n-   包含章节：\n    -   实现自定义组件 [[查看](https://formilyjs.org/zh-CN/guide/advanced/custom)]\n    -   `connect` [[查看](https://react.formilyjs.org/zh-CN/api/shared/connect)]\n    -   `mapProps` [[查看](https://react.formilyjs.org/zh-CN/api/shared/map-props)]\n    -   `mapReadPretty` [[查看](https://react.formilyjs.org/zh-CN/api/shared/map-read-pretty)]\n\n**包含：**\n\n文档并没有提到具体内容，这里对前面所有内容归纳总结如下：\n\n-   通过 `props` 转发给组件\n-   定制 `antd` 组件\n-   自定义非表单组件\n-   修复 `@formily/antd-v5` 组件\n-   通过 `observer` 包裹组件\n-   在 `observer` 组件中使用 `hooks`\n-   通过 `connect` 接入组件库\n\n---- 分割线 ----\n\n#### 数据解构、业务逻辑、打包\n\n-   包含章节：\n    -   前后端数据差异兼容方案 [[查看](https://formilyjs.org/zh-CN/guide/advanced/destructor)]\n    -   管理业务逻辑 [[查看](https://formilyjs.org/zh-CN/guide/advanced/business-logic)]\n    -   按需打包 [[查看](https://formilyjs.org/zh-CN/guide/advanced/build)]\n\n**数据解构 (前后端数据差异兼容方案)：**\n\n-   URL：`/destructor`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Destructor.tsx\n\n将 `name` 由普通的字符修改为 `[{name},{name}]` 这样的方式解构字段\n\n**管理业务逻辑：** 无案例，总结如下\n\n两种方式：\n\n-   全局设定：`form` 中的 `effects`\n-   局部设定：`JSX` 中使用 `reactions` 或 `schema` 中使用 `x-reactions`\n\n局部设定有缺点：\n\n-   优点：简单、直接写在字段上\n-   存疑：文档提到多场景，多字段维护，但 `reactions` 是可以和 `scope` 结合使用的\n-   缺点：不能逻辑复用，即便是 `scope` 结合使用的\n\n全局设定有缺点：\n\n-   优点：多字段处理，量化处理、逻辑复用、逻辑分离为单独文件\n-   缺点：不能作为服务端配置\n\n文档建议：\n\n-   纯源码模式\n    -   字段数量庞大，逻辑复杂，优先选择 `effects` 中定义逻辑\n    -   字段数量少，逻辑简单，优先选择 `reactions` 中定义逻辑\n-   `Schema` 模式\n    -   不存在异步逻辑，优先选择结构化 `reactions` 定义逻辑\n    -   存在异步逻辑，或者大量计算，优先选择函数态 `reactions` 定义逻辑\n\n**按需打包：** 无案例，总结如下\n\n-   `antd v5` 采用 `tree shaking` 方式，无需使用文档中提到的 `babel-plugin-import`\n-   本项目是基于 `create-react-app`，如果需要配置 `Webpack` 还是建议添加这两个包：`react-app-rewired` `customize-cra`\n\n---- 分割线 ----\n\n### Reactive Library\n\n-   URL：`/reactive`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Reactive.tsx\n-   包含章节：\n    -   整个 API 文档 [[查看](https://reactive.formilyjs.org/zh-CN/api/observable)]\n\n**总结：**\n\n-   暂且可以将其作为表单中的 `mbox` 来看，不同之处在于 `reactive` 更注重于对响应对象的操作，其提供的 API 都围绕这方面\n-   建议本地运行查看，这里只罗列索引，本地注释了详细备注\n\n#### observable\n\n创建响应对象\n\n-   `observable/observable.deep`：深拷贝\n-   `observable.shallow`：浅拷贝，响应对比\n-   `observable.computed`：响应计算，直接计算，`get\\set` 模式\n-   `observable.ref`：引用响应劫持对象，只能用于修改对象的`value`才能发生响应\n-   `observable.box`：`observable.ref` 的 `get\\set` 模式\n\n#### autorun\n\n在演示中演示了执行过程的顺序\n\n-   `autorun`：接收一个函数函数作为 `tracker`，并返回一个 `dispose` 函数用于停止响应\n-   `autorun.memo`：在 `autorun` 内部创建一个响应对象\n-   `autorun.effect`：在 `autorun` 内部的副作用处理\n\n\u003e `autorun` 的执行过程通过微任务来实现\n\n#### reaction\n\n在演示中演示了执行过程的顺序\n\n-   在响应过程中个执行一个脏检查，并返回一个 `dispose` 函数用于停止响应\n-   方法中 `tracker` 会随着响应数据更新调用，而 `subscriber` 只对更新数据有变化时才响应\n\n#### batch\n\n定义批量操作：在同一个任务事件中拆分成不同的微任务，通过堆栈分别执行\n\n-   `batch.scope`：局部batch\n-   `batch.abound`：异步 batch\n-   `batch.endpoit`：结束回调\n\n在演示中演示了：\n\n-   `batch` 内部执行顺序\n-   `batch` 外部执行响应\n\n\u003e `batch` 的执行过程通过微任务来实现\n\n#### action\n\n和 `batch` 一样，不同在于：\n\n-   不收集依赖，关于依赖详细见演示\n-   没有 `endpoint`\n\n#### defined\n\n手动定义领域模型，在文档中有个问题：\n\n-   在定义的模型类中 `box` 初始值是数值，之后通过 `define` 修正为 `observable.box`\n-   然而 `number` 类型是没有 `get\\set` 操作的，这点对于 `vsc`、`eslint` 来说是不能理解的\n\n修正：\n\n-   直接将 `box` 类型通过 `observable.box` 声明，取消 `define` 类型覆盖\n\n#### model\n\n快速定义模型，详细见演示\n\n#### observe\n\n和 `autorun` 的不同：\n\n-   监听 `observable` 对象的所有操作，`autorun` 只响应值的变化\n-   不响应 `observable` 初始值\n\n#### markRaw\n\n两个特征\n\n-   包裹对象，使其 `observable` 不响应\n-   包裹类，使其类声明的对象都不受 `observable` 响应\n\n#### markObservable\n\n`observable` 不响应有 3 类：`React Node` 与带有 `toJSON`、`toJS` 方法的对象\n\n两个特征：\n\n-   包裹对象，使其 `observable` 响应\n-   包裹类，使其类声明的对象受 `observable` 响应\n\n#### raw\n\n从 `observable` 对象中获取源数据\n\n#### toJS\n\n将 `observable` 转化为普通的 JS 对象，转换后的值不能用于依赖收集\n\n#### untracked\n\n函数内包裹的 `observable` 永远不会被收集依赖\n\n#### hasCollected\n\n用于检测某段执行逻辑是否存在依赖收集，演示中分别鉴定：\n\n-   `toJS` 属性对象、`markObservable` 对象、正常的 `observable` 对象、`markRaw` 对象、`toJS`对象\n-   只有 `markObservable` 对象、正常的 `observable` 对象能够正常依赖\n\n#### Tracker\n\n手动跟踪依赖，特征如下：\n\n-   `tracker.track` 调用后不会重复执行\n-   `Tracker` 构造中的 `scheduler` 声明后会随依赖值的变化而调用\n\n借此每次跟踪结束后，需要通过 `scheduler` 来决定后续跟踪\n\n#### Type Checker\n\n-   `isObservable`：判断是否为 `observable` 对象，演示了 `observable` 和 `observable toJS`\n-   `isAnnotation`：判断是否为 `Annotation` 对象，演示了 `action.bound` 和普通函数\n-   `isSupportObservable`：是否可以被 `observable` 对象，演示了普通对象和带有 `toJS` 的对象\n\n#### observer\n\n在 `React` 中，将 `Function Component` 变成 `Reaction`\n\n-   可以把 `observer` 和 `Observer` 的关系看作是 `memo` 和 `useMemo`\n-   演示中将文档示例做了拆分，建议对照比较\n-   没有记录 `vue` 部分，当前以 `React` 技术栈为主\n\n更多往下查看单元测试\n\n---- 分割线 ----\n\n### Core Library\n\n-   URL：`/core`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/Core.tsx\n-   包含章节：\n    -   整个 API 文档 [[查看](https://core.formilyjs.org/zh-CN/api/entry/create-form)]\n\n我将使用 `formily` 将这个库里对应的对象和属性，做成在线工具的形式进行演示，建议本地运行上手操作\n\n#### createForm\n\n创建一个 `form` 对象，其对象属性可以直接通过本地演示操作查看效果\n\n\u003cimg width=\"639\" alt=\"image\" src=\"https://github.com/cgfeel/formily/assets/578141/dd99e88c-2346-44b6-9c7b-451c86a6bf32\"\u003e\n\n#### Form Effect Hooks\n\n表单生命周期\n\n-   加载卸载：`onFormInit`、`onFormMount`、`onFormUnmount`\n-   表单变化：`onFormReact`、`onFormValuesChange`、`onFormInitialValuesChange`、`onFormInputChange`\n-   表单提交：`onFormSubmitStart`、`onFormSubmitSuccess`、`onFormSubmitEnd`、`onFormSubmitFailed`、`onFormSubmit`\n-   提交验证：`onFormSubmitValidateStart`、`onFormSubmitValidateSuccess`、`onFormSubmitValidateFailed`、`onFormSubmitValidateEnd`\n-   表单验证：`onFormValidateStart`、`onFormValidateEnd`、`onFormValidateSuccess`、`onFormValidateFailed`\n\n生命周期顺序在本地演示说明，更多请本地运行查看：\n\n\u003cimg width=\"507\" alt=\"image\" src=\"https://github.com/cgfeel/formily/assets/578141/9eb69c18-cdee-4536-967b-b5567e6c19d4\"\u003e\n\n#### Field Effect Hooks\n\n-   加载卸载：`onFieldInit`、`onFieldMount`、`onFieldUnmount`\n-   字段变化：`onFieldReact`、`onFieldChange`、`onFieldValueChange`、`onFieldInitialValueChange`、`onFieldInputValueChange`\n-   字段验证：`onFieldValidateStart`、`onFieldValidateEnd`、`onFieldValidateFailed`、`onFieldValidateSuccess`\n\n生命周期顺序在本地演示说明，更多请本地运行查看：\n\n\u003cimg width=\"385\" alt=\"image\" src=\"https://github.com/cgfeel/formily/assets/578141/0a56834c-09aa-47f4-8914-a57986e011bc\"\u003e\n\n#### Form Hooks API\n\n-   创建自定义钩子监听器：`createEffectHook`，详细见单元测试补充\n-   创建一个副作用的上下文：`createEffectContext`，提供一个 `provide`，将组件中的能力托管出来，提供一个消费方 `consume` 在外部消费上下文托管对象\n-   在表单生命周期中获取当前 `form` 对象：`useEffectForm`，可以搭配 `createEffectContext` 一起使用\n\n#### Form Checkers\n\n-   对象检查：`isForm`、`isField`、`isArrayField`、`isObjectField`、`isVoidField`、`isGeneralField`、`isDataField`、`isQuery`\n-   状态检查：`isFormState`、`isFieldState`、`isArrayFieldState`、`isObjectFieldState`、`isVoidFieldState`、`isGeneralFieldState`、`isDataFieldState`\n\n本地使用 `formily` 开发演示工具，更多请本地运行查看：\n\n\u003cimg width=\"677\" alt=\"image\" src=\"https://github.com/cgfeel/formily/assets/578141/b8fa5ba1-68cf-4f4b-895c-5062c8816f82\"\u003e\n\n#### Form Path\n\n本地使用 `formily` 开发演示工具\n\n-   包含：属性、数据路径语法、匹配路径语法、方法、静态方法进\n-   支持自定义路径匹配查询结果\n\n更多请本地运行查看：\n\n\u003cimg width=\"634\" alt=\"image\" src=\"https://github.com/cgfeel/formily/assets/578141/9cec78f8-de13-4208-bfd4-405515a30f0e\"\u003e\n\n\u003cimg width=\"637\" alt=\"image\" src=\"https://github.com/cgfeel/formily/assets/578141/ac7867d1-3e57-4c9f-a293-e9b30d6701f1\"\u003e\n\n#### Form Validator Registry\n\n`formily` 验证格式、定制信息、定制模板、校验规则、语言等，包含：\n\n-   `setValidateLanguage`：定制语言\n-   `registerValidateFormats`：注册校验格式，包含全局注册、局部注册\n-   `registerValidateLocale`：定制提示\n-   `registerValidateMessageTemplateEngine`：定制信息模板\n-   `registerValidateRules`：定制校验规则，包含全局定制、局部定制\n-   `getValidateLocaleIOSCode`：获取内置存在的 `ISO Code`\n\n更多往下查看单元测试\n\n---- 分割线 ----\n\n### React Library\n\n-   URL：`/react`\n-   目录：https://github.com/cgfeel/formily/blob/main/src/page/ReactLibrary.tsx\n-   包含章节：\n    -   整个 API 文档 [[查看](https://react.formilyjs.org/zh-CN/api/components/field)]\n\n详细了解建议：查看本地演示或查看单元测试\n\n#### Components\n\n-   `Field`：普通字段\n-   `ArrayField`：数组字段\n-   `ObjectField`：对象字段\n-   `VoidField`：虚拟字段\n-   `SchemaField`：解析 `Schema`、渲染字段、提供 `scope`、传递上下文\n-   `RecursionField`：递归渲染组件，主要有 2 种，将属性递归，组件自身递归。详细见单元测试\n-   `FormProvider`：入口组件，传递上下文\n-   `FormConsumer`：在 `SchemaField` 外部消费表单\n-   `ExpressionScope`：自定义组件内部给 `json-schema` 表达式传递局部作用域\n-   `RecordScope`：作用域注入组件一个有层级对象，主要三个变量：`$record`、`$index`、`$lookup`\n-   `RecordsScope`：作用域注入组件一个对象集合，主要三个变量：`$records`\n\n#### Hooks\n\n-   `useExpressionScope`：自定义组件中读取表达式作用域\n-   `useField`：自定义组件内读取当前字段属性，操作字段状态等\n-   `useFieldSchema`：自定义组件中读取当前字段的 `Schema` 信息\n-   `useForm`：自定义组件中读取当前 `Form` 实例\n-   `useFormEffect`：自定义组件中往当前 `Form` 实例额外注入副作用逻辑\n-   `userParentForm`：读取最近的 `Form` 或者 `ObjectField` 实例\n\n**本地演示补充：**\n\n-   `useExpressionScope` 包含各个层级作用域下发捕获\n-   `useField` 和 `useFieldSchema` 区别\n-   `userParentForm` 补充了父子表单交互示例\n\n#### Shared\n\n-   `connect`：第三方组件库的无侵入接入 `Formily`\n-   `mapProps`：将 `Field` 属性与组件属性映射的适配器函数，主要与 `connect` 函数搭配使用\n-   `mapReadPretty`：给组件赋予阅读状态，主要与 `connect` 函数搭配使用\n-   `observer`：为 `react` 函数组件添加 `reactive` 特性\n\n**需要通过单元测试了解：**\n\n以下建议通过单元测试、`formily` 组件源码了解使用\n\n-   `FormContext`：`Form` 上下文，可以获取当前 `Form` 实例\n-   `FieldContext`：字段上下文，可以获取当前字段实例\n-   `SchemaMarkupContext`：`Schema` 标签上下文\n-   `SchemaContext`：字段 `Schema` 上下文\n-   `SchemaExpressionScopeContext`：`Schema` 表达式作用域上下文\n-   `SchemaOptionsContext`：`Schema` 全局参数上下文，主要用于获取从 `createSchemaField` 传入的参数\n-   `Schema`：解析、转换、编译 `json-schema` 的能力\n\n\u003e 从 `@formily/react` 中可以导出 `Schema` 这个 `Class`，但是不希望使用 `@formily/react`，可以单独依赖 `@formily/json-schema` 这个包\n\n更多往下查看单元测试\n\n---- 分割线 ----\n\n## 单元测试\n\n### Reactive Library\n\n内部方法不再当前研究范围，不包含测试用例：`array.spec.ts` [[查看](https://github.com/alibaba/formily/blob/formily_next/packages/reactive/src/__tests__/array.spec.ts)]\n\n#### `action`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/action.spec.ts\n\n不可收集依赖的批量操作，具体特性见单元测试\n\n`action` 批量操作普通用法：\n\n-   不使用 `action` 每次修改 `observable` 都会响应一次\n-   `action` 内部所有修改只记录一次响应\n-   在 `track` 函数中使用 `action`\n-   `action.bound` 绑定一个批量操作\n-   在 `track` 函数中使用 `action.bound`\n-   `action.scope` 在 `action` 中分批执行\n-   使用 `action.socpe.bound`\n-   在 `track` 函数中使用 `action.scope`\n-   在 `track` 函数中使用 `action.scope.bound`\n\n`define` 定义模型中使用 `action` 批量操作：\n\n-   `define` 中使用 `action`\n-   在 `track` 函数中使用模型 `action`\n-   `define` 中使用 `action.bound`\n-   `track` 函数中使用模型 `action.bound`\n-   `define` 中使用 `action.scope`\n-   `define` 中使用 `action.scope.bound`\n-   `track` 函数中使用模型 `action.scope`\n-   `track` 函数中使用模型 `action.scope.bound`\n-   嵌套 `action` 批量操作在 `reaction` 中 `subscrible`\n-   嵌套 `action` 和 `batch` 批量操作在 `reaction` 中 `subscrible`\n\n#### `batch`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/batch.spec.ts\n\n定义批量操作，内部可以收集依赖\n\n`batch` 批量操作普通用法：\n\n-   不使用 `batch` 每次修改 `observable` 都会响应一次\n-   `batch` 内部所有修改只记录一次响应\n-   在 `track` 函数中使用 `batch`\n-   `batch.bound` 绑定一个批量操作\n-   在 `track` 函数中使用 `batch.bound`\n-   `batch.scope` 在 `batch` 中分批执行\n-   使用 `batch.socpe.bound`\n-   在 `track` 函数中使用 `batch.scope`\n-   在 `track` 函数中使用 `batch.socpe.bound`\n-   在 `batch` 中抛出错误\n\n`define` 定义模型中使用 `batch` 批量操作：\n\n-   `define` 中使用 `batch`\n-   在 `track` 函数中使用模型 `batch`\n-   `define` 中使用 `batch.bound`\n-   `track` 函数中使用模型 `batch.bound`\n-   `define` 中使用 `batch.scope`\n-   `define` 中使用 `batch.socpe.bound`\n-   在 `track` 函数中使用模型 `batch.scope`\n-   在 `track` 函数中使用 `batch.socpe.bound`\n\n批量操作结束回调，`batch` 独有 `action` 没有：\n\n-   `batch.endpoint` 注册批量执行结束回调\n-   `batch.endpoint` 意外的结束 - 不提供回调也不会执行\n-   直接使用 `batch.endpoint`\n\n\u003e `batch.endpoint` 不是微任务，而是单纯的回调函数\n\n其他：\n\n-   在 `reaction` 有效的收集依赖触发 `subscrible`\n-   在 `reaction subscript` 中无效的依赖不会反向触发响应\n\n参考：\n\n\u003e `reaction` 中 `subscrible` 不收集依赖 [[查看](#%E6%B5%85%E5%93%8D%E5%BA%94-autorunreaction)]\n\n-   正常的情况：`track` 函数会收集依赖，其他中两个例子是通过 `autorun` 收集依赖触发 `reaction` 的 `track` 函数中的依赖进行响应\n-   不正常的情况：在 `reaction` 的 `subscrible` 函数中会随着 `track` 函数响应触发调用，但在 `subscrible` 添加 `observable` 对象，试图更新反向触发响应是行不通的\n\n#### `annotations` - `observable`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/annotations.spec.ts\n\n创建不同响应式行为的 `observable` 对象：\n\n-   `observable` 创建劫持对象 - 默认深度劫持\n-   `observable.shallow` 创建的是浅劫持响应式对象\n-   `observable.box` 创建引用劫持响应式对象，带有 `get`/`set` 方法\n-   `observable.ref` 创建引用劫持响应式对象\n-   `action.bound` 中更新 `observable` 对象\n-   非批量操作中更新 `observable` 对象\n-   `observable.computed` 创建一个计算缓存器\n-   创建一个链式 `observable.computed`\n-   `model` 快速定义领域模型\n-   `model` 中创建一个计算缓存器，计算数组长度\n-   `model` 中创建一个计算缓存器，收集依赖\n-   `observable.computed` 容错机制\n-   `observable.computed` 接受一个带有 get 属性方法的对象\n-   `untracked` 中使用 `observable.computed` 对象\n-   `define` 定义一个类为领域模型\n\n#### `observable` - `Map`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/collections-map.spec.ts\n\n劫持 `Map` 类型对象作为 `observable` 对象：\n\n-   创建一个 `Map` 类型的 `observable` 对象\n-   在 `autorun` 中响应 `map` 类型对象\n-   在 `autorun` 中响应 `map.size`\n-   在 `autorun` 中通过 `for of` 迭代 `map` 获取值\n-   在 `autorun` 中通过 `forEach` 迭代 `map` 获取值\n-   在 `autorun` 中通过 `for of` 迭代 `map.keys` 获取值\n-   在 `autorun` 中通过 `for of` 迭代 `map.values` 获取值\n-   在 `autorun` 中通过 `for of` 迭代 `map.entries` 获取值\n-   在 `autorun` 中通过 `map.clear` 触发响应\n-   在 `autorun` 中不响应错误的 `map` 自定义属性\n-   在 `autorun` 中不响应未更新的数据\n-   在 `autorun` 中不响应 `map` 类型原始数据\n-   在 `autorun` 中不响应 `map` 类型原始数据迭代\n-   在 `autorun` 中不响应 `map` 类型原始数据的增删操作\n-   在 `autorun` 中不响应 `map` 类型原始数据长度\n-   在 `autorun` 中不响应 `map` 类型原始数据添加\n-   `map` 类型的 `observable` 对象中允许使用 `object` 作为 `key`\n-   `map` 类型的 `observable` 对象中允许设置一个普通对象，或是 `observable` 对象作为 `value`\n-   浅劫持 `map` 对象，不会响应对象的属性值修改\n\n#### `observable` - `Set`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/collections-set.spec.ts\n\n劫持 `Set` 类型对象作为 `observable` 对象\n\n-   创建一个 `set` 类型的 `observable` 对象\n-   在 `autorun` 中响应 `set` 类型对象\n-   在 `autorun` 中响应 `set.size`\n-   在 `autorun` 中通过 `for of` 迭代 `set`\n-   在 `autorun` 中通过 `forEach` 迭代 `set`\n-   在 `autorun` 中通过 `for of` 迭代 `set.keys` 获取值\n-   在 `autorun` 中通过 `for of` 迭代 `set.values` 获取值\n-   在 `autorun` 中通过 `for of` 迭代 `set.entries` 获取值\n-   在 `autorun` 中不响应 `set` 错误的自定义属性\n-   在 `autorun` 中不响应没有变更的 `set` 对象\n-   在 `autorun` 中不响应 `set` 类型原生数据迭代\n-   在 `autorun` 中不响应 `set` 类型原生数据的新增、删除\n-   在 `autorun` 中不响应 `set` 类型原生数据的 `set.size`\n-   在 `autorun` 中不响应来自 `set` 类型原生数据添加的项目\n\n#### `observable` - `WeakMap`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/collections-weakmap.spec.ts\n\n劫持 `WeakMap` 类型对象作为 `observable` 对象\n\n-   创建一个 `WeakMap` 类型的 `observable` 对象\n-   在 `autorun` 中响应 `WeakMap` 类型对象\n-   在 `autorun` 中不响应 `WeakMap` 不合理的自定义属性更新\n-   在 `autorun` 中不响应 `WeakMap` 中没有更新的属性\n-   在 `autorun` 中不响应 `WeakMap` 原生对象\n-   在 `autorun` 中不响应来自 `WeakMap` 原生对象增删操作\n\n#### `observable` - `WeakSet`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/collections-weakset.spec.ts\n\n劫持 `WeakSet` 类型对象作为 `observable` 对象\n\n-   创建一个 `WeakSet` 类型的 `observable` 对象\n-   在 `autorun` 中响应 `WeakSet` 对象\n-   在 `autorun` 中不响应 `WeakSet` 对象不合理的自定义属性更新\n-   在 `autorun` 中不响应 `WeakSet` 对象没有更新的属性\n-   在 `autorun` 中不响应 `WeakSet` 原生对象\n-   在 `autorun` 中不响应来自 `WeakSet` 原生对象触发的增删操作\n\n#### `observable` - `other`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/observable.spec.ts\n\n-   `array observable` 操作\n-   `contains` 判断 `observable` 对象中是否包含指定对象\n-   不能直接设置 `observable` 的 `__proto__`\n\n#### 创建模型：`define`、`model`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/define.spec.ts\n\n手动定义领域模型\n\n-   `define` + `observable`，响应手动定义领域模型\n-   `define` + `observable.shallow`，响应手动定义浅劫持领域模型\n-   `define` + `observable.box`，响应手动定义一个带有 `get`/`set` 方法的领域模型\n-   `define` + `observable.ref`，响应手动定义一个领域模型的引用\n-   `define` + `observable` + `batch`，批量操作中响应手动定义的领域模型\n-   `define` + `observable.computed`，响应手动定义一个领域模型的计算缓存器\n-   `define` 手动定义领域模型容错机制\n-   `model` 快速定义领域模型\n\n`model` 是快速定义一个模型\n\n-   `getter`/`setter` 属性自动声明 `computed`\n-   函数自动声明 `action`\n-   普通属性自动声明 `observable`\n\n`define` 需要手动定义对象，可以是对象，也可以是一个类，需要手动指定对象属性、方法作为 `observable`\n\n#### 浅响应 `autorun`、`reaction`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/autorun.spec.ts\n\n接收一个 `tracker` 函数用于响应 `observable` 数据变化，他们都返回一个 `dispose` 函数用于停止响应。分别如下：\n\n-   `autorun`：只接受 `tracker` 函数\n-   `reaction`：\n    -   接受 `tracker` 函数\n    -   接受 `callback` 函数作为 `subscrible`\n    -   接受一个属性用于初始化执行、脏检查等\n\n除此之外在 `tracker` 函数中会自动收集 `observable` 依赖，除非：\n\n-   使用了非 `observable` 对象\n-   使用的对象被 `action`、`untracked` 包裹\n\n注意：\n\n-   浅响应只响应指定对象，对象下的属性除非指定情况下会收集，否则不会主动收集\n-   `reaction` 可以通过 `equals` 脏检查将对象通过 `JSON.stringify` 转换成字符串作为深比较，但建议深比较通过下方的 `observe` 来响应\n\n测试示例：\n\n-   创建一个 `autorun`\n-   创建一个 `reaction`，监听 `subscrible` 订阅\n-   `reaction` 初始化后立即响应\n-   `reaction` 中 `subscrible` 不收集依赖\n-   `reaction` 中进行数据的脏检查\n-   `reaction` 响应中浅比较 - 默认\n-   `reaction` 响应中深比较\n-   在 `autorun` 中递增 `observable` 对象\n-   `autorun` 初始化收集的依赖决定后续响应情况\n-   `autorun` 中间接递归响应 - 单向递归\n-   `autorun` 中间接递归响应 - 批量操作递归\n-   `autorun` 跳出响应前，通过头部赋值收集依赖\n-   `autorun.memo` 在 `autorun` 中用于创建持久引用数据\n-   使用 `observable` 对象创建一个 `autorun.memo`\n-   `autorun.effect` 在 `autorun` 添加一个微任务，在响应结束前执行\n-   `autorun.memo` 中添加依赖\n-   `autorun.memo` 响应依赖更新以及 `autorun` 停止响应\n-   `autorun.memo` 容错，传递无效值\n-   在 `autorun` 外部使用 `autorun.memo` 会抛出错误\n-   在 `autorun` 中不使用 `autorun.memo` 无效递增\n-   `autorun.effect` 微任务的执行和结束回调\n-   `autorun.effect` 结束前 `autorun` 已 `dispose`\n-   `autorun.effect` 添加依赖\n-   `autorun.effect` 不添加依赖默认为 `[{}]`，随 `autorun` 每次都响应\n-   `autorun.effect` 在 `autorun` 外部使用将抛出错误\n-   `autorun.effect` 容错\n-   在 `batch` 内部停止 `autorun` 响应\n-   `autorun` 依赖 `observable.computed` 计算的值\n-   `autorun` 依赖 `observable.computed` 对象在 `delete` 后的响应\n-   `autorun` 依赖 `observable.computed` 使用 `Set` 类型对象\n-   `autorun` 依赖 `observable.computed` 删除 `Set` 类型子集\n-   `autorun` 依赖 `observable.computed` 使用 `Map` 类型对象\n-   `autorun` 依赖 `observable.computed` 删除 `Map` 类型子集\n-   `autorun` 中有条件的依赖收集\n-   `reaction` 中有条件的依赖收集、`subscrible`、`fireImmediately`\n\n#### 浅响应 `Tracker`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/tracker.spec.ts\n\n在依赖发生变化时不会重复执行 `tracker` 函数，需要用户手动重复执行，只会触发 `scheduler`\n\n-   `Tracker` 手动跟踪基础用法\n-   `Tracker` 嵌套跟踪\n-   `Tracker` 根据条件收集依赖\n-   `Tracker` 共享跟踪调度器\n\n#### 深响应 `observe`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/observe.spec.ts\n\n监听 `observable` 对象的所有操作，支持深度监听也支持浅监听\n\n-   `observe` 深响应\n-   `observe` 浅响应 - 第三个参数设置 `false`\n-   `observe` 响应根节点替换\n-   `observe` 通过 `dispose` 停止响应\n-   `observe` 中 `track` 函数的使用给定的参数进行条件判断\n-   `observe` 中动态树，见注 ②\n-   `observe` 响应对象传递为函数将会抛错\n\n注 ①：\n\n\u003e 当收集 `observable` 对象作为依赖进行响应的时候，修改的值没有变化，那么不会触发响应，这仅限于对象的值是原始类型数据；如果对象的值是引用类型的数据，由于引用的地址发生了改变，即便看上去值是一样的，依旧会触发响应，除非更新的对象也是同一个引用地址的值\n\n注 ②：\n\n-   `observable` 对象树中动态添加的 `observable` 节点，会响应深度修改\n-   `observable` 对象树中静态存在的 `observable` 节点，只响应浅度修改\n\n详细见单元测试代码\n\n#### `observable` 对象检查、转换\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/externals.spec.ts\n\n**`isSupportObservable`：**\n\n判断可以作为 `observable` 对象的类型，可以作为 `observable` 对象的类型：\n\n-   `observable` 对象\n-   不在排除范围的对象：类声明对象、普通对象、`Array`、`Map`、`WeakMap`、`Set`、`WeakSet`\n\n不可以作为响应劫持对象的类型：\n\n-   `null`、`React Node`、`MomentJS` 对象、`JSON Schema`、带有 `toJS`/`toJSON` 方法的对象\n\n**`isObservable`：**\n\n判断对象是否为 `observable` 对象\n\n**`makeRaw`：**\n\n-   创建一个永远不可以作为 `observable` 的对象\n-   标记一个类，使其声明的对象永远不可作为 `observable`\n\n**`markObservable`：**\n\n-   将一个带有 `toJS` 方法的对象作为 `observable`\n-   `markObservable` 只能接受一个对象作为 `observable`，不能将函数转换为 `observable`\n\n\u003e `makeRaw` 的权重比 `markObservable` 高，无论是 `makeRaw` 包裹 `markObservable`，还是 `markObservable` 包裹 `makeRaw` 都不能够作为 `observable` 对象\n\n补充：\n\n-   递归 `observable` 并打印 `JS`\n\n#### `hasCollected`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/hasCollected.spec.ts\n\n检测某段执行逻辑是否存在依赖收集\n\n#### `untracker`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/reactive/untracked.spec.ts\n\n`untracker` 函数内部永远不会被依赖收集，和 `action` 一样，不同的是 `untracker` 不支持批量操作\n\n-   `untracker` 函数内部永远不会被依赖收集\n-   `untracker` 不提供参数什么也不会发生\n\n---- 分割线 ----\n\n### Core Library\n\n内部方法不再当前研究范围，不包含测试用例：\n\n-   `heart.spec.ts` [[查看](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/__tests__/heart.spec.ts)]\n-   `internals.spec.ts` [[查看](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/__tests__/internals.spec.ts)]\n-   `lifecycle.spec.ts` [[查看](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/__tests__/lifecycle.spec.ts)]\n\n#### Field\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/field.spec.ts\n\n普通字段\n\n-   创建字段：`createField`\n-   创建带有属性的字段\n-   字段值和展示状态：`display`、`value`\n-   嵌套展示和表单模式：`display`、`pattern`\n-   设置字段值、初始值：`setValue`、`setInitialValues`\n-   设置字段加载状态和验证状态：`setLoading`、`setValidating`\n-   设置字段组件和组件属性：`setComponent`、`setComponentProps`\n-   设置装饰组件和装饰组件的属性：`setDecorator`、`setDecoratorProps`\n-   响应式初始值：`reactions` + `field.initialValue`\n-   字段验证状态、错误、警告、成功、有效、无效、验证状态、反馈信息：`selfValidate`、`errors`、`warnings`、`successes`、`valid`、`invalid`、`validateStatus`、`queryFeedbacks`\n-   设置验证器：`setValidatorRule`\n-   字段查询：`query`\n-   初始值为空：`initialValue: \"\"`\n-   有初始值的对象字段：`initialValue: { obj: {} }`\n-   有初始值的数组对象：`initialValue: [1, 2]`\n-   重置对象字段的初始值：`form.reset`\n-   字段重置：`field.reset`\n-   匹配路径：`field.match`\n-   获取、设置字段状态：`setState`、`getState`\n-   设置字段数据源：`setDataSource`\n-   设置字段标题和介绍：`setTitle`、`setDescription`\n-   必填字段，设置必填：`required`、`setRequired`\n-   设置字段值的 data 和 content：`setData`、`setContent`\n-   设置虚拟字段的 data 和 content：`setData`、`setContent`\n-   设置字段验证状态：`setErrors`、`setWarnings`、`setSuccesss`、`setValidator`\n-   字段联动：`reactions`\n-   字段容错\n-   初始值：`initialValue`\n-   无索引数组路径计算，下标 0 创建字段 `0.input`：`[{ input: \"123\" }]`\n-   无索引嵌套虚拟节点的数组路径计算，下标 0 创建虚拟节点 `column`，可以直接忽略，直接查找虚拟节点下的 `input`：`[{ input: \"123\" }]`\n-   通过对象索引计算数组路径，下标 0 是对象节点，对象节点下的 `input`：`[{ input: \"123\" }]`\n-   通过虚拟索引计算数组路径，下标 0 作为虚拟节点，节点下包含字段：`[\"123\"]`\n-   外层包裹一个虚拟节点并通过虚拟索引计算数组路径，可忽略外最层\n-   在联动中收集依赖触发响应：`reaction` + `field.query`\n-   嵌套字段隐藏和验证：`display`、`field.validate`\n-   深度嵌套字段隐藏和验证：`display`、`form.validate`\n-   深度嵌套字段隐藏和通过中间字段隐藏自身验证状态\n-   字段卸载和验证状态：`field.onUnmount`、`field.validate`\n-   数组字段下的自动清除：`form.setValues({ array: [] }`\n-   对象字段下的自动清除：`obj1.setValue({})`，和数组字段不一样，建议看单元测试\n-   初始值为空的字段：`initialValue: \"\"|null`\n-   字段提交：`field.submit`\n-   带有错误的字段提交\n-   初始值和展示状态的关系：`initialValue`、`visible`\n-   字段受控展示状态：`reactions` + `visible`\n-   字段值和初始值受控：`reactions` + `initialValue`\n-   字段名叫 `length`（`JS` 保护名称） 的初始值\n-   字段名叫 `length`，动态分配初始值\n-   嵌套字段的修改：`field.modified`、`field.selfModified`\n-   连续验证字段:`field.setValidator([validator1, validator2])`\n-   在自定义验证器中获取上下文字段和表单：`validator(_, __, _ctx) {}`\n-   单方向联动：`reactions`\n-   修改字段路径会重新计算字段值：`field.locate`\n-   重置对象字段：`form.reset`\n-   字段展示状态决定默认值是否有效：`onFieldReact` + `field.visible`\n-   通过相邻路径查找值：`.{path}`\n-   相对路径查找虚拟节点下的字段\n-   表单值和字段值定义和覆盖：`initialValue`、`value`\n-   销毁字段同时销毁值：`field.destroy`\n-   字段校验只校验第一个非法规则：`validateFirst: true`\n-   注销字段不再响应联动：`field.destroy` + `reactions`\n-   父级设置 `readPretty` 会覆盖 `disabled`、`readOnly` 的子集 `pattern`\n-   字段验证错误，不影响其他字段\n-   字段注销后不再合并到表单值中：`field.destroy`\n-   `onInput` 通过 `target` 传值：`field.onInput({ target })`\n-   表单初始值忽略已注销的字段、隐藏的字段：`display: none`、`field.destroy`\n-   字段 `actions`、字段方法注入 `inject`、调用 `invoke`\n-   字段隐藏保留值 `display: hidden` 和不保留值 `display: none`\n-   解构字段的展示状态：`{ name: [aa,bb] }`\n-   `onInput` 修改规则：`field.onInput({ currentTarget, target });`\n-   通过聚焦和失焦对无效的目标值触发验证：`validator: [{ triggerType }]`\n\n#### ArrayField\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/array.spec.ts\n\n数组字段：\n\n-   创建数组字段：`createArrayField`\n-   数组字段方法：`push`、`pop`、`unshift`、`remove`、`insert`、`move`、`shift`、`moveDown`、`moveUp`\n-   数组字段下标操作和交换：`push`、`pop`、`unshift`、`remove`、`insert`、`move`\n-   数组下标字段移动：`move`\n-   查询数组字段下标路径：`form.query`、`form.fields`\n-   数组字段中的虚拟子节点：`createVoidField` + `basePath`\n-   交换数组字段的子集：`move`\n-   数组字段容错\n-   修改数组字段容错\n-   数组字段通过 `move` API 移动子集\n-   数组添加、删除、创建子字段触发回调：`onFormValuesChange`、`onFormInitialValuesChange`、`onFieldValueChange`\n-   嵌套字段的 `indexes`，以及删除数组下的节点的坑点：`field.indexs`\n-   数组字段的 `indexes` 需要避免无效的数字：`field.index`\n-   在数组字段中没有字段的节点：`unshift({})`\n-   数组节点中可以跳过虚拟节点，直接获取数据\n-   数组字段清空：`form.reset(\"*\")`、`form.reset(\"*\", { forceClear: true })`\n-   数组字段删除节点不会导致内存泄露：`remove` + `onFieldValueChange`\n-   数组字段修补值：`unshift`\n-   数组字段初始值通过 `remove` 删除\n-   从 `records` 中查找数组字段\n-   在数组嵌套字段中查找 `record`\n-   查找数组字段中的 `record`\n-   获取对象字段的 `record`\n-   获取表单的 `record`\n\n#### ObjectField\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/object.spec.ts\n\n对象字段：\n\n-   创建 `ObjectField` 对象：`createObjectField`\n-   `ObjectField` 对象的方法：`field.addProperty`、`field.removeProperty`、`field.existProperty`\n\n#### VoidField\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/void.spec.ts\n\n虚拟字段：\n\n-   创建虚拟节点，注销虚拟节点：`createVoidField`、`field.destroy`\n-   创建带有属性的虚拟节点\n-   设置组件和组件属性：`field.setComponent`、`field.setComponentProps`\n-   设置标题、描述：`setTitle`、`setDescription`\n-   设置包装器和包装器属性：`setDecorator`、`setDecoratorProps`\n-   设置、获取状态：`setState`、`getState`\n-   嵌套展示、字段模式：`setPattern`、`setDisplay`\n-   联动：`reactions`\n-   容错机制\n-   子集联动：`reactions` + `field.query`\n\n#### form\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/form.spec.ts\n\n调用 `createForm` 所返回的核心表单模型：\n\n-   创建 `form` 对象并挂载：`createForm`\n-   创建字段：`createField`、`createArrayField`、`createObjectField`、`createVoidField`\n-   设置值、初始值：`setValues`、`setInitialValues`\n-   没有值的字段会使用初始值合并：`initialValue`、`value`\n-   设置表单加载状态：`setLoading`\n-   允许设置值为 `null`\n-   允许设置表单值为 `observable` 对象\n-   删除表单值，初始值：`deleteValuesIn`、`deleteInitialValuesIn`\n-   表单提交、验证：`setSubmitting`、`setValidating`\n-   添加、删除、覆盖式更新副作用：`addEffects`、`removeEffects`、`setEffects`\n-   字段查询：`query`\n-   通知、订阅、取消订阅：`notify`、`subscribe`、`unsubscribe`\n-   设置和获取表单状态、字段状态：`setState`、`getState`、`setFormState`、`getFormState`、`setFieldState`、`getFieldState`\n-   表单验证：`validate`、`valid`、`invalid`、`errors`、`warnings`、`successes`、`clearErrors`、`clearWarnings`、`clearSuccess`、`queryFeedbacks`\n-   表单模式：`setPattern`、`pattern`、`editable`、`readOnly`、`disabled`、`readPretty`\n-   表单展示状态：`setDisplay`、`display`、`visible`、`hidden`\n-   表单提交：`form.submit`\n-   表单重置：`form.reset`\n-   测试表单在调试工具下的表现，检查 `__FORMILY_DEV_TOOLS_HOOK__` 对象\n-   重置数组字段：`form.reset`\n-   重置对象字段：`form.reset`\n-   创建字段前初始值合并表单值，注 ①\n-   不能匹配的字段为空值\n-   创建字段后合并初始值，和注 ① 一样，不同的是赋值的顺序\n-   删除表单中没有定义的值：`display` + `hasOwnProperty`\n-   初始值为空数组\n-   表单生命周期触发回调：`onFormInitialValuesChange`、`onFormValuesChange`\n-   修改表单中的数组字段默认值：`push` + `onFormValuesChange`\n-   深度合并值：`form.setValues`\n-   `validator` 中 `throw new Error`\n-   可重复声明表单字段并覆盖字段值：`designable: true`\n-   跳过验证 `display: none` 的字段\n-   跳过验证已卸载：`aa.onUnmount` 不能跳过，需要回收字段：`form.clearFormGraph`\n-   跳过验证不可编辑的字段：`field.editable = false`\n-   带有格式的验证命令：`validator: { format }`、`validator(value) {}`\n-   卸载表单不会影响字段值：`form.onMount`\n-   回收字段会清除字段值：`form.clearFormGraph(\"*\")`\n-   回收字段不清除字段值：`form.clearFormGraph(\"*\", false)`\n-   表单自动回收不可见的字段值：`reactions` + `visible`\n-   通过异步设置初始值，自动隐藏表单不可见的字段值：`reactions` + `visible`、`form.setInitialValues`\n-   表单值不会因为 `setValues` 改变，注 ②\n-   表单初始值不会因为 `setInitialValues` 改变，注 ②\n-   表单字段设为 `undefined` 不会报错，会被表单忽略：`form.fields['a'] = undefined;`\n\n\u003e 注 ②，在表单字段创建前不能通过 `setValue` 或 `setInitialValue` 赋值，但是可以通过表单直接赋值 `form.value = {}` 或 `form.initialValue = {}`，字段创建后不再受到限制\n\n#### effect\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/effect.spec.ts\n\n表单、字段生命周期：\n\n-   表单初始化、挂载、卸载监听：`onFormInit`、`OnFormMount`、`onFormUnmount`\n-   监听表单值改变、表单初始值改变：`onFormValueChange`、`onFormInitialValueChange`\n-   监听表单输入：`onFormInputChange`\n-   表单响应式逻辑：`onFormReact`\n-   重置表单：`onFormReset`\n-   表单提交：`onFormSubmit`\n-   表单验证：`onFormValidate`\n-   字段主动受控：`onFieldChange`\n-   字段初始化、字段挂载、字段卸载：`onFieldInit`、`onFieldMount`、`onFieldUnmout`\n-   字段初始值变更、字段值变更、字段输入值变更：`onFieldInitialValueChange`、`onFieldValueChange`、`onFieldInputValueChange`\n-   字段被动受控：`onFieldReact`\n-   字段验证：`onFieldValidate`\n-   副作用里异步监听生命周期会抛出错误\n-   `createEffectContext` 副作用上下文\n-   `form.lifecycles` 表单副作用集合\n\n\u003e 备注：无论是表单提交还是表单验证，它们都是微任务\n\n#### 检查类型\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/externals.spec.ts\n\n检查对象类型：\n\n-   检查字段：`isField`、`isArrayField`、`isObjectField`、`isVoidField`、`isDataField`、`isGeneralField`\n-   检查字段状态：`isFieldState`、`isArrayFieldState`、`isObjectFieldState`、`isVoidFieldState`、`isDataFieldState`、`isGeneralFieldState`\n-   检查表单：`isForm`、`isFormState`、`isQuery`\n\n自定义 `effect`：\n\n-   `createEffectHook`\n\n#### 表单字段模型操作\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/graph.spec.ts\n\n字段模型操作方法\n\n-   回收字段模型：`form.clearFormGraph`\n-   获取字段模型：`form.getFormGraph`\n-   设置字段模型：`form.setFormGraph`\n\n---- 分割线 ----\n\n### React Library\n\n#### `ExpressionScope`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/react/expression.spec.tsx\n\n自定义组件内部给 `json-schema` 表达式传递局部作用域：\n\n-   创建带有 `ExpressionScope` 组件的表单\n-   忽略编译 `{{variable}}`\n-   表单隐藏和可见：`form.fields[{name}].hidden`、`form.fields[{name}].visible`\n\n#### `Field`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/react/field.spec.tsx\n\n`createField` 的 `React` 实现：\n\n-   没有 `FormProvider` 上下文渲染 `Field` 会抛出错误'\n-   渲染字段：`Field`、`ArrayField`、`ObjectField`、`VoidField`\n-   字段属性基础操作：`mounted`\n-   数组字段属性：`moveDown`、`mounted`、`value`\n-   注入副作用逻辑：`observer` + `useFormEffects` + `onFieldChange`\n-   第三方组件库的无侵入接入 `Formily`：`connect` + `mapProps` + `mapReadPretty`\n-   字段验证和卸载：`onFieldUnmount`、`form.validate`\n\n不包含：\n\n-   `ReactiveField`：这个字段不对外导出，可以通过 `ArrayField` 进行了解\n\n#### `Form`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/react/form.spec.tsx\n\n表单渲染相关：\n\n-   渲染表单：`FormProvider`、`FormConsumer`\n-   查询最近的 `ObjectField` 或 `Form`：`useParentForm`\n\n#### `JsonSchema`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/react/schema.json.spec.tsx\n\n使用 `JsonSchema` 渲染：\n\n-   通过 `JsonSchema` 渲染单一字段\n-   通过 `JsonSchema` 渲染带有对象字段的表单\n-   通过 `JsonSchema` 渲染单一对象字段\n-   通过 `x-component-props` 传递 `children`\n-   通过 `x-content` 传递 `children`\n\n#### `MarkupSchema`\n\n-   目录：https://github.com/cgfeel/formily/blob/main/src/__tests__/react/schema.markup.spec.tsx\n\n使用 `MarkupSchema` 渲染节点：\n\n-   字符节点：`SchemaField.String`\n-   布尔节点：`SchemaField.Boolean`\n-   数值节点：`SchemaField.Number`\n-   日期节点：`SchemaField.Date`\n-   日期时间节点：`SchemaField.DateTime`\n-   虚拟节点：`SchemaField.Void`\n-   数组节点：`SchemaField.Array`\n-   对象节点：`SchemaField.Object`\n-   其他节点：`SchemaField.Markup`\n-   虚拟节点支持 `children props`，`SchemaField.Markup` 不支持 `children`\n-   子节点 - 通过 `props` 设置 `children`\n-   通过 `x-content` 设置 `children`\n\n`RecursionField` 递归渲染组件：\n\n-   `onlyRenderProperties`：只渲染 `schema properties`\n-   `mapProperties`：`schema properties`映射器，主要用于改写`schema`\n-   `filterProperties`：`schema properties`过滤器，被过滤掉的 `schema` 节点不会被渲染\n-   `onlyRenderSelf`：是否只渲染自身，不渲染 `properties`，注 ①\n-   引入一个无效的 `schema`\n-   `schema` 联动：`x-reactions`\n-   作用域范围：`scope`\n-   `x-content` 通过 `scope` 将组件作为 `children`\n-   作用域内隐藏和展示：`x-visible`\n-   作用域联动设置值：`x-value`\n-   通过作用域嵌套更新组件属性：`x-component-props`\n-   通过联动嵌套更新组件属性：`x-reactions`\n-   `schema` 验证和必填：`x-validator`、`required`\n-   `schema` 根据值响应：`{{$values.input}}`\n-   虚拟节点的 `children`\n-   在响应执行过程中，调用第三方字段的值\n    -   触发更新：`onClick` + `onChange`\n    -   `x-reactions` 响应：`target` + `onFieldInputValueChange`\n-   多个响应的副作用隔离\n    -   触发更新：`onClick` + `onChange`\n    -   `x-reactions` 响应：`dependencies` + `{{$deps}}`\n-   嵌套作用域记录对象：`RecordScope`\n-   作用域记录对象字面量：`{{$record + $index}}`\n-   作用域集合：`RecordsScope`\n-   `propsRecursion`：`RecursionField` 的 `proprety` 是否递归传递 `mapProperties` 和 `filterProperties`\n-   不提供 `propsRecursion` 过滤 `schema` 只能过滤下一级字段，对于更深层次的字段不能过滤\n\n\u003e 注 ①： `ObjectField` 下所有子集都是 `properties`\n\n---- 分割线 ----\n\n## 作业练习\n\n### `fishedee` 总结的 `Formily`\n\n包含有 4 篇文档：\n\n-   `Formily` 的 `Reactive` 的经验汇总 [[查看](https://blog.fishedee.com/2021/07/13/Formily%E7%9A%84Reactive%E7%9A%84%E7%BB%8F%E9%AA%8C%E6%B1%87%E6%80%BB)]\n-   `Formily` 的 `core` 的经验汇总 [[查看](https://blog.fishedee.com/2021/07/14/Formily%E7%9A%84core%E7%9A%84%E7%BB%8F%E9%AA%8C%E6%B1%87%E6%80%BB)]\n-   `Formily` 的 `React` 库经验汇总 [[查看](https://blog.fishedee.com/2021/07/21/Formily%E7%9A%84React%E5%BA%93%E7%BB%8F%E9%AA%8C%E6%B1%87%E6%80%BB)]\n-   `Formily` 的 `Antd` 经验汇总 [[查看](https://fishedee.com/2021/07/25/Formily%E7%9A%84Antd%E7%BB%8F%E9%AA%8C%E6%B1%87%E6%80%BB)]\n\n\u003e 如果是没有接触过 `Formily` 的新手，建议从上面官方文档示例开始看；对于这几篇文档提到的 `Formily` 知识点有限，且存在有错误，我会在演示中纠正部分错误；而对于已经了解 `Formily`，这几篇文档可以作为补充练习来看\n\n这里我将 4 篇文章比较精彩的部分罗列出来，其他的大多和官方演示一样，可以直接本地运行查看\n\n#### 一步步复现 `Formily`\n\n包含以下章节：\n\n-   `core`.0: 仅用 `@formily/reactive` 实现表单逻辑 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/coreReactive)]\n-   `core`.10: 为之前的 `reactive` 表单增加 `core` [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/coreForm)]\n-   `React`.3: 复现 `Field` [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/customField)]\n-   `React`.4: 复现 `Schema` [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/customSchema)]\n\n原理直接本地运行查看演示备注，除此之外还可以看看字段和模型的实践\n\n#### 字段和模型的实践\n\n-   `React`.1: `Field` 的 `React` 实现 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/reactField)]\n-   `React`.2: 字段模型 `SchemaField` 实现 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/schemaField)]\n-   `React`.6: 字段特性 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/customTable)]\n-   掘金练习：自增对象 [[查看](https://github.com/cgfeel/formily/tree/main/src/components/objectBase)]\n\n#### 其他推荐演示\n\n-   `Reactive`.5：多计数器 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/count)]\n-   `Core`.1.1：字段值的获取与更新 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/fieldAction)]\n-   `Core`.2 展示状态 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/display)]\n-   `Core`.3 校验和反馈 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/validateInput)]\n-   `React`.7.1: `value` 与 `onChange` 的隐式传递 [[查看](https://github.com/cgfeel/formily/tree/main/src/fishedee/reactFaq)]\n\n---- 分割线 ----\n\n### 掘金练习题：自增对象\n\n原文章：手把手教你写一个条件组合组件 [[查看](https://juejin.cn/post/7005869798483558431)]，将其用 `Formily` 复现\n\n目录：https://github.com/cgfeel/formily/tree/main/src/components/objectBase\n\n优点：\n\n1. 简洁，就 2 文件\n2. 一套模型，不用修改模型代码1 个字母，就可拓展 N 个逻辑渲染，如下图\n\n\u003cimg width=\"733\" alt=\"企业微信截图_bdbe233d-180d-4246-8fab-7c2efd0598f1\" src=\"https://github.com/cgfeel/formily/assets/578141/76cecadc-02e3-44d3-b203-fd8c3021cdaa\"\u003e\n\n---- 分割线 ----\n\n### 字节面试题：部门员工选择\n\n\u003cimg height=\"500\" alt=\"image\" src=\"https://github.com/user-attachments/assets/9a80ce78-58b8-4f93-8c89-78c981a4753f\" /\u003e\n\n要求做一个部门成员选择的浮窗，支持展开员工所属部门。在此要求上实现了：\n\n- 搜索筛选、最近选择记录，部门成员选择、排序、展示已选择成员、取消选择。\n\n\u003e 示例并没有采用全 `formily` 交互的方式，而是让 `formily` 与 `React` 进行交互。\n\n---- 分割线 ----\n\n## 写在最后\n\n在刷 `Formily` 的时候时常会有个疑问，`Formily` 的优势是什么，对比诸如：`react-hook-form`、`zod`，亦或者直接使用 `Antd`，我有什么非用 `Formily` 不可的地方吗？在刷完整个文档后我大致总结出这么几条：\n\n-   低代码：`Formily` 天生为低代码生的\n-   赋予 `Schema` 模型处理的能力\n-   `DMS` 数据管理服务\n\n几乎市面上所有的表单验证库也好，还是相关的库也好，它们都是围绕：验证、受控展开，但是很少会提到：模型渲染、数据分离，这应该就是 `Formily` 特有的地方了，它不仅仅是一个表单验证、受控的库。借助模型处理的能力，可以无限拓展、分离、组合不同的 `schema`，实现、管理一个极其庞大的数据。\n\n当然带来的问题就是学习成本的上升，要了解 `Formily`，就要了解一套状态管理 `Reactive`、了解副作用及声明周期和相关对象 `Core`、了解一套现有的渲染引擎库 `React`，以及对应的一套主题，如：`Antd`\n\n那什么时候选择使用 `Formily` 呢？\n\n-   看使用场景，比如做低代码，诸如：问卷调查、任务引擎等\n-   看业务处理\n    -   如果你仅仅需要处理注册、登录、商品购买提交订单，`Formily` 可能并非最优解\n    -   如果你需要做比较大的数据处理可以考虑 `Formily`，例如：商城平台订单管理，分大类目、小类目、不同 SKU，库存，价格，优惠策略等等\n    -   亦或者你需要做一个 CRM 可以考虑 `Formily`，管理不同的客户群体，付费情况、不同链路渠道划分、统计等等\n    -   一个复杂的动态结果页，比如你有一个上千模型的落地页，根据不同的情况，按照不同的状态、不同的顺序分别展示\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcgfeel%2Fformily","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcgfeel%2Fformily","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcgfeel%2Fformily/lists"}