# 《低代码引擎物料协议规范》
# 1 介绍
## 1.1 本协议规范涉及的问题域
- 定义本协议版本号规范
- 定义本协议中每个子规范需要被支持的 Level
- 定义中后台物料目录规范(A)
- 定义中后台物料 API 规范(A)
- 定义中后台物料入库规范(A)
- 定义中后台物料国际化多语言支持规范(AA)
- 定义中后台物料主题配置规范(AAA)
- 定义中后台物料无障碍访问规范(AAA)
## 1.2 协议草案起草人
- 撰写:九神、大果、元彦、戊子、林熠、屹凡、金禅
- 审阅:潕量、月飞、康为、力皓、荣彬、暁仙、度城、金禅、戊子、林熠、絮黎
## 1.3 版本号
1.0.0
## 1.4 协议版本号规范(A)
本协议采用语义版本号,版本号格式为 `major.minor.patch` 的形式。
- major 是大版本号:用于发布不向下兼容的协议格式修改
- minor 是小版本号:用于发布向下兼容的协议功能新增
- patch 是补丁号:用于发布向下兼容的协议问题修正
## 1.5 协议中子规范 Level 定义
| 规范等级 | 实现要求 |
| -------- | ---------------------------------------------------------------------------------- |
| A | 强制规范,必须实现;违反此类规范的协议描述数据将无法写入物料中心,不支持流通。 |
| AA | 推荐规范,推荐实现;遵守此类规范有助于业务未来的扩展性和跨团队合作研发效率的提升。 |
| AAA | 参考规范,根据业务场景实际诉求实现;是集团层面鼓励的技术实现引导。 |
## 1.6 名词术语
- **物料**:能够被沉淀下来直接使用的前端能力,一般表现为业务组件、区块、模板。
- **业务组件(Business Component)**:业务领域内基于基础组件之上定义的组件,可能会包含特定业务域的交互或者是业务数据,对外仅暴露可配置的属性,且必须发布到公域(如阿里 NPM);在同一个业务域内可以流通,但不需要确保可以跨业务域复用。
- **低代码业务组件(Low-Code Business Component)**:通过低代码编辑器搭建而来,有别于源码开发的业务组件,属于业务组件中的一种类型,遵循业务组件的定义;同时低代码业务组件还可以通过低代码编辑器继续多次编辑。
- **区块(Block)**:通过低代码搭建的方式,将一系列业务组件、布局组件进行嵌套组合而成,不对外提供可配置的属性。可通过区块容器组件的包裹,实现区块内部具备有完整的样式、事件、生命周期管理、状态管理、数据流转机制。能独立存在和运行,可通过复制 schema 实现跨页面、跨应用的快速复用,保障功能和数据的正常。
- **模板(Template)**:特定垂直业务领域内的业务组件、区块可组合为单个页面,或者是再配合路由组合为多个页面集,统称为模板。
## 1.7 物料规范背景
目前集团业务融合频繁,而物料规范的不统一给业务融合带来额外的高成本,另一方面集团各个 BU 的前端物料也存在不同程度的重复建设。我们期望通过集团层面的物料通不阻碍业务融合的发展,同时通过集团层面的物料流通来提升物料丰富度,通过丰富物料的复用来提效中后台系统研发,同时也能给新业务场景提供高质量的启动物料。
## 1.8 物料规范定义
- **源码物料规范**:一套面向开发者的目录规范,用于规范化约束开发过程中的代码、文档、接口规范,以方便物料在集团内的流通。
- **搭建物料规范**:一套面向开发者的 Schema 规范,用于规范化约束开发过程中的代码、文档、接口规范,以方便物料在集团内的流通。
# 2. 物料规范 - 业务组件规范
## 2.1 源码规范
### 2.1.1 目录规范(A)
```
component // 组件名称, 比如 biz-button
├── build // 【编译生成】【必选】
│ └── index.html // 【编译生成】【必选】可直接预览文件
├── lib // 【编译生成】【必选】
│ ├── index.js // 【编译生成】【必选】js 入口文件
│ ├── index.scss // 【编译生成】【必选】css 入口文件
│ └── style.js // 【编译生成】【必选】js 版本 css 入口文件,方便去重
├── demo // 【必选】组件文档目录,可以有多个 md 文件
│ └── basic.md // 【必选】组件文档示例,用于生成组件开发预览,以及生成组件文档
├── src // 【必选】组件源码
│ ├── index.js // 【必选】组件出口文件
│ └── index.scss // 【必选】仅包含组件自身样式的源码文件
├── README.md // 【必选】组件说明及 API
└── package.json // 【必选】组件 package.json
```
#### README.md
- README.md 应该包含业务组件的源信息、使用说明以及 API,示例如下:
```
# 按钮 // 这一行是标题
按钮用于开始一个即时操作。 // 这一行是描述
{这段通过工程能力自动注入, 开发者无需编写
## 安装方法
npm install @alifd/ice-layout -S
}
## API
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| ---- | ---- | ------ | ------------------- | ------ |
| type | 类型 | String | `primary`、`normal` | `normal` |
```
- README.en-US.md(文件命名采取 [bcp47 规范](http://www.rfc-editor.org/rfc/bcp/bcp47.txt))多语言的情况,可选
```
# Button
Button use to trigger an action.
{这段通过工程能力自动注入, 开发者无需编写
## Install
npm install @alifd/ice-layout -S
}
## API
| Param | Description | Type | Enum | Default |
| ----- | ----------- | ------ | ------------------- | ------- |
| type | type | String | `primray`、`normal` | normal |
```
#### package.json
`package.json` 中包含了一些依赖信息和配置信息,示例如下:
```json
{
"name": "@alife/1688-button",
"description": "业务组件描述",
"version": "0.0.1",
"main": "lib/index.js",
"stylePath": "lib/style.js", // 【私有字段】样式文件地址,webpack 插件引用
"files": [
"demo/",
"lib/",
"build/" // 存放编译后的 demo,发布前应该编译生成该目录
],
"dependencies": {
"@alifd/next": "1.x" // 【可选】可以是一个 util 类型的组件,如果依赖 next,请务必写语义化版本号,不要写*这种
},
"devDependencies": {
"react": "^16.5.0",
"react-dom": "^16.5.0"
},
"peerDependencies": {
"react": "^16.5.0"
},
"componentConfig": { // 【私有字段】组件配置信息
"name": "button", // 组件英文名
"title": "按钮", // 组件中文名
"category": "form" // 组件分类
}
}
```
#### src/index.js
包含组件的出口文件,示例如下:
```javascript
import Button from './Button.jsx';
import ButtonGroup from './ButtonGroup.jsx';
export const Group = ButtonGroup; // 子组件推荐写法
export default Button;
```
推荐用法
```javascript
import Button, { Group } form '@scope/button';
```
#### src/index.scss
```css
/* 不引入依赖组件的样式,比如组件 import { Button } from '@alifd/next'; */
/* 不需要在 index.scss 中引入 @import '~@alifd/next/lib/button/index.scss'; */
/* 如果需要引入主题变量引入此段 */
@import '~@alifd/next/variables.scss';
/* 组件自身样式 */
.custom-component {
color: $color-brand1-1;
}
```
#### demo
demo 目录存放的是组件的文档,无文档的业务组件无法带来任何价值,因此 demo 是必选项。demo 目录下的文件采取 markdown 的写法,可以是多个文件,示例(demo/basic.md)如下:
demo/basic.md
~~~
---
title: {按钮类型}
order: {文档的排序,数字,0 最小,从小到大排序}
---
按钮有三种视觉层次:主按钮、次按钮、普通按钮。不同的类型可以用来区别按钮的重要程度。
:::lang=en-US
---
title: Container
order: 3
---
Change the default container by passing a function to `container`;
enable `useAbsolute` to use `absolute position` to implement affix component;
:::
```jsx // 以下建议用英文编写
import Button from '@alife/1688-button';
ReactDOM.render(
, mountNode);
```
```css
.test {
background: #CCC;
}
```
~~~
### 2.1.2 API 规范(A)
API 是组件的属性解释,给开发者作为组件属性配置的参考。为了保持 API 的一致性,我们制定这个 API 命名规范。对于业界通用的,约定俗成的命名,我们遵循社区的约定。对于业界有多种规则难以确定的,我们确定其中一种,大家共同遵守。
#### 通用规则
- 所有的 API 采用小驼峰的书写规则,如 `onChange`、`direction`、`defaultVisible`。
- 标签名采用大驼峰书写规则,如 `Menu`、`Slider`、`DatePicker`。
#### 通用命名
| API 名称 | 类型 | 描述 | 常见变量 |
| :------------- | :------------- | :----------------------------------------------------------- | :---------------------------------------------------- |
| shape | string | 形状,从组件的外形来看有区别的时候,使用 shape | |
| direction | enum | 方向,取值采用缩写的方式。 | hoz(水平), ver(垂直) |
| align | enum | 对齐方式 | tl, tc, tr, cl, cc, cr, bl, bc, br |
| status | enum | 状态 | normal, success, error, warning |
| size | enum | 大小 | small, medium, large 更大或更小可用(xxs, xs, xl, xxl) |
| type | enum or string | 分类:1. dom 结构不变、只有皮肤的变化 2.组件类型只有并列的几类 | normal, primary, secondary |
| visible | boolean | 是否显示 | |
| defaultVisible | boolean | 是否显示(非受控) | |
| disabled | boolean | 禁用组件 | |
| closable | bool/string | 允许关闭的方式 | |
| htmlType | string | 当原生组件与 Fusion 组件的 type 产生冲突时,原生组件使用 `htmlType` | |
| link | string | 链接 | |
| dataSource | array | 列表数据源 | [{label, value}, {label, value}] |
| has+'属性' | boolean | 拥有某个属性 | 例如 `hasArrow`, `hasHeader`, `hasClose` 等等 |
#### 多选枚举
当某个 API 的接口,允许用户指定多个枚举值的时候,我们把这个接口定义为多选枚举。一个很典型的例子是某个弹层组件的 `closable` 属性,我们会允许:键盘 esc 按键、点击 mask、点击 close 按钮、点击组件以外的任何区域进行关闭。
不要有一个 API 值,支持多种类型。例如某个弹层的组件,我们会允许 esc、点击 mask、点击 close 按钮等进行关闭。此时 API 设计可以通过多个 API 承载,例如:
```js
closable?: boolean; // 默认为 true
closeMode?: CM[] | string; // 默认值是 ['close', 'mask', 'esc']
```
true 表示触发规则都会关闭,false 表示触发规则不会关闭。
示例:
- ``,所有合法条件都会关闭
- ``,任何情况下都不关闭,只能通过受控设置 visible
- ``,用户按 esc 或者点击关闭按钮会关闭
#### 事件
- 标准事件或者自定义的符合 w3c 标准的事件,命名必须 on 开头, 即 `on` + 事件名,如 onExpand。
#### 表单规范
- 支持[受控模式](https://reactjs.org/docs/forms.html#controlled-components)(value + onChange) (A)
- value 控制组件数据展现
- onChange 组件发生变化时候的回调函数(第一个参数可以给到 value)
- `value={undefined}` 的时候清空数据, field 的 reset 函数会给所有组件下发 undefined 数据 (AA)
- 一次完整操作抛一次 onChange 事件 `建议` 比如有 Process 表示进展中的状态,建议增加 API `onProcess`;如果有 Start 表示启动状态,建议增加 API `onStart` (AA)
#### 属性的传递
**1. 原子组件(Atomic Component)**
> 最小粒子,不能再拆分的组件
举例:Input/Button/NumberPicker
期望使用起来像普通的 html 标签一样,能够把用户传入的参数,透传到真正的节点上。
```jsx
```
渲染后的 dom 结构:
```jsx
```
**2. 复合组件(Composite component)**
复合组件一般由两个及以上的原子组件/复合组件构成,比如:Select 由 Inupt + 弹窗组成,Search 由 Select + Button 组成,TreeSelect 由 Tree + Select 组成。
为了提高组件使用的便利性,对 API 属性的要求如下:
1. 复合组件核心的原子组件(比如 Search 的核心原子组件是 Input)的属性以及使用频率高的属性建议扁平化,让复合组件可以直接使用其属性;
2. 复合组件内的非核心原子组件,则通过 `xxxProps` (如 inputProps/btnProps)的方式,将参数传递到相应原子组件上。
**属性扁平化例子**:
比如 `Search` 组件由 `Input` 和 `Button` 构成,但是 `Search` 更像是 `Input` ,因此把 `Input` 作为主要组件,将属性扁平化。即在 `Search` 组件上直接使用一些 `Input` 的属性。 ``
比如 `Select` `TreeSelect` 都有弹层部分,`Overlay` `Overlay.Popup` 的 `visible` 属性使用率较高,一般用于 fixed 布局下的弹窗滚动跟随。因此把该属性暴露到最外层,简化使用 `