babel在提拔前端效力的实践

纲要

  1. 碰到的题目场景及处置惩罚方案对照
  2. 什么是babel?
  3. 处置惩罚历程
  4. 现在遗留的题目
  5. 现在完成功用API
  6. 参考

碰到的题目场景及处置惩罚方案对照

我们现在采纳的是antd + react(umi)的框架做营业开辟。在营业开辟历程当中会有较多频仍涌现而且相似度很高的场景,比方基于一个table的基础的增编削查,这个置信人人都非常熟习。在接到一个新的营业需求的时刻,置信有不少人会挑选copy一份功用相似的代码然后基于这份代码去革新以满足当前营业,固然我现在也是如许做的~

实在想把这块功用提取成一个大众组建的主意由来已久,近来最先做基础组件,便拿这个动手了。经由一周摆布的时候完成了基础组件的编写。

检察基础支撑的
功用点API

基础的思绪是经由过程json天生一些笼统设置,然后经由过程剖析json的笼统设置+衬着器终究天生页面。json设置涵盖了现在80%的营业场景的基础需求,然则可扩展性很低。比方一些庞杂的营业场景:表单的关联校验数据关联显现多级列表下钻等等功用。虽然经由过程一些较为庞杂的处置惩罚能够把这些功用融入进来,但终究组件将会非常巨大难以保护。

所以,我能不能经由过程这些json设置经由过程某种东西天生对应的代码?如许一来以上提到的题目就完整不存在了,由于这和我们本身写的代码完整一样,东西只是帮我们完成初始化的历程。所以厥后想了许多要领,最初采纳template string的体式格局,这类体式格局较为简朴粗犷,不过经由过程string中嵌套变量的推断来输出code。然则在现实写的时刻发明许多题目,比方

  1. function的输出(JSON.stringify会将function疏忽)
  2. 多层函数嵌套今后怎样猎取终究衬着的节点code
  3. 嵌入变量怎样完成、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。重要功用:

  1. 语法转换
  2. 环境中缺乏的Polyfill功用
  3. 源代码转换
  4. 检察更多Babel功用

《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别的另有letvar的值。其次是声明declarations部份,这里值为数组,由于我们能够同时定义多个变量。数组中值的范例为VariableDeclarator,包括idinit两个参数,离别为变量称号以及变量值。id的范例为Identifier,译为修饰符等于变量称号。init范例为Literal,等于常量,平常经常使用的有stringLiteralnumericliteralbooleanliteral等。此时即完成了变量赋值的历程。

固然这只是很简朴的语法转换,假如人人想进修更多关于转换及范例的学问,可参考以下两个官方链接:

处置惩罚历程

起首定义目次构造:

.
├── 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               // 主进口

