formily思维导图

总入口(which)
import {
observable,
Tracker,
batch,
define,
action,
toJS,
autorun,
} from "@formily/reactive";
import {
FormPath,
each,
isFn,
isValid,
isUndef,
isEmpty,
isPlainObj,
isNumberLike,
clone,
toArr,
} from "@formily/shared";
import {createForm} from "@/components/my-formily/core";
import {observer} from "@/components/my-formily/reactive-react";
import {FormItem, Input, Submit} from "@/components/my-formily/antd";
import {
FormProvider,
Field,
FormConsumer,
FieldContext,
useParentForm,
} from "@/components/my-formily/react";
export {
createForm,
FormProvider,
Field,
FormConsumer,
FieldContext,
useParentForm,
observable,
Tracker,
batch,
define,
action,
toJS,
autorun,
observer,
FormItem,
Input,
Submit,
FormPath,
each,
isFn,
isValid,
isUndef,
isEmpty,
isPlainObj,
isNumberLike,
clone,
toArr,
};
@formily/antd
antd入口
import FormItem from "./FormItem";
import Input from "./Input";
import Submit from "./Submit";
export {FormItem, Input, Submit};
formItem组件的实现
import { observer, FieldContext } from "@/which";
import { useContext } from "react";
const FormItem = observer(({ children }) => {
const field = useContext(FieldContext);
return (
<div>
<div>{field.title}</div>
{children}
<div className="red">{field.selfErrors.join(",")}</div>
</div>
);
});
export default FormItem;
Input组件实现
const Input = (props) => {
return (
<input
{...props}
value={props.value || ""}
style={{...props.style, border: "solid 1px green"}}
/>
)
}
export default Input
Submit组件实现
import {useParentForm} from "@/which";
const Submit = ({
children,
onSubmit,
onSubmitSuccess,
onSubmitFailed,
onClick,
}) => {
const form = useParentForm();
return (
<button
onClick={(e) => {
if (onClick) {
if (onClick(e) === false) {
return;
}
}
if (onSubmit) {
form
.submit(onSubmit)
.then(onSubmitSuccess)
.catch(onSubmitFailed);
}
}}>
{children}
</button>
);
};
export default Submit;
- 可以看到FormItem组件和Input组件传入的参数有所区别,这主要是由@formily/react里的Field组件在渲染的时候控制的,在下面的实现中可以重点关注它们之间的联系
- FormItem和Field组件之间存在父子之间的通信,但是具体跨多少层级不确定,所以用到了react里的Context来实现跨层级之间的通信(使用了@formily/react对外暴露的FiledContext)
- 可以思考一下Submit里的form是如何实现的
@formily/react
入口
import FormProvider from "./FormProvider";
import Field from "./Field";
import FormConsumer from "./FormConsumer";
import {FieldContext} from "./context";
import {useParentForm} from "./hooks";
export {FormProvider, Field, FormConsumer, FieldContext, useParentForm};
FormProvider组件的实现
import { useEffect } from "react";
import { FormContext } from "./context";
export default function FormProvider({ form, children }) {
useEffect(() => {
form.onMount();
return () => {
form.onUnmount();
};
}, []);
return <FormContext.Provider value={form}>{children}</FormContext.Provider>;
}
FormConsumer组件的实现
import {observer} from "@/which";
import {useContext} from "react";
import {FormContext} from "./context";
const FormConsumer = observer((props) => {
const form = useContext(FormContext);
const children = props.children(form);
return children;
});
export default FormConsumer;
hooks(自定义hook)
import {useContext} from "react";
import {FormContext} from "./context";
export function useParentForm() {
const form = useContext(FormContext);
return form;
}
context
import React from "react"
export const FormContext = React.createContext()
export const FieldContext = React.createContext()
Field组件的实现
import React, { useContext } from "react"
import { observer, FieldContext } from "@/which"
import { FormContext } from "./context"
// 组件层的Field
const Field = observer((props) => {
const form = useContext(FormContext)
const field = form.createField(props)
const component = React.createElement(field.component[0], {
...field.component[1],
value: field.value || "",
onChange: field.onInput,
})
const decorator = React.createElement(
field.decorator[0],
field.decorator[1],
component
)
return (
<FieldContext.Provider value={field}>{decorator}</FieldContext.Provider>
)
})
export default Field
- 这部分的实现相对来说也较简单,重点是创建了两个context(FormContext和FieldContext),重点关注它们的作用
- 可以看到Fild组件的field来源于form.createField,那咱们的关注点又来到了@formily/core里,这是在core对外暴露来获取数据的方法
@formily/core
入口
import createForm from "./createForm";
export {createForm};
createForm
import Form from "./Form";
const createForm = (options) => {
return new Form(options);
};
export default createForm;
Form
import Field from "./Field"
import {define, observable} from "@/which"
import {batchSubmit, batchValidate} from "./internals"
export default class Form {
constructor(props) {
this.initialize(props)
this.makeObservable()
}
initialize = (props) => {
this.props = {...props}
this.fields = {}
this.initialValues = props.initialValues
// 所有field的value
this.values = {...props.initialValues}
this.errors = []
}
makeObservable = () => {
define(this, {
fields: observable.shallow,
values: observable,
})
}
createField = (props) => {
const {name} = props
if (!this.fields[name]) {
new Field(name, props, this)
}
return this.fields[name]
}
onMount = () => {}
onUnmount = () => {}
validate = () => {
return batchValidate(this)
}
submit = (onSubmit) => {
return batchSubmit(this, onSubmit)
}
}
数据层Field
import {define, observable} from "@/which"
import {createReactions, validateSelf} from "./internals"
// 数据层的Field
export default class Field {
constructor(name, props, form) {
this.name = name
this.props = {...props}
this.form = form
this.form.fields[name] = this
this.component = props.component
this.decorator = props.decorator
this.selfErrors = []
this.value = this.form.values[name]
this.query = {required: props.required}
this.makeObservable()
this.makeReactive()
}
makeObservable = () => {
define(this, {
value: observable,
selfErrors: observable,
})
}
makeReactive = () => {
createReactions(this)
}
onInput = (e) => {
const newValue = e.target.value
this.value = newValue
this.form.values[this.props.name] = newValue
// todo 校验
validateSelf(this)
}
}
internal
import {autorun, batch, toJS} from "@/which"
export const validateSelf = (field) => {
let value = field.value
if (typeof value == "string") {
value = value.trim()
}
const query = field.query
if (query.required && (value == "" || value == undefined)) {
field.selfErrors = ["请输入必填项"]
}
}
export const createReactions = (field) => {
const reactions = field.props.reactions
if (typeof reactions === "function") {
autorun(
batch.scope.bound(() => {
reactions(field)
})
)
}
}
export const batchValidate = async (target) => {
target.errors = []
let i = 0
for (const key in target.fields) {
const field = target.fields[key]
validateSelf(field)
if (field.selfErrors[0]) {
target.errors.push({key, msg: field.selfErrors[0]})
}
}
if (target.errors.length > 0) {
throw target.errors
}
}
export const batchSubmit = async (target, onSubmit) => {
await batchValidate(target)
const res = onSubmit(toJS(target.values))
return res
}
- 这里重点关注数据层Form和Field之间的联系
- createField实际上也就是获取数据层Field里的所有数据
响应式reactive-react
入口
import observer from "./observer";
export { observer };
observer
import { memo } from "react";
import { useObserver } from "./hooks";
export default function observer(component) {
const WrappedComponent = (props) => {
return useObserver(() => component({ ...props }));
};
const memoComponent = memo(WrappedComponent);
return memoComponent;
}
hooks(自定义hook)
import { useReducer, useEffect, useRef } from "react";
import { Tracker } from "@/which";
export function useObserver(view) {
const [, forceUpdate] = useReducer((x) => x + 1, 1);
const trackerRef = useRef(null);
if (!trackerRef.current) {
trackerRef.current = new Tracker(() => {
forceUpdate();
});
}
useEffect(() => {
return () => {
if (trackerRef.current) {
trackerRef.current.dispose();
trackerRef.current = null;
}
};
}, []);
return trackerRef.current.track(view);
}