学习formily总结
- 领域概念
- 路径追踪系统
- 组件桥接
- 组件设计思想
在Formily中作者提供了Reactive领域模型, 我们可以很方便的在消费数据的时候进行依赖追踪,通过依赖追踪响应器去跟踪Field的变化,然后Formily/core 让我们将ui与逻辑进行解耦,将领域模型可以独立使用,通用性变得更强
Formily设计器踩坑记录
其实引起各种问题出现还是因为自己对Formily以及设计器不够熟悉
问题记录
- 设计器render区域禁用问题
- 设计FormItem装饰器问题之前理解为在移动端中只使用Fied组件进行包装,以及处理事件传递,组件值传递就可以,但是发现与setting设置无法同步
- 基础组件与render渲染不同步问题
解决方案
- 通过为组件添加# pointer-events 解决【CSS 属性指定在什么情况下 (如果有) 某个特定的图形元素可以成为鼠标事件的】
- 通过使用接收designabled中的props属性,并且不使用ui组件,使用div结合designabled拿到的props数据绘制,并公布事件机制,修改modelValue
- 在render中通过选择器选择时的配置 一定要与 代理组件同步
Formily-vant设计器设计方案
目标
vant的ui组件可以与设计器完美结合,并且渲染流畅
渲染流程图绘制
功能点
vant-render
- 渲染组件开发
- schema配置
- Locales配置
- Field 出口组件
- VantForm 上文样式处理
vant-components
- 样式统一,使用Field或者Cell进行包裹 可采用useField()、useDesigner()拿到props信息作处理,本组件所有接收props同步
- 为组件绑定统一前缀 stylePrefix 类名,为render组件提供统一样式处理
- 采用connect结合mapProps无入侵式桥接props
- 组件不可重名
playground
作为设计器的可视化窗口,采用vue3项目搭建
- 组件库引入
- componentsTreeNode 渲染
- ResourcrWidget
- settingform
通用核心代码
FromItem
主要构成就是FormBaseItem,由renderLabel与renderContent组成
//renderLabel
const renderLabel =
label &&
h(
'div',
{
class: {
[`${prefixCls}-label`]: true,
[`${prefixCls}-label-tooltip`]:
(tooltip && tooltipLayout === 'text') || overflow.value,
[`${prefixCls}-item-col-${labelCol}`]: enableCol && !!labelCol,
},
style: labelStyle,
},
{
default: () => [
// label content
renderLabelText(),
// label tooltip
renderTooltipIcon(),
// label colon
label &&
h(
'span',
{
class: `${prefixCls}-colon`,
},
{ default: () => [colon ? ':' : ''] }
),
],
}
)
// renderContent
const renderContent = h(
'div',
{
class: {
[`${prefixCls}-control`]: true,
[`${prefixCls}-item-col-${wrapperCol}`]: enableCol && !!wrapperCol,
},
},
{
default: () => [
h(
'div',
{ class: `${prefixCls}-control-content` },
{
default: () => [
addonBefore &&
h(
'div',
{ class: `${prefixCls}-addon-before` },
{
default: () => [resolveComponent(addonBefore)],
}
),
h(
'div',
{
class: {
[`${prefixCls}-control-content-component`]: true,
[`${prefixCls}-control-content-component-has-feedback-icon`]:
!!feedbackIcon,
},
style: wrapperStyle,
},
{
default: () => [
formatChildren,
feedbackIcon &&
h(
'div',
{ class: `${prefixCls}-feedback-icon` },
{
default: () => [
typeof feedbackIcon === 'string'
? h('i', { class: feedbackIcon }, {})
: resolveComponent(feedbackIcon),
],
}
),
],
}
),
addonAfter &&
h(
'div',
{ class: `${prefixCls}-addon-after` },
{
default: () => [resolveComponent(addonAfter)],
}
),
],
}
),
renderFeedback,
renderExtra,
],
}
)
Field
Field作为render中每个组件都会用到的组件,它承担了统一去处理JsonSchema的作用以及中英文转换处理,接收一份Schema数据,然后通过hooks拿到设计器模型字段值,components字段值,以及node节点,通过路径系统进行与viewModel与输入控件做绑定,toDesignableFieldProps 得到最终fieldProps
const toDesignableFieldProps = (
schema: ISchema,
components: any,
nodeIdAttrName: string,
id: string
) => {
const results: any = {}
each(SchemaStateMap, (fieldKey, schemaKey) => {
const value = schema[schemaKey]
if (isExpression(value)) {
if (!NeedShownExpression[schemaKey]) return
if (value) {
results[fieldKey] = value
return
}
} else if (value) {
results[fieldKey] = filterExpression(value)
}
})
if (!components['FormItem']) {
components['FormItem'] = FormItem
}
const decorator =
schema['x-decorator'] && FormPath.getIn(components, schema['x-decorator'])
const component =
schema['x-component'] && FormPath.getIn(components, schema['x-component'])
const decoratorProps = schema['x-decorator-props'] || {}
const componentProps = schema['x-component-props'] || {}
if (decorator) {
results.decorator = [decorator, toJS(decoratorProps)]
}
if (component) {
// 有的是functional 有的是 正常的 vueComponent
results.component = [
isFn(component)
? component
: Object.assign({}, component, { Behavior: null, Resource: null }),
toJS(componentProps),
]
}
if (decorator) {
FormPath.setIn(results['decorator'][1], nodeIdAttrName, id)
} else if (component) {
FormPath.setIn(results['component'][1], nodeIdAttrName, id)
}
// vue为异步渲染需要进行缓存 不然就变成了函数
const title = results.title
const description = results.description
results.title =
title && (() => <span data-content-editable="title">{title}</span>)
results.description = description && (
<span data-content-editable="description">{results.description}</span>
)
return results
}
//
const FieldComponent = observer(
defineComponent({
name: 'DnField',
inheritAttrs: false,
setup(props: ISchema, { slots, attrs }) {
const designerRef = useDesigner()
const componentsRef = useComponents()
const nodeRef = useTreeNode()
props = attrs as ISchema
return () => {
if (!nodeRef.value) return null
const fieldProps = toDesignableFieldProps(
props,
componentsRef.value,
designerRef.value.props.nodeIdAttrName!,
nodeRef.value.id
)
if (props.type === 'object') {
return (
<Container>
<ObjectField
name={nodeRef.value.id}
{...fieldProps}
v-slots={slots}
/>
</Container>
)
} else if (props.type === 'array') {
return (
<ArrayField
{...fieldProps}
v-slots={slots}
name={nodeRef.value.id}
/>
)
} else if (nodeRef.value.props?.type === 'void') {
return (
<VoidField
{...fieldProps}
v-slots={slots}
name={nodeRef.value.id}
/>
)
}
return <InternalField {...fieldProps} name={nodeRef.value.id} />
}
},
})
)
成果展示
目前还为不完整版