主体流程为:

  1. 指定要天生代码的途径。
  2. 依据schema中当前json设置途径,顺次挪用genCode目次中各个模块的代码天生要领猎取对应code。
  3. 在指定的途径下写入对应的文件。
  4. 实行eslint ${filePath} --fix花样化天生的代码。
  5. 依据设置对应复制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)}
            },
        };
    `
}

由于列表数据可能有字典项从背景猎取值来对应显现,所以importeffectsreducers模块均有需依据设置动态天生的代码。
以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应用正则替换来处置惩罚。
假如人人有碰到相似的题目迎接议论~

题目

  1. 现在运用的编辑器组件为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]);
  1. 天生的代码怎样删除未运用的依靠?运用eslint –fix不会删除未运用的变量定义。
  2. 初始化今后的代码要修正怎样办?因当前要领只会完成代码初始化历程,今后修正的历程暂无思绪处置惩罚。

功用API

参数范例参考react-antd-admin
功用设置包括三个基础设置文件:

config.json

设置列表

参数必填范例默许值申明
namespacetruestringnull定名空间
showExportfalsebooleantrue是不是显现导出
showCreatefalsebooleantrue是不是显现建立
showDetailfalsebooleantrue是不是显现检察
showUpdatefalsebooleantrue是不是显现修正
showDeletefalsebooleantrue是不是显现删除
newRouterModefalsebooleanfalse在新的页面新增/编辑/检察概况。若包括富文本编辑器,发起此值设为true,富文本在模态框展现不是非常雅观。
showBatchDeletefalsebooleantrue是不是显现批量删除,需multiSelection为 true
multiSelectionfalsebooleantrue是不是支撑多选
defaultDateFormatfalsestring‘YYYY-MM-DD’日期花样
uploadfalseobjectnull上传相干设置,上传图片和上传一般文件离别设置。 详见下方upload属性
paginationfalseobjectnull分页相干设置, 详见下方pagination属性
dictionaryfalsearraynull须要请求的字典项,用于下拉框或treeSelect的值为从后端猎取的状况,可在dataSchema 和querySchema中运用, 详见下方dictionary属性

upload

参数必填范例默许值申明
uploadUrlfalsestringnull默许的上传接口.优先级image/fileApiUrl > uploadUrl > Global.apiPath
imageApiUrlfalsestringnull默许的图片上传接口
fileApiUrlfalsestringnull默许的文件上传接口
imagefalsestring‘/uploadImage’默许的上传图片接口
imageSizeLimitfalsenumber1500默许的图片大小限定, 单元KB
filefalsestring‘/uploadFile’默许的上传文件接口
fileSizeLimitfalsenumber10240默许的文件大小限定, 单元KB

pagination

参数必填范例默许值申明
pageSizefalsenumber10每页显现数目
showSizeChangerfalsebooleanfalse是不是能够转变pageSize
pageSizeOptionsfalsearray[’10’, ’20’, ’50’, ‘100’]指定每页能够显现若干条
showQuickJumperfalsebooleanfalse是不是能够疾速跳转至某页
showTotalfalsebooleantrue是不是显现总数

dictionary

参数必填范例默许值申明
keytruestringnull变量标识
urltruestringnull请求数据地点

dataSchema.json

设置列表

参数必填范例默许值申明
keytruestringnull唯一标识符
titletruestringnull显现称号
primaryfalsebooleanfalse主键 假如不指定主键, 不能update/delete, 但能够insert;
假如指定了主键, insert/update时不能填写主键的值;
showTypefalsestringinput显现范例
input/textarea/inputNumber/datePicker/rangePicker/radio/select/checkbox/multiSelect/image/file/cascader/editor
disabledfalsebooleanfalse表单中这一列是不是制止编辑
addonBeforefalsestring/ReactNodenullshowType 为input能够设置前标签
addonAfterfalsestring/ReactNodenullshowType 为input能够设置后标签
placeholderfalsestringnull默许提醒笔墨
formatfalsestringnull日期范例的花样
showInTablefalsebooleantrue这一列是不是要在table中展现
showInFormfalsebooleantrue是不是在新增或编辑的表单中显现
validatorfalsebooleannull设置校验划定规矩, 参考https://github.com/yiminghe/a…
widthfalsestring/numbernull列宽度
optionsfalsearraynullformat:[{ key: ”, value: ” }]或string。showType为cascader时,此字段暂不支撑Array,数据只能经由过程异步猎取。
minfalsenumbernull数字输入的最小值
maxfalsenumbernull数字输入的最大值
acceptfalsestringnull上传文件花样限定
sizeLimitfalsenumber20480上传文件花样限定
urlfalsestringnull上传图片url。图片的上传接口, 能够针对每一个上传组件零丁设置, 假如不零丁设置就运用config.js中的默许值;假如这个url是http开首的, 就直接运用这个接口; 不然会依据config.js中的设置推断是不是加上host
sorterfalsebooleanfalse是不是排序
actionsfalsearraynull操纵

actions

参数必填范例默许值申明
keysfalsearraynull许可更新哪些字段, 假如不设置keys, 就许可更一切字段
nametruestringnull展现题目
typefalsestringnullupdate/delete/newLine/component

querySchema.json

设置列表

参数必填范例默许值申明
keytruestringnull唯一标识符
titletruestringnull显现称号
placeholderfalsestringnull提醒语
showTypefalsestringinput显现范例, 一些可罗列的字段, 比方type, 能够被显现为单选框或下拉框
input, 就是一个一般的输入框, 这时候能够省略showType字段
现在可用的showType: input/inputNumber/datePicker/rangePicker/select/radio/checkbox/multiSelect/cascader
addonBeforefalsestring/ReactNodenullshowType 为input能够设置前标签
addonAfterfalsestring/ReactNodenullshowType 为input能够设置后标签
defaultValuefalsestring/array/numbernull多选的defaultValue是个数组
minfalsenumbernullshowType为 inputNumber 时可设置最小值
maxfalsenumbernullshowType为 inputNumber 时可设置最大值
optionsfalsearraynulloptions的key请求必需是string, 不然会有warning
normal-format: [{“key”: “”, “value”: “”}]
cascader-format: [{“value”: “”, “label”: “”, children: [“value”: “”, “label”: “”, children: []]}]
假如值为string,代表异步猎取的数据,则猎取当前定名空间下该key对应的值
defaultValueBeginfalsestringnullshowType为 rangePicker 时可设置默许最先值
defaultValueEndfalsestringnullshowType为 rangePicker 时可设置默许完毕值
placeholderBeginfalsestring最先日期showType为 rangePicker 时可设置默许最先提醒语
placeholderEndfalsestring完毕日期showType为 rangePicker 时可设置默许完毕提醒语
formatfalsestringnull日期挑选花样
showInSimpleModefalsebooleanfalse在简朴查询体式格局下展现,若数据中有一项包括此字段且为true的值,则开启简朴/庞杂挑选切换

参考

    原文作者:原来就是你
    原文地址: https://segmentfault.com/a/1190000019240810
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