2022-03-16

formilyjs

formilyjs V2

概念性问题

MVVM(Model–view–viewmodel) 模型

MVVM(Model–view–viewmodel)是一种 OOP 软件架构模式, 它的核心是将我们的应用程序的逻辑与视图做分离, 提升代码可维护性与应用健壮性; 我们可以用一张图来描述:

解释一下就是, View(视图层)负责维护 UI 结构与样式, 同时负责与 ViewModel(视图模型)做数据绑定, 这里的数据绑定关系是双向的, 也就是, ViewModel(视图模型)的数据发生变化, 会触发 View(视图层)的更新, 同时视图层的数据变化又会触发 ViewModel(视图模型)的变化; Model 则更偏实际业务数据处理模型; ViewModel 和 Model 都是充血模型, 两者都注入了不同领域的业务逻辑, 比如 ViewModel 的业务逻辑更偏视图交互层的领域逻辑, 而 Model 的业务逻辑则更偏业务数据的处理逻辑;

MVVM 模型

那么, Formily 解决方案在 MVVM 中应该是什么样的定位呢?

很明显, Formily 它提供了 View 和 ViewModel 两层能力,View 则是@formily/react @formily/vue, 专门用来与@formily/core 做桥接通讯的, 所以,@formily/core 的定位就是 ViewModel 层

@formily/core 文档 @formily/vue 文档

各核心关系/概念

表单模型

core 表单模型

@formily/core作为 ViewModel 可以理解为(表单的core), 负责管理表单’状态’, 管理表单’字段’ 与后台交互’Model层’参考其API就能更好的理解了

@formily/vue作为 View, Components 的FormProvider描述, 可理解为’表单’的Vue UI实现, 核心桥接 表单上下文 用于下发表单上下文给字段组件, 负责整个表单状态的通讯, 它相当于是一个通讯枢纽;

‘表单模型’的关键API 通过 createField/createArrayField/createObjectField/createVoidField 方法来创建字段, 如果字段已经存在, 则不会重复创建 通过 query 方法来查询字段, query 方法可以传入字段的路径或者正则表达式来匹配字段;

字段模型

core 字段模型

@formily/vue作为 View, Components 的Field from vue, 可理解为’表单字段’的Vue UI实现

‘表单字段模型’的关键API

  • Field 模型, 主要负责管理非自增型字段状态, 比如 Input/Select/NumberPicker/DatePicker 这些组件;
  • ArrayField 模型, 主要负责管理自增列表字段状态, 可以对列表项进行增删移动的;
  • ObjectField 模型, 主要负责管理自增对象字段状态, 可以对对象的 key 做增删操作;
  • VoidField 模型, 主要负责管理虚字段状态, 虚字段是一种不会污染表单数据的节点存在, 但是它可以控制它的子节点显示隐藏, 交互模式;

ArrayField 和 ObjectField 都是继承自 Field, Field 的定位就是维护非自增型数据字段, 对比 ArrayField/Object,并不是说 Field 就不能存数组类型或者对象类型的数据, Field 其实可以存任意数据类型的数据, 只是, 如果用户期望实现数组的添加删除移动这样的交互, 则需要使用 ArrayField, 对象属性的添加删除交互, 则需要使用 ObjectField, 如果没有这样的需求, 所有数据类型统一用 Field 即可;

数据源规则 考虑到字段的值来源不是只有通过 Input 输入框输入的, 还有会从一个数据源中选取的, 比如下拉框之类的, 所以字段模型加了一个数据源的属性 dataSource, 专门用于读取数据源; 只是在组件消费端需要做一层映射; 写入数据源的方式可以直接修改 dataSource 属性, 也可以调用 setDataSource 方法

组件规则 字段模型, 如果没有代理 UI 组件信息, 那就没法实现更加精细化的联动控制了, 比如 A 字段的值变化要控制 B 字段的 placeholder, 那就必须将字段的属性给代理起来, 所以 formily 提供了 component 属性, 专门用于代理 UI 组件信息, component 是一个数组[Component,ComponentProps], 第一个元素代表是哪个组件, 第二个代表组件的属性有哪些, 为什么用数组, 主要原因是这样方便类型提示, 同时写法也比较简单;

View & ViewModel 的几层绑定关系

核心概念

@formily/core 就是 ViewModel, (字段模型中的属性)Component 和 Decorator 就是 View, @formily/vue 就是将 ViewModel 和 View 绑定起来的胶水层 ViewModel 和 View 的绑定就叫做模型绑定, 实现模型绑定的手段主要有useField, 也能使用connect和mapProps, 需要注意的是, Component 只需要支持 value/onChange 属性即可自动实现数据层的双向绑定;

前面讲了模型绑定, 而协议绑定则是将 Schema 协议转换成模型绑定的过程, 因为 JSON-Schema 协议是 JSON 字符串, 可离线存储的, 而模型绑定则是内存间的绑定关系,是 Runtime 层的, 比如x-component在 Schema 中是组件的字符串标识, 但是在模型中的 component 则是需要组件引用, 所以 JSON 字符串与 Runtime 层是需要转换的; 然后我们就可以继续完善一下以上模型绑定的图:

主要有 2 层绑定关系,Schema 绑定模型(JSON Schema 绑定表单/字段模型),模型绑定组件(表单/字段模绑定UI实现), 实现绑定的胶水层就是 @formily/vue, 需要注意的是, Schema 绑定字段模型之后, 字段模型中是感知不到 Schema 的, 比如要修改enum, 就是修改字段模型中的dataSource属性了

帮助理解 - 协议绑定图

协议驱动: formily 的协议驱动主要是基于标准 JSON Schema 来进行驱动渲染的 协议绑定: JSON Schema 绑定到 ViewModel

几种开发模式

JSON Schema 该模式主要是使用 Field/ArrayField/ObjectField/VoidField 组件 Template 该模式是给 SchemaField 的 schema 属性传递 JSON Schema Markup Schema 主要依赖 SchemaStringField/SchemaArrayField/SchemaObjectField…这类描述标签来表达 Schema SchemaField 子节点不能随意插 UI 元素, 因为 SchemaField 只会解析子节点的所有 Schema 描述标签, 然后转换成 JSON Schema

formilyjs + vue2 element-ui 实例!

项目依赖

npm install --save @formily/core # 核心
npm install --save @formily/vue  # UI桥接库
 
npm install --save element-ui #UI实现
npm install --save @vue/composition-api @formily/element #UI实现 

formilyjs quick-start formilyjs element

入口

全局使用 ElementUI 即可 main.js

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false
/* eslint-disable no-new */
Vue.use(ElementUI)
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

错误

scss 问题

万年的 *.scss 编译错误!

npm install sass-loader@7.x --save-dev # 特定版本
npm install sass --save-dev # 用 sass, 不能用 node-sass,  参考: https://github.com/alibaba/formily/discussions/1438

我* 官网文档也有说

因为 Element UI 是基于 Sass 构建的, 如果你用 Webpack 配置请使用以下两个 Sass 工具

“sass”: “^1.32.11”, “sass-loader”: “^8.0.2”

designable 项目

开源了通用表单设计器 react + ts 开发的

Lerna 是一个优化使用 git 和 npm 管理多包存储库的工作流工具, 用于管理具有多个包的 JavaScript 项目;
使用了lerna管理项目, 先执行 npm run bootstrap 处理下

//基本的设计器架子 npm run start:basic 源码在/examples/basic

表单设计器开发指南

问题

Cannot find module ‘mini-css-extract-plugin’ or its corresponding type declarations

npm install —save-dev mini-css-extract-plugin