复盘yh-page的设计与实现

介绍

yh-page 是给公司开发的一个小型模板库,集成自生成 mobx/redux store、自动插入路由、傻瓜式图形界面生成等功能于一体。具体用法不再赘述,都在这里了。因为迭代了很多版本,优化了很多次核心代码,有同学反映说我代码看不懂,注释太少了,所以今天给大家讲讲设计思路,也是给大家介绍一下 yh-page 中的 newTemplate 功能,这个东西可以看成一个简单的编译器。

别看它隐藏在犄角旮旯,其实是整个 yh-page 最核心的设计思想,这个放到最后再说。

设计思路

4 月份给大家分享过 plop,yh-page 一开始的定位是写几个通用模板,封装到 plop 中使用,但在逐步的迭代中,发现这种实现方式并不能很高效率的满足公司的需求。

由 plop 的演变

plop是一个小型脚手架,它的使用方法十分简单粗暴,只要通过命令行交互的方式,把所需要的数据注入模板中,就可以生成需要的页面了。

虽然使用 plop 完全能解决需求,但是维护的成本和灵活度远远不及预期。主要是在公司的需求中,有一个核心问题是,上海与北京的团队所使用的状态管理工具是不一样的,也就是说,同一套页面模板要配套两份状态管理模板(redux、mobx)。然而这两套状态库的业务逻辑都是一样的,只是 api 不同,如果按照 plop 所制定的使用方法,需要写两个 handlerbars 模板,但起码 80%的代码都是重复的,这大大增加了我之后维护 yh-page 的成本。

所以在后面的版本中考虑弃用 plop,自己写核心逻辑。用模板生成页面,底层就是字符串拼接。然而使用 plop 去操纵字符串,就相当于一个正常视力的人带着 800 度的近视镜去挤黑头一样,真的是多此一举。

整体流程

整体分为两个阶段,初始化和模板的创建阶段。

其中,在初始化阶段,包含一系列检查的工作,比如:检查用户是否安装了依赖项、所需的配置文件格式是否合法、读取配置文件等等。

接下来主要给大家讲的是模板创建阶段,工作流程如下:

模板创建

这部分最核心的工作就是将模板翻译成页面。我还是采用 hbs 作为模板引擎,但页面部分的代码只关注 jsx 中的组件部分,而不是像 plop 一样把页面所有代码都写到模板里,如:

<StyledWrapper>
      <Panel title="表单页">
        <YHForms/>
      </Panel>
</StyledWrapper>

对于让人头疼的两套 store 模板(redux/mobx),本质上都是由数据(data)和一些事件(events)来生成的。而 data 和 events 其实都是数据层与表示层公用的东西,为了极大限度的压缩代码,就要更大程度的复用核心逻辑!

data

data 就是从 store 中调出注入页面组件中的数据。

从 store 中拿出来渲染到页面的 data,基本上都是数组或对象类型的,所以对于 store 模板来说,其需要的数据都是一种制式的。对于各个模板,我们无需对每个模板都写好一份固定的数据,所以我们只需要一份可定制的 mock 数据就 ok 了!

events

再看 events, 是由页面发起的事件,这里分为两类,一个是 get 类型,一个是 submit 类型。在这里可以类比客户端与服务端的双向传输,要么是 store 传给页面数据,比如从 store 中获取数据再渲染;要么是页面往 store 中传数据,比如表单的提交、用户触发状态更改等。

通过 events,data 才能在 store 与页面上传输,所以 data 是依赖于 events 的,在这里就可以把数据与事件抽象成以下数据结构:

 {
    name: 'getDetails',  // 事件名称
    type: 'array', // 数据类型
    // 以下非必填
    init: true, // 是否在页面初始化时调用
    len: 5, // mock数据长度
    dataKeys: ['label', 'value'], // mock数据的key
  },

注意,name 的声明规则为'get'|'submit' + [storeName],拿上面的数据来举例,yh-page 会生成一份命名为detail的数组,长度为 5,每个数组项都会有labelvalue这两个属性。这个数组会被放入 store 中,并在 store 中注册一个方法getDetails,同时在展示层的页面也会声明这个方法,并且会在初始化方法中(如:useEffect…)调用。

页面抽取

上面是 store 部分的流程,我们再看下展示层这边是怎么工作的。

经过分析和对比团队中页面部分的代码,发现都满足

import语句+jsx部分(yh-components+原生dom+自定义组件)+store部分

在语句分析过后,这部分的工作分为 import 阶段、patch 阶段和 insert events 阶段。

import 阶段

经过分析 hbs 模板后,我们可以得到 yh-design 的组件、自定义组件、store events 等。import 阶段就是把所需要的依赖性全部引入,并把自定义组件拷贝给用户的过程。

patch 阶段

在 yh-design 中的很多组件都需要一些配置项数据,然而这些配置项也不是需要从 store 中拿到的,而是固定写死在页面中的,比如 table 组件的 columns。

这个阶段就是分析哪些组件需要这种常量,并把这些常量 mock 后注入到页面代码中。

insert events 阶段

这个阶段就是把上一小节说的 events 进行转换,得到 mobx/redux 的 api 封装后的事件,插入到新页面代码中。

图形界面的实现

yh-page 不仅能使用命令式生成,同样也能使用图形界面生成,如图:
demo
这部分实现方式比较简单,根据用户写好的项目主页地址,分析语句插入<script>标签,并起个服务,把这部分的 js 代码注入到页面中。

new template

因为内置的几个模板,如:list、tab、form 只支持几个固定场景,所以为了用户能更灵活的使用 yh-page 以兼容大多数场景,写了一个页面,复用上述的翻译部分的逻辑:
demo

只需定义好 dom 结构和 store 中的 event 类型就可以生成新的页面了!