纲要
碰到的题目场景及处置惩罚方案对照
我们现在采纳的是antd + react(umi)的框架做营业开辟。在营业开辟历程当中会有较多频仍涌现而且相似度很高的场景,比方基于一个table的基础的增编削查,这个置信人人都非常熟习。在接到一个新的营业需求的时刻,置信有不少人会挑选copy一份功用相似的代码然后基于这份代码去革新以满足当前营业,固然我现在也是如许做的~
实在想把这块功用提取成一个大众组建的主意由来已久,近来最先做基础组件,便拿这个动手了。经由一周摆布的时候完成了基础组件的编写。
检察基础支撑的
功用点API。
基础的思绪是经由过程json天生一些笼统设置,然后经由过程剖析json的笼统设置+衬着器终究天生页面。json设置涵盖了现在80%的营业场景的基础需求,然则可扩展性很低。比方一些庞杂的营业场景:表单的关联校验
、数据关联显现
、多级列表下钻
等等功用。虽然经由过程一些较为庞杂的处置惩罚能够把这些功用融入进来,但终究组件将会非常巨大难以保护。
所以,我能不能经由过程这些json设置经由过程某种东西天生对应的代码?如许一来以上提到的题目就完整不存在了,由于这和我们本身写的代码完整一样,东西只是帮我们完成初始化的历程。所以厥后想了许多要领,最初采纳template string
的体式格局,这类体式格局较为简朴粗犷,不过经由过程string中嵌套变量的推断来输出code。然则在现实写的时刻发明许多题目,比方
-
function
的输出(JSON.stringify会将function疏忽) - 多层函数嵌套今后怎样猎取终究衬着的节点code
- 嵌入变量怎样完成、umi-models-effects/reducer中分外的字典查询怎样天生等等..
终究进修了一些天生代码的东西比方angular-cli以及一些关于js天生代码的文章,主假如经由过程知乎上的这篇议论相识到了人人是怎样处置惩罚这类题目的。终究决议采纳babel的生态链来处置惩罚上述碰到的题目。
我们现在采纳的体式格局是基于antd+react(umi)编写通用的CRUD模板,然后经由过程代码天生器剖析json中的设置天生对应的代码,大抵的流程是:
React –> JavaScript AST —> Code Generator –> Compiler –> Page
现在功用只是完成了开端版本,待应用在项目中运用一段时候稳固今后将会开源~
什么是babel?
Babel是一个东西链,重要用于编译ECMAScript 2015+代码转换为向后兼容的可运行在种种浏览器上的JavaScript。重要功用:
- 语法转换
- 环境中缺乏的Polyfill功用
- 源代码转换
- 检察更多Babel功用
Understanding ASTs by Building Your Own Babel Plugin
如上供应了babel基础的流程及一篇引见AST的文章。
我的明白中比方一段string范例code,起首经由过程babel.transform会将code转为一个包括AST(Abstract Syntax Tree)的Object,一样能够运用@babel/generator将AST转为code完成逆向历程。
比方一段变量声明代码:
const a = 1;
在剖析今后的构造为:
{
"type": "Program",
"start": 0,
"end": 191,
"body": [
{
"type": "VariableDeclaration",
"start": 179,
"end": 191,
"declarations": [
{
"type": "VariableDeclarator",
"start": 185,
"end": 190,
"id": {
"type": "Identifier",
"start": 185,
"end": 186,
"name": "a"
},
"init": {
"type": "Literal",
"start": 189,
"end": 190,
"value": 1,
"raw": "1"
}
}
],
"kind": "const"
}
],
"sourceType": "module"
}
起首范例为VariableDeclaration
,起首他的范例是const
,能够经由过程点击检察api别的另有let
、var
的值。其次是声明declarations
部份,这里值为数组,由于我们能够同时定义多个变量。数组中值的范例为VariableDeclarator
,包括id
和init
两个参数,离别为变量称号以及变量值。id
的范例为Identifier
,译为修饰符等于变量称号。init
范例为Literal
,等于常量,平常经常使用的有stringLiteral
、numericliteral
、booleanliteral
等。此时即完成了变量赋值的历程。
固然这只是很简朴的语法转换,假如人人想进修更多关于转换及范例的学问,可参考以下两个官方链接:
处置惩罚历程
起首定义目次构造:
.
├── genCode // 代码天生器
| ├── genDetail // 须要新页面翻开时零丁的detail目次
| └── genIndex // 首页
| └── genModels // umi models
| └── genServices // umi services
| └── genTableFilter // table挑选地区
| └── genTableForm // 非新页面形式,新增/更新模态框
| └── genUpsert // 新页面形式下,新增/更新页面
| └── genUtils // 天生东西类
├── schema // 模子定义文件
| ├── table // 当前要天生的模子
| └── ├──config.js // 基础设置
| └── └──dataSchema.js // 列表、新增、更新设置
| └── └──querySchema.js // 挑选项设置
├── scripts // 天生剧本
| ├── generateCode.js // 天生主文件
| └── index.js // 进口
| └── utils.js // 东西类
├── toCopyFiles // 天生时须要拷贝的文件,比方less
└── index.js // 主进口
主体流程为:
- 指定要天生代码的途径。
- 依据schema中当前json设置途径,顺次挪用genCode目次中各个模块的代码天生要领猎取对应code。
- 在指定的途径下写入对应的文件。
- 实行
eslint ${filePath} --fix
花样化天生的代码。 - 依据设置对应复制toCopyFiles文件夹中依靠的less等文件到对应的文件夹。
个中重要模块为genCode文件夹中依据json设置天生代码的历程。
以genModels为例,起首提取能够运用template string
完成的部份,削减代码剖析的工作量。
module.exports = (tableConfig) => {
return `
import { message } from 'antd';
import { routerRedux } from 'dva/router'
import { parse } from 'qs'
${dynamicImport(dicArray, namespace)}
export default {
namespace: '${namespace}',
state: {
...
},
effects: {
*fetch({ payload }, { call, put }) {
const response = yield call(queryData, payload);
if (response && response.errorCode === 0) {
yield put({
type: 'save',
payload: response.data,
});
} else {
message.error(response && response.errorMessage || '请求失利')
}
},
...,
${dynamicYieldFunction(dicArray)}
},
reducers: {
save(state, action) {
return {
...state,
data: action.payload,
};
},
...,
${dynamicReducerFunction(dicArray)}
},
};
`
}
由于列表数据可能有字典项从背景猎取值来对应显现,所以import
、effects
、reducers
模块均有需依据设置动态天生的代码。
以dynamecImport为例:
function dynamicImport (dicArray, namespace) {
// 基础api import
let baseImport = [
'queryData', 'removeData', 'addData', 'updateData', 'findById'
]
// 推断json数据中是不是有需从背景加载项
if (dicArray && dicArray.length) {
baseImport = baseImport.concat(dicArray.map(key => getInjectVariableKey(key)))
}
// 遍历天生依靠项
const _importDeclarationArray = map(specifier => (
_importDeclarationArray.push(t.importSpecifier(t.identifier(specifier), t.identifier(specifier)))
))
// 定义importDeclaration
const ast = t.importDeclaration(
_importDeclarationArray,
t.stringLiteral(`../services/${namespace}`)
)
// 经由过程@babel/generator 将ast天生code
const { code } = generate(ast)
return code
}
别的代码天生逻辑相似,有不确定怎样天生的部份可参考上方供应的链接完成代码转换再去天生。
如有经由过程babel转换没法天生的代码,可经由过程正则来完成。
比方以下umi-models
代码:
*__dicData({ payload }, { call, put }) {
const response = yield call(__dicData, payload);
if (response && response.errorCode === 0) {
yield put({
type: 'updateDic',
payload: response.data,
});
} else {
message.error(response && response.errorMessage || '请求失利')
}
}
基础代码可经由过程yieldExpression
天生,然则转换今后无function今后的*
标记,重复差了文档今后没有处置惩罚要领,末了只能将天生完的code应用正则替换来处置惩罚。
假如人人有碰到相似的题目迎接议论~
题目
- 现在运用的编辑器组件为braft-editor,然则连系antd运用initialValue不见效,必需运用setFieldsValue。然则运用useEffects时会默许增加props.form作为依靠而且props.form会不停变化而触发死循环,现在无法只要禁用eslint react-hooks/exhaustive-deps。
useEffect(() => {
props.form.setFieldsValue({
editorArea: BraftEditor.createEditorState(current.editorArea),
editorArea2: BraftEditor.createEditorState(current.editorArea2)
});
}, [current.editorArea, current.editorArea2]);
- 天生的代码怎样删除未运用的依靠?运用eslint –fix不会删除未运用的变量定义。
- 初始化今后的代码要修正怎样办?因当前要领只会完成代码初始化历程,今后修正的历程暂无思绪处置惩罚。
功用API
参数范例参考react-antd-admin
功用设置包括三个基础设置文件:
-
config.json
设置基础属性 -
dataSchema.json
设置列表及新增修正字段 -
querySchema.json
设置挑选地区字段
config.json
设置列表
参数 | 必填 | 范例 | 默许值 | 申明 |
---|---|---|---|---|
namespace | true | string | null | 定名空间 |
showExport | false | boolean | true | 是不是显现导出 |
showCreate | false | boolean | true | 是不是显现建立 |
showDetail | false | boolean | true | 是不是显现检察 |
showUpdate | false | boolean | true | 是不是显现修正 |
showDelete | false | boolean | true | 是不是显现删除 |
newRouterMode | false | boolean | false | 在新的页面新增/编辑/检察概况。若包括富文本编辑器,发起此值设为true,富文本在模态框展现不是非常雅观。 |
showBatchDelete | false | boolean | true | 是不是显现批量删除,需multiSelection为 true |
multiSelection | false | boolean | true | 是不是支撑多选 |
defaultDateFormat | false | string | ‘YYYY-MM-DD’ | 日期花样 |
upload | false | object | null | 上传相干设置,上传图片和上传一般文件离别设置。 详见下方upload属性 |
pagination | false | object | null | 分页相干设置, 详见下方pagination属性 |
dictionary | false | array | null | 须要请求的字典项,用于下拉框或treeSelect的值为从后端猎取的状况,可在dataSchema 和querySchema中运用, 详见下方dictionary属性 |
upload
参数 | 必填 | 范例 | 默许值 | 申明 |
---|---|---|---|---|
uploadUrl | false | string | null | 默许的上传接口.优先级image/fileApiUrl > uploadUrl > Global.apiPath |
imageApiUrl | false | string | null | 默许的图片上传接口 |
fileApiUrl | false | string | null | 默许的文件上传接口 |
image | false | string | ‘/uploadImage’ | 默许的上传图片接口 |
imageSizeLimit | false | number | 1500 | 默许的图片大小限定, 单元KB |
file | false | string | ‘/uploadFile’ | 默许的上传文件接口 |
fileSizeLimit | false | number | 10240 | 默许的文件大小限定, 单元KB |
pagination
参数 | 必填 | 范例 | 默许值 | 申明 |
---|---|---|---|---|
pageSize | false | number | 10 | 每页显现数目 |
showSizeChanger | false | boolean | false | 是不是能够转变pageSize |
pageSizeOptions | false | array | [’10’, ’20’, ’50’, ‘100’] | 指定每页能够显现若干条 |
showQuickJumper | false | boolean | false | 是不是能够疾速跳转至某页 |
showTotal | false | boolean | true | 是不是显现总数 |
dictionary
参数 | 必填 | 范例 | 默许值 | 申明 |
---|---|---|---|---|
key | true | string | null | 变量标识 |
url | true | string | null | 请求数据地点 |
dataSchema.json
设置列表
参数 | 必填 | 范例 | 默许值 | 申明 |
---|---|---|---|---|
key | true | string | null | 唯一标识符 |
title | true | string | null | 显现称号 |
primary | false | boolean | false | 主键 假如不指定主键, 不能update/delete, 但能够insert; 假如指定了主键, insert/update时不能填写主键的值; |
showType | false | string | input | 显现范例 input/textarea/inputNumber/datePicker/rangePicker/radio/select/checkbox/multiSelect/image/file/cascader/editor |
disabled | false | boolean | false | 表单中这一列是不是制止编辑 |
addonBefore | false | string/ReactNode | null | showType 为input能够设置前标签 |
addonAfter | false | string/ReactNode | null | showType 为input能够设置后标签 |
placeholder | false | string | null | 默许提醒笔墨 |
format | false | string | null | 日期范例的花样 |
showInTable | false | boolean | true | 这一列是不是要在table中展现 |
showInForm | false | boolean | true | 是不是在新增或编辑的表单中显现 |
validator | false | boolean | null | 设置校验划定规矩, 参考https://github.com/yiminghe/a… |
width | false | string/number | null | 列宽度 |
options | false | array | null | format:[{ key: ”, value: ” }]或string。showType为cascader时,此字段暂不支撑Array,数据只能经由过程异步猎取。 |
min | false | number | null | 数字输入的最小值 |
max | false | number | null | 数字输入的最大值 |
accept | false | string | null | 上传文件花样限定 |
sizeLimit | false | number | 20480 | 上传文件花样限定 |
url | false | string | null | 上传图片url。图片的上传接口, 能够针对每一个上传组件零丁设置, 假如不零丁设置就运用config.js中的默许值;假如这个url是http开首的, 就直接运用这个接口; 不然会依据config.js中的设置推断是不是加上host |
sorter | false | boolean | false | 是不是排序 |
actions | false | array | null | 操纵 |
actions
参数 | 必填 | 范例 | 默许值 | 申明 |
---|---|---|---|---|
keys | false | array | null | 许可更新哪些字段, 假如不设置keys, 就许可更一切字段 |
name | true | string | null | 展现题目 |
type | false | string | null | update/delete/newLine/component |
querySchema.json
设置列表
参数 | 必填 | 范例 | 默许值 | 申明 |
---|---|---|---|---|
key | true | string | null | 唯一标识符 |
title | true | string | null | 显现称号 |
placeholder | false | string | null | 提醒语 |
showType | false | string | input | 显现范例, 一些可罗列的字段, 比方type, 能够被显现为单选框或下拉框 input, 就是一个一般的输入框, 这时候能够省略showType字段 现在可用的showType: input/inputNumber/datePicker/rangePicker/select/radio/checkbox/multiSelect/cascader |
addonBefore | false | string/ReactNode | null | showType 为input能够设置前标签 |
addonAfter | false | string/ReactNode | null | showType 为input能够设置后标签 |
defaultValue | false | string/array/number | null | 多选的defaultValue是个数组 |
min | false | number | null | showType为 inputNumber 时可设置最小值 |
max | false | number | null | showType为 inputNumber 时可设置最大值 |
options | false | array | null | options的key请求必需是string, 不然会有warning normal-format: [{“key”: “”, “value”: “”}] cascader-format: [{“value”: “”, “label”: “”, children: [“value”: “”, “label”: “”, children: []]}] 假如值为string,代表异步猎取的数据,则猎取当前定名空间下该key对应的值 |
defaultValueBegin | false | string | null | showType为 rangePicker 时可设置默许最先值 |
defaultValueEnd | false | string | null | showType为 rangePicker 时可设置默许完毕值 |
placeholderBegin | false | string | 最先日期 | showType为 rangePicker 时可设置默许最先提醒语 |
placeholderEnd | false | string | 完毕日期 | showType为 rangePicker 时可设置默许完毕提醒语 |
format | false | string | null | 日期挑选花样 |
showInSimpleMode | false | boolean | false | 在简朴查询体式格局下展现,若数据中有一项包括此字段且为true的值,则开启简朴/庞杂挑选切换 |