看了 @formily/reactive、@formily/vue、之后,了解到一些具体的hooks用法,以及渲染模式。 主要分两种 1:JSON SCHEMA 2:markup schema 【仅代表自己理解,希望大神给予指导】
这个是@formily/vue 的一个渲染结构图
从这里我们可以很清晰的看到最上层的组件FormProvider 作为统一上下文来使用。 然后我们通过SchemaField 进行包裹 ,通过Markup Schema或 Json Schema开发的方式将组件上文得到的schema传递到下层组件【Field、ArrayField、ObjectField、VoidField】,并且结合@Formily/core为我们提供的工具函数Hooks
designable左侧表单Input渲染举例
- 在playground/src/app.vue 中我们可以看到 sources.Inputs 做为数据源,传递到下层组件取进行一个渲染,其中 title 作为组件的一个标题,sources 中传递的就是我们的 components
<CompositePanelItem title="panels.Component" icon="Component">
<ResourceWidget title="sources.Inputs" :sources="sources.Inputs" />
<ResourceWidget
title="sources.Layouts"
:sources="sources.Layouts"
/>
</CompositePanelItem>
// return {
// engine,
// sources: {
// Inputs: [
// Input,
// ]
//},
// }
- 接下来我们看一下packages/prototypes/src/widgets/ResourceWidget这个组件在接收到Input组件时候是如何渲染的进行
props: {
defaultExpand: { type: Boolean, default: true },
sources: { type: Array, default: () => [] },
className: String,
title: String,
},
- 因为文件是tsx方式,里面有interface类型定义, 我们可以很明确的知道,其实这个组件默认接收这几个参数,目前我们上文中已经得到了sources,可以看到这个组件中又个主渲染回调函数是renderNode
/**
* 渲染展示主要用来
* @param source 组件数据
*
* node.id 唯一标识 TreeNode
* icon
* elements 组件信息
* @returns
*/
const renderNode = (source: IResource) => {
const prefix = unref(prefixRef)
const { node, icon, title, thumb, span } = source
return (
<div
class={prefix + '-item'}
style={{ gridColumnStart: `span ${span || 1}` }}
{...{ key: node?.id, 'data-designer-source-id': node?.id }}
>
{thumb && <img class={prefix + '-item-thumb'} src={thumb} />}
{/* icon ==>> 判断是不是svg */}
{icon && isVNode(icon) ?
<>{icon}</>
: (
<IconWidget
class={prefix + '-item-icon'}
infer={icon}
style={{ width: '150px', height: '40px' }}
/>
)}
{/* 标题 */}
<span class={prefix + '-item-text'}>
{title || <NodeTitleWidget node={node}></NodeTitleWidget>}
</span>
</div>
)
}
// 进行数据整合Resource数据结构!reduce 求和方法; 统一拍平,变成一维
const sources = props.sources.reduce<IResource[]>((buf, source) => {
//判断是否 Resource[], 取出里面的数据结构
if (isResourceList(source)) {
return buf.concat(source)
} else if (isResourceHost(source)) {
return buf.concat(source.Resource)
}
return buf
}, [])
<div class={prefix + '-content-wrapper'}>
<div class={prefix + '-content'}>
{sources.map(isFn(slots.default) ? slots.default : renderNode)}
{remainItems ? (
<div
class={prefix + '-item-remain'}
style={{ gridColumnStart: `span ${3 - remainItems}` }}
></div>
) : null}
</div>
</div>
看完组件如何渲染后,我们再看一下input组件是如何定义的
Input 例子
路径:packages/renderer/src/components/Input 主要目录: preview
- 在这里我们看到其中有两个函数createBehavior && createResource, 其中 createBehavior主要是为节点绑定对应的行为操作,createResource 创建数据节点,其中selector中的x-component要与下面props中的x-component 相互对应
//
export const Input: DnFC<any> =
composeExport(FormilyInput, {
Behavior: createBehavior(
{
name: 'Input',
extends: ['Field'], // 匹配
selector: (node) =>{
// console.log('寻找符合标签配置')
return node.props?.['x-component'] === 'Input'
},
designerProps: {
propsSchema: createFieldSchema(AllSchemas.Input),
},
designerLocales: AllLocales.Input,
},
// {
// name: 'Input.TextArea',
// extends: ['Field'],
// selector: (node) => node.props?.['x-component'] === 'Input.TextArea',
// designerProps: {
// propsSchema: createFieldSchema(AllSchemas.Input.TextArea),
// },
// designerLocales: merge(AllLocales.Input, {
// 'zh-CN': {
// title: '多行输入',
// },
// 'en-US': {
// title: 'TextArea',
// },
// }),
// }
),
Resource: createResource(
{
icon: 'InputSource',
elements: [
{
componentName: 'Field',
props: {
type: 'string',
title: 'Input',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
],
},
// {
// icon: 'TextAreaSource',
// elements: [
// {
// componentName: 'Field',
// props: {
// type: 'string',
// title: 'TextArea',
// 'x-decorator': 'FormItem',
// 'x-component': 'Input.TextArea',
// },
// },
// ],
// }
),
})
- propsSchema 中是通过Schema的方式指定了之后我们会用到的属性
createFieldSchema、根据现有的一份Schema数据就可以实现一个左侧组件渲染
// 预制schema配置 【组件属性】
/**
*
* @param component
* @param decorator
* @returns
*
* 取默认 AllSchemas 中的配置
* component-group 组件属性
* decorator-group 容易属性
* component-style-group 组件样式
* decorator-style-group 容器样式
*/
export const createComponentSchema = (component: ISchema, decorator: ISchema) => {
return {
'component-group': component && {
type: 'void',
'x-component': 'CollapseItem',
'x-reactions': {
fulfill: {
state: {
visible: '{{!!$form.values["x-component"]}}'
}
}
},
properties: {
'x-component-props': component
}
},
'decorator-group': decorator && {
type: 'void',
'x-component': 'CollapseItem',
'x-component-props': { defaultExpand: false },
'x-reactions': {
fulfill: {
state: {
visible: '{{!!$form.values["x-decorator"]}}'
}
}
},
properties: {
'x-decorator-props': decorator
}
},
'component-style-group': { // 组件样式
type: 'void',
'x-component': 'CollapseItem',
'x-component-props': { defaultExpand: false },
'x-reactions': {
fulfill: {
state: {
visible: '{{!!$form.values["x-component"]}}'
}
}
},
properties: {
'x-component-props.style': AllSchemas.CSSStyle
}
},
'decorator-style-group': { // 容器样式
type: 'void',
'x-component': 'CollapseItem',
'x-component-props': { defaultExpand: false },
'x-reactions': {
fulfill: {
state: {
visible: '{{!!$form.values["x-decorator"]}}'
}
}
},
properties: {
'x-decorator-props.style': AllSchemas.CSSStyle
}
}
}
}
// 预制一份schema配置 【字段属性】
export const createFieldSchema = (component?: ISchema, decorator: ISchema = AllSchemas.FormItem): ISchema => {
return {
type: 'object',
properties: {
'field-group': {
type: 'void',
'x-component': 'CollapseItem',
properties: {
name: { // 字段标识
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true
}
},
title: { // 标题
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true
}
},
description: { // 描述
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input.TextArea',
'x-component-props': {
rows: 1
}
},
'x-display': { // 展示状态
default: 'visible',
type: 'string',
enum: ['visible', 'hidden', 'none', ''],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {}
},
'x-pattern': { // ui状态
default: 'editable',
type: 'string',
enum: ['editable', 'disabled', 'readOnly', 'readPretty', ''],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {}
},
default: { // 默认值
'x-decorator': 'FormItem',
'x-component': 'ValueInput',
'x-value': '这是默认值'
},
enum: { // 可选项
'x-decorator': 'FormItem',
'x-component': DataSourceSetter,
},
'x-reactions': { //响应器规则
'x-decorator': 'FormItem',
'x-component': ReactionsSetter,
},
'x-validator': { // 校验规则
type: 'array',
'x-component': ValidatorSetter
},
required: { // 必填
type: 'boolean',
'x-decorator': 'FormItem',
'x-component': 'Switch'
}
}
},
...createComponentSchema(component, decorator)
}
}
}