Watch Print
Author: hu@yifeishu.com Last modified: 0000-00-00 00:00 Helpful: 1 Visits: 488

被动客制化服务转为主动搭建服务

前言

从前端角度看,大量客户定制化需求带来的是非常繁重的交付任务,需要前端开发者消耗大量精力处理重复性高、技术难度低的工作内容,同时也会给企业带来更高的开发成本、更低的人效。

前端开发者如何从定制业务中脱身,可以参考市场上已有的行业化解决方案,即低代码平台(Low Code)和无代码平台(No Code)。它的能力使前端开发者的角色由“页面的生产者”转换为“组件的生产者”。

阿里宜搭低代码平台

https://www.aliwork.com/

可视化组件拖拽搭建页面:

配置页面级数据源,数据驱动UI更新:

编辑页面JS Code,组件联动处理:

阿里已开源一套成熟的低代码框架:https://lowcode-engine.cn/

有赞移动端商城无代码搭建平台

可视化组件拖拽搭建页面:

https://www.youzan.com/

Low CodeNo Code的差异

低代码工具使用群体面向开发者角色,业务覆盖人群是初中级开发者,工作流程上极大降低重复性工作,因为基础单元是通用组件,所以最终产出页面风格统一,代码容易维护。

无代码平台使用群体面向运营角色,业务覆盖人群是所有普通用户,对比低代码工具,客制化能力弱,但是学习成本低。

