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

背景介绍

正常情况下大部分功能可以用CML语法即可实现;常常会有特殊情况:

  • 当我们想实现一个特别复杂功能时常常会用到成熟的第三方库
  • 当产品经理需要各端差异化实现需求
  • 类似“分享到朋友圈”功能在小程序、Web、Native天生有底层能力差异
    我们这里举例实现一个柱状图图表一般会想到很强大的开源可视化库 echart

本文讲讲如何在CML项目中优雅的使用echart库,最终效果图如下:

多态组件扩展  - 图1

在CML项目中使用第三方库可以基于多态组件来开发。

多态组件扩展  - 图2多态协议的意义我就不做赘述了,直接说怎么使用。

项目初始化

新建一个项目 demo-poly

  1. cml init project

在项目中调用组件创建命令:

  1. cd demo-poly
  2. cml init component
  • 切换选择Polymorphic component

  • 输入echart

多态组件扩展  - 图3

此时项目中看到./src/components/echart文件夹

  1. ./src/components
  2. └── echart
  3. ├── echart.interface
  4. ├── echart.web.cml
  5. ├── echart.weex.cml
  6. └── echart.wx.cml

修改src/pages/index/index.cml文件里面的json配置,引用多态组件

  1. "base": {
  2. "usingComponents": {
  3. "echart": "/components/echart/echart",
  4. "demo-com": "/components/demo-com/demo-com"
  5. }
  6. }

修改src/pages/index/index.cml文件里面的组件template模板,调用echart组件

  1. <template>
  2. <page title="chameleon">
  3. <view class="scroller-wrap">
  4. <demo-com title="{{title}}" image-src="{{chameleonSrc}}"></demo-com>
  5. <echart class="echart" name="hi"></echart>
  6. </view>
  7. </page>
  8. </template>

修改style标签代码给<echart>标签添加宽高

  1. .scroller-wrap {
  2. display: flex;
  3. flex-direction: column;
  4. align-items: center;
  5. }
  6. .echart{
  7. display:flex;
  8. flex-direction:row;
  9. justify-content: center;
  10. height: 500px;
  11. width: 500px;
  12. }

在项目根目录下启动预览cml dev,有效果如下,展现2个组件分别是默认项目初始化放置的<demo-com>和我们刚刚添加的<echart>组件。echart作为多态组件,在web、weex、微信小程序分别展示的是不同文案。

web端效果如下:

多态组件扩展  - 图4

多态组件构成介绍

echart.interface:描述echart组件的输入和输出。

默认interface定义了一个组件,参数name的值是一个字符串,事件onshow的值是一个函数,函数的回调是一个Object结构为{value: 字符串}

  1. <script cml-type="interface">
  2. /*
  3. 定义一个inteface用于描述组件的属性和事件
  4. 1、 如何区分组件属性和事件?
  5. 通过类型来区分,事件为函数类型,属性为非函数类型
  6. 2、 如何定义组件属性
  7. 给interface添加同名的属性即可,指定类型
  8. 3、 如何定义组件事件
  9. 以事件名称为key值给interface定义属性,该属性是一个函数类型,返回值为void,
  10. 定义函数的第一个参数为自定义事件传递的detail对象类型
  11. */
  12. //定义事件detail对象的参数
  13. type EventDetail = {
  14. value: String
  15. }
  16. interface EchartInterface {
  17. name: String,
  18. onshow(eventDetail: EventDetail): void;
  19. }
  20. </script>

其中的具体语法参见。这里我们先不用改。

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

  • 在灰度区的template模板中:可以调用下层组件,传入的属性也可以是各自下层端的语法,绑定的函数回调事件对象也是原始对象。也可以正常使用普通的cml模板语法

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

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

实现微信echart

我们先修改./src/components/echart/echart.wx.cml实现微信的 echart 功能。Google搜索到了echarts-for-weixin,我们看该项目的使用文档,我们把该项目下的ec-canvas文件夹拷贝到./src/components/echart文件夹下

  1. ./src/components
  2. └── echart
  3. ├── ec-canvas
  4. ├── ec-canvas.js
  5. ├── ec-canvas.json
  6. ├── ec-canvas.wxml
  7. ├── ec-canvas.wxss
  8. ├── echarts.js
  9. └── wx-canvas.js
  10. ├── echart.interface
  11. ├── echart.web.cml
  12. ├── echart.weex.cml
  13. └── echart.wx.cml

