阅读 5745

你不知道的前端组件库

        关于组件库设计的文章一搜能出来一大把,但大多数都是从UI角度或者视觉规范层面来讲的,但随着前端技术的发展和迭代,前端这个岗位早已不是过去定义的实现样式那么简单了,这篇文章会站在一个技术同学的角度来聊一下组件库的设计除了样式还有哪些可以关注的。

组件库应用场景思考

        组件的本质其实在于代码的复用和抽象,以此起到提升开发效率的目的, 可以说一个组件的灵魂就在于对业务场景的解构,比如各种控件的抽象,事件的抽象,样式的抽象等,并通过组合和配置的方式实现业务需求,某种程度上一个组件库就像是一个工具箱,为业务场景的开发提供各种零件,而要如何设计这些零件,就需要对应用场景有一个深入的了解和思考。

        比如C端的组件库,需要在UI,性能,浏览器兼容性上有更多的思考,追求更极致的交互体验和视觉体验,同时C端应用的UI经常会根据节日,活动的主题而切换, 在组件库的设计上也要考虑到个性化的扩展空间。

        而B端的组件库,则更加注重易用性和可复用性,保证中台或者B端工具类应用的高速迭代和高效使用。同时B端应用会涉及很多的数据展示,和增删改查操作,这就需要组件库在交互控件上有更细的职责划分和更丰富的选择。

        除了应用的业务场景不同,还可能存在平台场景的多元化问题,比如同一套组件库是否能在H5和小程序中复用。这些都需要组件库设计者在规划之初就考虑进去。

目前主流的组件库分析

目前主流的组件库可以分为两大类:

        一类是基于几大前端框架的生态来实现的,在设计上都已经形成了自己的风格和规范,也有自己的沉淀,比如基于Vue生态的element, 基于react的antD 等。他们的优点是已经发展的很成熟了基本上可以cover大多数的业务场景,缺点是组件库在跨框架迁移上的成本较大。

        还有一类是组件库则是针对跨端的需求设计的。通常底层会依赖一套跨平台开发的框架 如京东的taro,美团的mpVue, 淘宝的Rax等,他们的核心思想也是 write one run anywhere, 这类组件库在实现上, 主要利用框架提供的基础实现尽可能抹平各个平台的差异,让使用者可以基于一套开发规范,实现多个平台的业务。这类组件的优点是,可以减少开发者在跨平台上的学习成本,缺点是组件库的丰富度还有所欠缺,能覆盖的场景也比较局限

        除了上述的两大类,腾讯的omi,则是利用web comp的特性,来实现的跨端和跨框架,可以算是下一代前端框架的一个尝试了。

组件库的规划

        组件库的设计过程其实也很类似一款产品的设计过程,会有前期的场景调研,竞品分析, 需求整理,用户体验(这里应该叫开发体验),以及持续的试错迭代。

综合上面的一些思考和分析,我会试着把组件库规划如下:

组件需求:

  • 组件胶水层 --> 实现组件和实际的业务之间的衔接

  • 组件代理层 --> 可以基于代理实现组件内部的灵活调用和局部刷新

  • 组件交互层 --> 根据业务场景 抽象基于交互事件的功能,并通过装饰器注入

  • 组件扩展层 --> 实现组件间的克隆,继承,扩展

  • 组件库cli --> 方便组件的迭代和工程化

  • 性能 --> 实现最小粒度的重新渲染

组件库解决痛点

  • function component 外部不方便继承扩展
  • 实现组件和调用方交互时的局部更新,提高性能
  • 通过胶水层和前端框架连接,降低框架迁移的成本
  • 封装事件驱动的状态管理,也可以配合一个状态组件做扩展

组件内部系统:

  • 样式

  • 事件

  • 状态

  • 继承

组件库架构选型(暂定,后期可能还会调整)

以rax为基础,结合mono repo, peer dep 和 组件的多态协议 开发, 实现按需引入,保证业务调用层在跨端场景下的接口统一,最大发挥各个端的特性。

组件库features(暂定,后期可能还会调整):

  • 局部刷新 和 跨层级调用
     Modal.proxy.visible = true;
     Modal.show(config)
复制代码
  • 扩展继承

    //克隆
    const Modal2 = Modal.clone();
    复制代码
    //扩展
    const Modal2 = genModal({
        methods: {
          cancel() {
            console.log('%c rewrite the cancel','color:#299865',this);
            this.visible = false;
          }
        }
     });
    
    复制代码
    //批量扩展
    import {factoryAuto} from 'component'
    const {Button, Modal} = factoryAuto(pubConfig);
    复制代码
    //继承
    const Button2 = genButton(Button1.proxy, newFeature)
    复制代码
  • 轻量的状态组件


    <Button 
         x-for={(item, key) in statusList} 
         type="+success" 
         onClick={_ => trySingle(item)} >
        wait{key}
     </Button>

复制代码

    function trySingle (item) {
        performance.watch()
        item.choosed = !item.choosed
        if (item.choosed) {
          return new Status('pending...', { backgroundColor: 'green' })
        } else {
          return new Status(null)
        }
  }

复制代码
  • 装饰器注入
   @addTransition('visible')  
   class BaseMix extends Base {}
复制代码
  • scope作用域组件(局部render)

<Button scope={item}  
    x-for={(item, key) in scopeList} 
    type="+success" 
    onClick={_ => {tryScope(item)}} >
    wait{key}
</Button>

复制代码

function tryScope(item) {
    performance.watch();
    item.choosed = !item.choosed;
  }

复制代码
  • 事件装饰器(loading, lock等)
    @addLoading
    class EventCtxMix extends EventCtxBase {}
复制代码
     function Okfunc() {
        return new Promise((resolve, reject) => {
            //封装了防重锁和loading效果
            setTimeout(() => {
              resolve()
            }, 1600)
        })
    }
复制代码

组件库未来展望

        前端技术的一个特点就是变化快,除了各大框架提供的组件能力, 这几年web components 的概念也逐渐成为热点。除此之外随着react hooks的横空出世 function components时代也随之而来,相比于class compontents 基于hooks的function compontents在设计上更加灵活但外部扩展性也相对较差,基于这层考虑我有一个常识性的构想是在框架上层封装组件的逻辑层和样式层的接口,通过胶水层和组件连接只借助组件完成视图层的渲染,从而打破组件库和框架的次元壁,组件库的核心部分可以迁移到其他框架甚至web compontents,只需要重新coding胶水层就行了。当然这个构想距离最终落地还需要大量的实践基础。

友情链接

webpack构建流程及梳理 UI自动化测试框架Cypress初探 canvas在前端开发中的实践