搭建平台中的物料系统(Material Design

https://material.io/

不管是低代码还是无代码,交付客户的需求拆分为基础单元,需要一个物料系统承载,所谓“物料”即聚合的通用组件+客制化组件。

1. 元组件

如“按钮、输入框、文本、标题、下拉框、价格、营销标签”等最细粒度的组件,特点是极少需要更新、无法继续拆分

2. 基础组件

PC端低代码搭建如“文本、图片、链接、布局、表单容器、表格、卡片、地图、富文本框”等。

移动端无代码搭建如“一行一列商品、一行两列商品、优惠券、视频、轮播图、搜索栏、信息明细”等。

特点是基于元组件开发,具备交互能力,提供可配置能力到开发者/运营人员

3. 业务组件

即客户定制组件,针对每个客户的需求作开发,产出组件投放到物料市场。搭建平台手动添加对应的业务组件使用。特点是开发由一个组件、到一个仓库、到一个NPM包(CDN)的模式管理

编辑态到运行态的中间层 - Schema

最终产出物(JSON-Schema)数据,类似于一种DSL(领域特定语言)。即仅适用于当前系统的语言描述。

// 无代码 Schema
{
  version: '1',
  layout: {
    compfloat: ["component_id", ...],      // 浮动排版的组件,霸屏或悬浮的组件
    compflow: ["component_id", ...],       // 流式排版的组件,即正常从上到下
    components: {
      component_id: {
        type: "Carousel",        // 对应组件名
        props: {
          height: 100,
          ...
        },
        pageid: "10001",                    // 页面id
        pagename: "页面名称",                // 页面名称
        theme: {                            // 动态主题
          color-primary: "#333",
          border-radius: 30,
          ...
        }
      },
      ...
    }
  }
}

如果是低代码Schema,因为涉及组件版本、平台解释器版本、页面数据源、自定义Javascript代码,其Schema模型比无代码模型复杂度更高,技术上更值得探索。

// 低代码 Schema
{
  formUuid: 'FORM-NT766881QD7XWV5A3GDZB5RFHKOK2P3GD9SYKD1',
  title: '查询表格',
  version: 1,
  schemaVersion: 'V5',
  pages: [
    {
      formType: 'display',
      flowData: {
        operatePermission: {
          OPERATE_BATCH_PRINT: 'y',
          OPERATE_RESTART: 'y',
          OPERATE_PRINT: 'y',
          OPERATE_BATCH_CREATE: 'y',
          OPERATE_HISTORY: 'y',
          OPERATE_CREATE: 'y',
          OPERATE_BATCH_EXPORT: 'y',
          OPERATE_DING_GROUP: 'n',
          OPERATE_TERMINATE: 'y',
          OPERATE_EDIT: 'y',
          OPERATE_BATCH_IMPORT: 'y',
          OPERATE_STASH: 'y',
          OPERATE_VIEW: 'y',
          OPERATE_DELETE: 'y',
          OPERATE_BATCH_DELETE: 'y',
          OPERATE_COMMENT: 'y'
        },
        behaviors: [
          {
            fieldBehavior: 'NORMAL',
            hidden: false,
            disabled: false,
            readOnly: true,
            componentName: 'TextField',
          },
        ]
      },
      id: 'FORM-NT766881QD7XWV5A3GDZB5RFHKOK2P3GD9SYKD1',
      componentsTree: [
        {
          condition: true,
          css: 'body {\n  background-color: #f2f3f5;\n}',
          children: [
            {
              condition: true,
              children: [
                {
                  condition: true,
                  children: [
                    {
                      condition: true,
                      loopArgs: ['item', 'index'],
                      componentName: 'TextField',
                      id: 'node_kahx5l5b',
                      props: {
                        labelTipsTypes: 'none',
                        __useMediator: 'value',
                        hasClear: false,
                        labelTipsIcon: '',
                        validationType: 'text',
                        autoFocus: false,
                        useI18nInput: false,
                        tips: {
                          type: 'JSExpression',
                          value:
                            '({"en_US":"","zh_CN":"","type":"JSExpression","extType":"i18n"})[this.utils.getLocale()]',
                        },
                        trim: false,
                        labelTextAlign: 'right',
                        placeholder: {
                          type: 'JSExpression',
                          value: '"请输入"',
                        },
                        state: '',
                        behavior: 'NORMAL',
                        value: {
                          type: 'JSExpression',
                          value: '""',
                        },
                        addonBefore: {
                          type: 'JSExpression',
                          value: '""',
                        },
                        validation: [{ type: 'required' }],
                        hasLimitHint: false,
                        cutString: false,
                        htmlType: 'input',
                        autoHeight: false,
                        labelColOffset: 0,
                        label: {
                          type: 'JSExpression',
                          value: '"活动名称"',
                        },
                        __category__: 'form',
                        labelColSpan: 4,
                        wrapperColSpan: 0,
                        rows: 4,
                        addonAfter: {
                          type: 'JSExpression',
                          value: '""',
                        },
                        scanCode: {
                          editable: true,
                          type: 'all',
                          enabled: false
                        },
                        wrapperColOffset: 0,
                        size: 'medium',
                        labelAlign: 'top',
                        labelTipsText: {
                          type: 'JSExpression',
                          value: '""',
                        },
                        maxLength: 200
                      }
                    },
                  ],
                  componentName: 'Form',
                }
              ],
              componentName: 'Dialog',
              props: {
                hasMask: true,
                visible: false,
                footer: true,
                footerActions: 'cancel,ok',
                confirmStyle: 'primary',
                confirmState: '确定',
                confirmText: {
                  type: 'JSExpression',
                  value: '"确定"',
                },
                autoFocus: true,
                title: {
                  type: 'JSExpression',
                  value: '"创建活动"',
                },
                closeable: 'esc',
                cancelText: {
                  type: 'JSExpression',
                  value: '"取消"',
                }
              }
            },
          ],
          methods: {
            __initMethods__: {
              type: 'JSExpression',
              value:
                "function (exports, module) { 'use strict';\n\nexports.__esModule = true;\nexports.helloPage = helloPage;\nexports.onCreateActive = onCreateActive;\n/**\n * 私有的,可复用的函数\n * 动作面板帮助文档:\n * @see https://lark.alipay.com/legao/help/design-tool-logic\n */\nfunction printLog(obj) {\n  console.info(obj);\n}\n\n/**\n * 页面内的函数,可以被页面内任何位置调用\n */\nfunction helloPage() {\n  console.log('hello page');\n}\n\nfunction onCreateActive() {\n  this.$('dialog_kahx5l5f').show();\n} }"
            }
          },
          componentName: 'Page',
          dataSource: { online: [], list: [], sync: true },
          lifeCycles: {
            constructor: {
              type: 'JSExpression',
              value:
                "function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n  if(typeof module.exports[item] === 'function'){\n    _this[item] = module.exports[item];\n  }\n});\n\n}",
              extType: 'function'
            },
            componentDidMount: {
              type: 'JSExpression',
              value:
                'function main(){\n    \n    "use strict";\n\nvar __compiledFunc__ = function didMount() {\n  // 页面节点加载渲染完毕\n  if (this.siteSwitch) {\n    this.siteSwitch();\n  }\n};\n    return __compiledFunc__.apply(this, arguments);\n  }',
              extType: 'function'
            }
          },
          props: {
            extensions: { 启用页头: true },
            contentBgColor: 'white',
            pageStyle: ':root {\n  background-color: #f2f3f5;\n}',
            className: 'page_km66qe5x',
            templateVersion: '1.0.0'
          }
        }
      ],
      dataSource: {
        online: [
          {
            isReadonly: true,
            formUuid: 'FORM-NT766881QD7XWV5A3GDZB5RFHKOK2P3GD9SYKD1',
            name: 'urlParams',
            description:
              '当前页面地址的参数:如 aliwork.com/APP_xxxx/workbench?id=1&name=宜搭,可通过 this.state.urlParams.name 获取到宜搭',
            id: 'NT766881ZB7XUX0E07MU7B1DMJCF3L4GD9SYKCQ',
            protocal: 'URI'
          }
        ],
        list: [],
        sync: true
      }
    }
  ]
}
×
Is this page helpful?
Yes No