修改./src/components/echart/echart.wx.cml文件里面的json配置,引用微信组件

  1. "base": {
  2. "usingComponents": {
  3. "ec-canvas": "./ec-canvas/ec-canvas"
  4. }
  5. }

修改cml模板,调用组件

  1. <template>
  2. <view class="container">
  3. <ec-canvas class="mychart_dom_bar" canvas-id="mychartbar" ec="{{ ec }}"></ec-canvas>
  4. </view>
  5. </template>

新建./src/components/echart/bar.js,作为统一存放echart图表的配置代码,代码如下:

  1. export default function getdata () {
  2. var option = {
  3. tooltip: {},
  4. xAxis: {
  5. data: ["衬衫","羊毛衫","雪纺衫"]
  6. },
  7. yAxis: {},
  8. series: [{
  9. name: '销量',
  10. type: 'bar',
  11. data: [5, 20, 36]
  12. }]
  13. };
  14. return option;
  15. }

修改./src/components/echart/echart.wx.cml文件里面的JS代码,编写传递给组件的参数

  1. import * as echarts from './ec-canvas/echarts';
  2. let chart = null;
  3. import getBar from './bar'
  4. function initChart(canvas, width, height) {
  5. chart = echarts.init(canvas, null, {
  6. width: width,
  7. height: height
  8. });
  9. canvas.setChart(chart);
  10. chart.setOption(getBar());
  11. return chart;
  12. }
  13. class Echart implements EchartInterface {
  14. props = {
  15. name: {
  16. type: String,
  17. default: '默认值'
  18. }
  19. }
  20. data = {
  21. ec: {
  22. onInit: initChart
  23. }
  24. }
  25. computed = {
  26. }
  27. watch = {
  28. }
  29. methods = {
  30. }
  31. beforeCreate() {
  32. }
  33. created() {
  34. }
  35. beforeMount() {
  36. }
  37. mounted() {
  38. this.$cmlEmit('onshow',{
  39. value: this.name
  40. })
  41. }
  42. beforeDestroy() {
  43. }
  44. destroyed() {
  45. }
  46. }
  47. export default new Echart();

修改./src/components/echart/echart.wx.cml文件里面的style代码,设置宽高

  1. .mychart_dom_bar,
  2. .container{
  3. height: 750cpx;
  4. width: 750cpx;
  5. }

此时能看到微信能展现图表了

多态组件扩展  - 图5

实现web端echart

web端使用vue框架,所以找到echart的vue版本 vue-echarts,按他说明文档,直接在chameleon项目根目录demo-poly(我创建的project名)下安装组件

  1. npm install echarts vue-echarts --save

打开src/components/echart/echart.web.cml修改json标签配置,引用组件

  1. {
  2. "base": {
  3. "usingComponents": {
  4. "chart": "vue-echarts/components/ECharts"
  5. }
  6. }
  7. }

修改cml模板,调用组件

  1. <template>
  2. <view class="container">
  3. <chart class="mychart_dom_bar"
  4. :options="bar"
  5. :init-options="initOptions"
  6. ref="bar"
  7. theme="ovilia-green"
  8. auto-resize
  9. />
  10. </view>
  11. </template>

修改script,设置传入chart组件的参数

  1. import 'echarts/lib/chart/bar'
  2. import getBar from './bar'
  3. class Echart implements EchartInterface {
  4. props = {
  5. name: {
  6. type: String,
  7. default: '默认值'
  8. }
  9. }
  10. data = {
  11. bar: getBar(),
  12. initOptions: {
  13. renderer: 'canvas'
  14. }
  15. }
  16. computed = {
  17. }
  18. watch = {
  19. }
  20. methods = {
  21. }
  22. beforeCreate() {
  23. }
  24. created() {
  25. }
  26. beforeMount() {
  27. }
  28. mounted() {
  29. this.$cmlEmit('onshow',{
  30. value: this.name
  31. })
  32. }
  33. beforeDestroy() {
  34. }
  35. destroyed() {
  36. }
  37. }
  38. export default new Echart();

