SDK

    1. 前端极速提效SDK开发

    Published
    December 16, 2022
    Reading Time
    3 min read
    Author
    Felix

    文章主要介绍了前端极速提效 SDK 的开发。探讨了开发初衷是节省开发时间和提高精准度,开发前需考虑多平台、可扩展、可插拔等。还阐述了输入输出和中间态,包括输入的限制、输出的示例、两种中间态的数据结构及选择原因。最后作者总结写文章要有用,能提高能力。

    关联问题: SDK如何实现可扩展 中间态如何选择 多平台支持怎样

    1.开篇

    作为一个api前端工程师,我经常会在调接口的时候想,为什么不能每次在调接口前就能根据文档或者Json数据事例直接去生成tsinterface接口和组件配置项来干掉我们这个对字段的操作?当然本文章不阐述具体的生成配置项的代码,只会简单的说一下思路(因为关于组件生成配置项完全是一个定制化的功能,而且主要是出于业务考虑这块也不是特别好扔出来讲)。

    先设想一下我们最简单的平时的开发流程。

    看文档->写ts类型->写组件配置项->联调->不同环境测试->上线。

    当我们拥有了一个高度定制化的自动生成类型和配置项的SDK后,可能你就只需要微调一下你的代码。

    输入文档url或输入Json数据->联调->不同环境测试->上线。

    可能对于长期写ts或者组件配置项的同学们很清楚这意味着什么,几乎可以把开发时间对半砍掉,当然这是有代价的,代价就是尽可能详情的文档,但这个代价并不是我们不能接受的一件事情。

    总结一下:

    1、提效,极大的节约我们的开发时间,从搬砖中解放出来。

    2、精准, 人毕竟总是会犯错的,有时候可能少写一个字段多写少写或者写错字段也是经常发生的事情了。而如果借助插件至少能避免在前端层面上的字段错误(后端给你的字段是错误的情况也时有发生)。

    2.对于一个SDK的自我修养

    在我们进入开发之前,我们需要先调研和思考一下大方向的点多平台可扩展可插拔

    具体落实到细节上就是目录的分层、逻辑的分离无关平台的核心代码,有关平台的方法和一些抛出的服务),它的关注点在什么地方,它对外部是否会造成影响,市面上主流的api平台返回的数据结构。

    关注点

    首先对于一个这样的插件,它的作用就决定了,它一定要快轻量且满足多平台,我并不希望它是一个负担很重的SDK让我们本来就复杂的项目难上加难。我希望它在使用层面上非常简单,就是仅仅只需要一个抛出的api,就可以使得我们达成目的。

    对外部的影响

    对于一个SDK,我们要做的是不对输入造成任何影响,我们的输入源对我们来说是一个未知的(可能是平台接口返回来的数据可能是一个代码里定义的对象也可能是复制的Json数据等)数据,我们要做的就是保证一个原始数据的不可变性。

    目录分层和逻辑的分离

    关于逻辑的分离,我们可以将整个插件的整体逻辑拆分成几个简单部分,1.对接各api平台。2.整理数据。3.输出ts接口。4.输出组件配置项

    每一个平台生成一个单独一个SDK(内核+SDK),然后每个平台分开打包分开上传到npm,平台服务做一些初始的拿取数据、整合数据,将数据交给核心层core,由核心层进一步整理完成输出交给应用层。

    这就解决了扩展性多平台的设计理念,每次需要扩展的时候就是在web中去加一些新的代码和一些新的定制功能

    实际上这并不是很标准,但有时候我们确实需要从一些标准中逃出来(因为它从功能来看不需要更多的数据流)。

    而对2、3、4点都是属于核心代码,1是属于外部接入层的东西,那么相对到一个简单的目录划分上。

    image.png

    市面上api平台返回的数据结构

    这里对比了wiki,Yapi,swagger,比较好让大家看懂的是Yapi,它是以一个数组对象的结构来描述一个字段,这对我们后面的开发是有帮助的。

    image.png
    image.png

    3.输出输出和中间态

    这里我们只讲core输入和输出,以及比较合适于两个功能的中间态。

    输入

    对于输入来说,我们处理的东西会有两种,一种是JSON数据,还有一种是来自文档的输入,对于这两种输入来说我们都需要对其有所限制,仅仅只处理数组数组对象对象,我们先说到普通的JSON数据说起,后面我们会说到来自文档的输入,对于我们的输入应该尽可能的复杂去考虑多种情况。

    const json = {
      phone: 12312312312,
      name: 'Hello',
      obj: {
        cc: 1,
        dd: 2
      },
      sOBj: {
        dd: 5,
        g: 7,
        e: {
          c: 'y'
        }
      },
      arr: [1, 23, 4, 5],
      list: [
        {
          cc: 1,
          dd: 2
        },
        {
          cc: 1,
          g: 3,
          bb: 5
        }
      ],
    };
    
    

    输出

    看到上述例子,那有关于我们的输出,应该是什么样子。

    interface Json {
      phone: number;
      name: string;
      objL: Obj;
      sObj: SObj;
      arr: (number | string)[];
      list: List[];
    }
    interface Obj {
      cc: number;
      dd: number;
    }
    interface SObj {
      dd: number;
      g: number;
      e: E;
    }
    interface E {
      c: string;
    }
    
    interface List {
      cc: number;
      dd?: number;
      g?: number;
      bb?: number;
    }
    
    

    中间态

    关于两种中间态,他们从输入->中间态过程,其中时间复杂度是相同的都会是一个o(n^2)-o(n)的时间复杂度reduce + 递归),唯一的区别就是再最后输出ts的时候的时间复杂度第二种会降低一个等级的时间复杂度,但第二种中间态的数据结构并不利于我们生成表格配置项(因为实际上对应到api平台的数据上第一种更加契合)。

    所以我们需要做出一点牺牲,因为本质上我们的sdk并不是运行时的sdk,而是一个我们开发时的提效sdk我们是可以牺牲一点时间复杂度的。所以最终的选择为描述性数组结构

    1.描述性结构

    我们先来比较简单的一种数据结构,相当于将对象的每一个值进行具体的描述,再进行接下来的输出,这种数据结构有2个好处,1、便于理解。2、他在输出ts.interface的同时也方便于组件配置项的生成。

    const json = {
      phone: 12312312312,
      name: 'Hello',
      obj: {
        cc: 1,
        dd: 2
      },
      list: [
        { a: 1, b: 2 },
        { c: 3, b: 5 }
      ]
    };
    const jsonFormmter = [
      {
        key: 'phone',
        value: 12312312312, //示例value
        type: 'number'
      },
      {
        key: 'name',
        value: 'Hello',
        type: 'string'
      },
      {
        key: 'obj',
        type: 'Object',
        value: [
          {
            key: 'cc',
            value: 1,
            type: 'string'
          },
          {
            key: 'dd',
            value: 2,
            type: 'number'
          }
        ]
      },
      {
        ket: 'list',
        type: 'ArrayofObject',
        value: [
          [
            {
              key: 'a',
              value: 1,
              type: 'number'
            },
            {
              key: 'b',
              value: 2,
              type: 'number'
            }
          ],
          [
            {
              key: 'c',
              value: 3,
              type: 'number'
            },
            {
              key: 'b',
              value: 5,
              type: 'number'
            }
          ]
        ]
      }
    ];
    
    

    2.id连接型结构

    我们可以观察下面伪代码中的types,每一个复杂对象都拥有自己id,来进行一个链接。比如id:3链接的是大外层结构,里面的id:2分别又对应到2个具体的数组对象的结构中。

    这样的结构的好处是:1、利于追溯问题。2、在最后生成ts的时候相比于上面的结构时间复杂度降低了。

    const json = {
      phone: 12312312312,
      name: 'Hello',
      obj: {
        cc: 1,
        dd: 2
      },
      list: [
        { a: 1, b: 2 },
        { c: 3, b: 5 }
      ]
    };
    const jsonFormmters1 = {
      rootTypeId: '3',
      types: [
        {
          id: '1',
          typeObj: {
            cc: 'number',
            dd: 'number'
          }
        },
        {
          id: '4',
          typeObj: {
            a: 'number',
            b: 'number'
          }
        },
        {
          id: '5',
          typeObj: {
            c: 'number',
            b: 'number'
          }
        },
        {
          id: '2',
          arrayOfTypes: ['4', '5']
        },
        {
          id: '3',
          typeObj: {
            phone: 'number',
            name: 'string',
            obj: '1',
            list: '2'
          }
        }
      ]
    };
    
    

    总结

    认真的反思了一下,写文章这事吧,需要有用,至少说对看完的人或者自己都有一个实际上的提高不管是思路还是编写代码的能力。所以就慢慢放慢节奏,多学习多看看再写。这几天阳了好难受,祝大家身体早日康复。