组件多态(Polymorphic component)

chameleon在跨端的统一性上做了很多的工作,但即使是做到了99%的统一,仍然存在着1%的差异,基于代码可维护性的考量,chameleon引入了多态协议

多态组件全景图

组件多态的使用

项目根目录下执行cml init component,选择Polymorphic component,输入组件名称,例如c-list,生成如下文件结构

  1. ├── components
  2. ├── c-list
  3. ├── c-list.interface
  4. ├── c-list.web.cml
  5. ├── c-list.weex.cml
  6. ├── c-list.wx.cml
  7. ├── c-list.alipay.cml
  8. ├── c-list.baidu.cml
  9. └── ...

interface 文件

.interface文件利用接口校验语法对组件的属性和事件进行类型定义,保证各端的组件和事件一致,框架在开发环境的运行时做校验。例如c-list.interface

  1. type eventType = 'change';
  2. type eventDetail = {
  3. value: string
  4. }
  5. type changeEvent = (a: eventType, detail: eventDetail) => void;
  6. export default Interface Clist {
  7. name: string,
  8. age: number,
  9. changeEvent: changeEvent
  10. }

*.[web|weex|wx|alipay|baidu].cml

c-list.web.cmlc-list.weex.cmlc-list.wx.cmlc-list.alipay.cmlc-list.baidu.cml文件是灰度区,它是唯一可以调用下层端组件的CML文件,分别是web、weex、wx、alipay、baidu五个端的调用入口。建议这一块代码尽量薄,只是用来调用下层端代码,不要编写过于重的代码。

  • 在灰度区的template模板中:
    • 调用下层全局组件或者引入的下层组件时,该组件传入的属性是各自下层端的语法,绑定的函数回调事件对象也是原始对象
      • 引入的下层组件通过可以直接调用,传递各端下层属性语法
      • 下层全局组件需添加origin-前缀,例如<组件/>改成<origin-组件名/>,传递各端下层语法,查看示例
    • 调用普通CML内置组件或者引入的cml组件时,正常使用cml模板属性语法
  • 在灰度区的script逻辑代码中:

    • 可以调用下层端的全局变量和任意方法,以及下层端的生命周期。
    • 也可以正常使用普通cml逻辑代码。
  • 在灰度区的style样式代码中:

    • 可以使用下层端css语法。
    • 也可以正常调用cmss语法。
  • 在灰度区的json配置代码中:

使用举例

手把手教你系列- 实现多态 echart

扩展阅读

什么时候用到组件多态?

chameleon中的组件是采用单文件格式的cml文件,其中包括了一个组件所拥有的视图层、逻辑层及配置信息。考虑以下两种场景:

  • 场景一:当某个功能组件需要调用各端的原生组件,各端原生组件的属性不一致,或者一端有原生组件,其他端需要组合实现等。
  • 场景二:产品在需求上导致某一个组件在各端的结构表现不同。

为什么要引入多态协议

以场景一为例,先看一个最容易理解的跨端组件实现:

  1. <template c-if="{{ENV === 'web'}}">
  2. <ul c-for="{{list}}">
  3. <li>{{item.name}}</li>
  4. </ul>
  5. </template>
  6. <template c-else-if="{{ENV === 'wx'}}">
  7. // 假设wx-list 是微信小程序原生的组件
  8. <wx-list data="{{list}}"></wx-list>
  9. </template>
  10. <template c-else-if="{{ENV === 'alipay'}}">
  11. // 假设alipay-list 是微信小程序原生的组件
  12. <alipay-list data="{{list}}"></alipay-list>
  13. </template>
  14. <template c-else-if="{{ENV === 'baidu'}}">
  15. // 假设baidu-list 是微信小程序原生的组件
  16. <baidu-list data="{{list}}"></baidu-list>
  17. </template>
  18. <template c-else-if="{{ENV === 'weex'}}">
  19. // 假设list 是weex端原生的组件
  20. <list source="{{list}}"></list>
  21. </template>

上面的代码块是一个简单的列表实现,wx和weex都是使用了各自的原生组件,这样的实现方法其实是把三端或者N端的模版放在了同一个文件中,当然,这里只是展示了模版的复杂,假设在js代码块中也存在着端的判断,那代码的复杂可想而知。

总结下来,这样的代码有如下待解决问题:

  • 增加代码复杂度,难以维护
  • 各端组件的属性和事件定义可能不一致
  • 各端组件耦合在一起,bug风险极高
  • 没有做到各端代码的分离,增大体积
    而利用了组件多态之后的使用方式如下:
  1. <c-list data="{{list}}"><c-list>

可以看到我们只引用了一个c-list组件,该组件提供了统一的属性。