此时web端也能看到图表效果了

多态组件扩展  - 图6

实现native weex端echart

weex不支持canvas接口,所以无法在weex环境下运行,我们可以用weex的web组件

需要注意点就是 weex的 web组件是用于展示一个页面的

所以,我们需要新建一个页面用于展示 echarts

  1. cml init page
  2. 然后输入页面名称
  3. weex-echarts

然后在 ./src/pages/weex-echarts/weex-echarts.cml中写入图表这个页面的内容

  1. <template>
  2. <page title="chameleon">
  3. <scroller height="{{-1}}">
  4. <view class="scroller-wrap">
  5. <echart class="echart" name="hi"></echart>
  6. </view>
  7. </scroller>
  8. </page>
  9. </template>
  10. <script>
  11. class Index {
  12. data = {
  13. }
  14. }
  15. export default new Index();
  16. </script>
  17. <style>
  18. .scroller-wrap {
  19. display: flex;
  20. flex-direction: column;
  21. align-items: center;
  22. }
  23. .echart{
  24. display:flex;
  25. flex-direction:row;
  26. justify-content: center;
  27. height: 500px;
  28. width: 500px;
  29. }
  30. </style>
  31. <script cml-type="json">
  32. {
  33. "base": {
  34. "usingComponents": {
  35. "echart": "/components/echart/echart",
  36. }
  37. },
  38. "wx": {
  39. "navigationBarTitleText": "index",
  40. "backgroundTextStyle": "dark",
  41. "backgroundColor": "#E2E2E2"
  42. }
  43. }
  44. </script>

./src/components/echart/echart.weex.cml里面修改

  1. <template>
  2. <web :src="h5url"
  3. @pagestart="onPageStart" @pagefinish="onPageFinish" @error="onError" @receivedtitle="onReceivedTitle"></web>
  4. </template>
  5. <script>
  6. class Echart implements EchartInterface {
  7. props = {
  8. name: {
  9. type: String,
  10. default: '默认值'
  11. }
  12. }
  13. data = {
  14. pagestart: '',
  15. pagefinish: '',
  16. title: '',
  17. error: '',
  18. canGoBack: false,
  19. canGoForward: false,
  20. //这是作者的本机IP,cml/h5/weex-echarts 这个页面只有echarts图表
  21. h5url: 'http://172.22.137.224:8000/cml/h5/weex-echarts'
  22. }
  23. methods = {
  24. onPageStart: function(e) {
  25. this.pagestart = e.url;
  26. },
  27. onPageFinish: function(e) {
  28. this.pagefinish = e.url;
  29. this.canGoBack = e.canGoBack;
  30. this.canGoForward = e.canGoForward;
  31. if (e.title) {
  32. this.title = e.title;
  33. }
  34. },
  35. onError: function(e) {
  36. this.error = url;
  37. },
  38. onReceivedTitle: function(e) {
  39. this.title = e.title;
  40. }
  41. }
  42. mounted() {
  43. this.$cmlEmit('onshow',{
  44. value: this.name
  45. })
  46. }
  47. }
  48. export default new Echart();
  49. </script>
  50. <style >
  51. .mychart_dom_bar{
  52. height: 500cpx;
  53. width: 750cpx;
  54. }
  55. .container{
  56. height: 500cpx;
  57. width: 750cpx;
  58. }
  59. </style>
  60. <script cml-type="json">
  61. {
  62. "base": {
  63. "usingComponents": {
  64. }
  65. }
  66. }
  67. </script>

此时weex端也能看到图表效果了

多态组件扩展  - 图7

最后

  • 同时如果有更多配置参数需要由调用方index.cml传入时,可以再echart.interface中定义更多输入输出的参数,保障各端一致。

  • 基于多态协议开发各端上实现差异较大的一类组件,调用上抹平了差异,同时我们可以各自维护,修改其中一端代码时,不会影响其他端的代码,做到充分隔离。

  • 后期我们可以把这 src/components/echart 单独发布成一个npm包,单拉出来维护