组件多态(Polymorphic component)

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

多态组件全景图

组件多态的使用

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

├── components
│   ├── c-list
│   │   ├── c-list.interface
│   │   ├── c-list.web.cml
│   │   ├── c-list.weex.cml
│   │   ├── c-list.wx.cml
│   │   ├── c-list.alipay.cml
│   │   ├── c-list.baidu.cml
│   │   └── ...

interface 文件

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

type eventType = 'change';
type eventDetail = {
  value: string
}
type changeEvent = (a: eventType, detail: eventDetail) => void;

export default Interface Clist {
  name: string,
  age: number,
  changeEvent: changeEvent
}

*.[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文件,其中包括了一个组件所拥有的视图层、逻辑层及配置信息。考虑以下两种场景:

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

为什么要引入多态协议

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

<template c-if="{{ENV === 'web'}}">
  <ul c-for="{{list}}">
    <li>{{item.name}}</li>
  </ul>
</template>
<template c-else-if="{{ENV === 'wx'}}">
  // 假设wx-list  是微信小程序原生的组件
  <wx-list data="{{list}}"></wx-list>
</template>
<template c-else-if="{{ENV === 'alipay'}}">
  // 假设alipay-list  是微信小程序原生的组件
  <alipay-list data="{{list}}"></alipay-list>
</template>
<template c-else-if="{{ENV === 'baidu'}}">
  // 假设baidu-list  是微信小程序原生的组件
  <baidu-list data="{{list}}"></baidu-list>
</template>
<template c-else-if="{{ENV === 'weex'}}">
  // 假设list  是weex端原生的组件
  <list source="{{list}}"></list>
</template>

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

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

  1. 增加代码复杂度,难以维护
  2. 各端组件的属性和事件定义可能不一致
  3. 各端组件耦合在一起,bug风险极高
  4. 没有做到各端代码的分离,增大体积

而利用了组件多态之后的使用方式如下:

<c-list data="{{list}}"><c-list>

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

results matching ""

    No results matching ""