codecamp

导入与导出

导出

即 将 CML 可重用组件导出给普通项目使用,或者说 某端普通项目里面使用 CML 可重用组件。

导入

即 在 CML 可重用项目中导入普通项目产出的组件,或者说 可重用代码 CML 项目使用某端普通组件。

使用方式

终端项目框架导入导出说明
微信原始项目已支持
微信MPX待支持
WebVue已支持
WebReact待支持
Web其他支持导入,不支持导出
NativeVue已支持
NativeReact待支持
NativeFlutter待支持
NativeObjective-C MVVM框架待支持
NativeAndroid Java MVVM框架待支持

渐进式接入

跨端有 2 种需求。

  • 整个项目一套代码实现:第一种业务层需求在各端环境高度类似,原本需要针对不同端重复开发、重复测试,那么使用 CML 将整个项目”从上至下“都用一套代码运行,针对各端底层极个别差异化实现(使用多态协议)。 - 场景举例:首页官网、列表详情页等
  • 仅组件一套代码运行:第二种是各端页面需求不一致,却有共同的组件需要重复开发、重复测试,各个端用各自原本框架开发(或者使用 CML 方案),使用一套代码开发公用组件,然后各个端可以使用公用组件实现业务 - 场景举例:分享组件、支付组件、地图组件
整个项目一套代码实现仅组件一套代码运行

webpack 集成

注:内部 webpack 插件版本基于 webpack@3.12.0 开发选择,暂不兼容 webpack4。

通过以下步骤,可以让 webpack 项目中使用 CML 的任意组件。

  • 1 安装 npm 包 npm i easy-chameleon chameleon-ui-builtin
  • 2 执行脚本 node node_modules/\easy-chameleon/bin/index.js,该脚本会执行检测,安装未安装的第三方 npm 包
  • 3 .babelrc 的 preset 添加 flow, CML 中用了 flow 语法,如果需要用到chameleon-api,建议配置babel-plugin-chameleon-import插件实现按需加载。例如:
{
  "presets": [
    "flow",
    ["env", {
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-0"
  ],
  "plugins": [
    "transform-vue-jsx",
    "transform-runtime",
    [
      "babel-plugin-chameleon-import", {
      "libraryName": "chameleon-api",
      "libraryDirectory": "src/interfaces",
      "libraryFileName": "index.js",
      "defaulLibraryDirectory": "",
      "defaulLibraryFileName": "index.js",
      }
    ]
  ]
}

  • 4 入口代码中,引入代码 import 'easy-chameleon/entry/web_global.js';
  • 5 修改 webpack 配置文件,easy-chameleon提供了getConfig方法获取 webpack 配置 ,利用webpack-merge将项目原有 webpack 配置与getConfig方法获取的配置进行合并,例如:const merge = require('webpack-merge') const {getConfig} = require('easy-chameleon'); devWebpackConfig = merge(devWebpackConfig, getConfig({ cmlType: 'web', media: 'dev', hot: true, disableExtract: false, context: path.join(__dirname,'../'), cmss: { rem: false, scale: 0.5 } })) getConfig方法参数 getConfig(Object object)参数类型默认值必填说明cmlTypeString是端类型,可选值为web|wx|weexmediaString是构建模式,可选值为dev|buildhotBooleanfalse否是否开启热更新,只在web端生效disableExtractBooleanfalse否不提取css文件cmssObject否cmss处理的配置,参见下方cmss对象属性列表wxConfigObject否微信端构建配置,参见下方wxConfig对象属性列表

cmss 对象属性列表

参数类型默认值必填说明
remBooleantrue将cpx以75cpx=1rem为标准转换成rem
scaleNumber0.5当rem为false时,scale将生效,将cpx乘以scale为px

wxConfig 对象属性列表

参数类型默认值必填说明
entryArray[String]指定输出的组件入口,以项目根目录下的相对路径,会寻找指定路径下的cml文件进行编译
outputPathString输出路径


组件导出

这里介绍的是第二种场景,将 CML 组件导出到某个端使用。

介绍

为了让开发者更自由地使用 CML 我们提供组件导出的形式,让开发者可以在任意端原有项目中使用通过 CML 开发的组件。我们用 Web 端和小程序端举例,按正常开发模式我们需要分别维护着 4 套代码(web、wx、alipay、baidu),在某一个业务场景下需要开发一个时间选择器的组件,原有模式下我们需要每个端独自开发,而对于这种公共的组件,就可以选择使用 CML 去维护,通过 CML 开发,只需要维护一套组件代码,最后通过组件导出就可以在各个端进行使用了。这样的模式完全利用了 CML 的跨端优势,大大降低了代码开发维护成本。

组件导出开发模式: 

命令行

  • cml web export 执行 Web 端组件导出
  • cml weex export 执行 Weex 端组件导出
  • cml wx export 执行 wx 端组件导出
  • cml alipay export 执行 alipay 端组件导出
  • cml baidu export 执行 baidu 端组件导出

配置

在 chameleon.config.js 中增加 export 配置,即可自定义组件导出配置,可配置项参考下表:

配置项类型说明
entryArray组件导出的入口目录,项目根目录的相对路径
outputPathString组件导出目录,绝对路径
publicPathString公共资源地址
hashBoolean导出文件名是否带hash,默认true
minimizeBoolean导出文件名是否压缩,默认true
disableExtractBoolean不拆分css,默认false
externalsObject导出依赖分离,可配置不导出部分依赖,而使用外部依赖

以 Web 端为例,配置如下:

cml.config.merge({
  web: {
    dev: {},
    build: {},
    export: {
      entry: ['src/components'],
      publicPath: 'https://static.chameleon.com/static',
    },
  },
});

导出组件

CML 组件导出目录结构如下:

├── dist
|   ├── export
│   │   ├── platform (web、weex、wx、alipay、baidu ...)
│   │   │   ├── common
|   │   │   │   ├── web_global.js [仅导出web端组件时存在,需要在入口文件中引用]
|   │   │   │   ├── web_global.css [会在web_global.js中引用]
│   │   │   ├── 组件目录
│   │   │   └── 资源目录

下面是在 webpack+vue 环境下引用 CML 导出组件的示例:

假设目前已经通过 CML 项目导出了 c-header 组件,将组件移动到 webpack+vue 的环境后,需要进行一下几步操作:

  1. 假设是 Web 端组件,则首先需要在入口文件中引用 web_global.js
  2. 在需要使用组件的地方引用组件即可

导出组件公共依赖分离

当组件 A 和组件 B 同时依赖于公共模块 C 时,普通导出会将 C 打包到导出的 A、B 组件代码中,此时使用 A、B 组件相当于有两份模块 C 的代码,为了优化这一问题,导出组件提供分离公共依赖配置,即将模块 C 作为第三方依赖,在 A、B 组件导出时将模块 C 分离,在使用时依赖模块 C。

这里以chameleon-runtime为例,首先将chameleon-runtime编译出仅 Web 端使用的版本chameleon-runtime-web,在导出 web 组件配置部分增加相关配置,然后导出的组件在依赖chameleon-runtime的部分就会变成依赖chameleon-runtime-web,最后在使用的项目中下载chameleon-runtime-web即可使用。

cml.config.merge({
  web: {
    export: {
      entry: ['src/components'],
      publicPath: 'https://static.chameleon.com/static',
      externals: {
        'chameleon-runtime': 'chameleon-runtime-web',
      },
    },
  },
});

Bug & Tips

  • .babelrc 配置中去掉module: false
  • 由于生产模式组件为编译后的模块,所以尽量使用线上资源

Web

组件开发模式

Web 端组件接入

chameleon 允许在 Web 端多态组件中直接引入原生 vue 组件,一方面是为了增加代码重用度,另一方面则是方便渐进式地迁移使用 CML

为什么要接入 Web 端组件

chameleon 作为跨端框架,将各端相同性及差异性进行统一封装形成 CML 的规范,但即使是这样,我们也没有办法百分百地避免差异,这样的差异可能来自产品的要求、技术的实现等等,由此 CML 提出了多态组件协议,在多态组件实现中,直接引用原生组件,降低开发成本。

怎么引入 Web 端组件

在 CML 中使用组件只需要在组件配置中写入依赖的子组件,下面是 Web 端组件引用 vue 单文件组件的示例:

<!-- index.cml -->
<template>
  <v-list></v-list>
</template>
...
<script cml-type="json">
{
  "base": {
    "usingComponents": {
      "v-list": "/components/vue-components/v-list"
    }
  }
}
</script>
<!-- components/vue-components/v-list.vue -->
<template>
  <ul>
    <li v-for="l in list">{{ l }}</li>
  </ul>
</template>
<script>
export default {
  props: {
    list: {
      type: Array,
      default: function() {
        return [1, 2, 3, 4];
      },
    },
  },
};
</script>
需要注意的是组件路径需要写到.vue 层级,但是不带后缀。

Weex

组件开发模式 

Weex 端组件接入

chameleon 允许在 Weex 端多态组件中直接引入原生 vue 组件,一方面是为了增加代码重用度,另一方面则是方便渐进式地迁移使用 CML

为什么要接入 Weex 端组件

chameleon 作为跨端框架,将各端相同性及差异性进行统一封装形成 CML 的规范,但即使是这样,我们也没有办法百分百地避免差异,这样的差异可能来自产品的要求、技术的实现等等,由此 CML 提出了多态组件协议,在多态组件实现中,直接引用原生组件,降低开发成本。

怎么引入 Weex 端组件

第一类是 Weex 支持的原生组件,比如div text等,详细请查看Weex 原生组件列表,如果这样的组件已经满足开发需求,那么就可以直接使用了:

<!-- list.weex.cml -->
<template>
  <div>
    <text v-for="l in list">{{ l }}</text>
  </div>
</template>

第二类是原先封装好的组件,首先你需要将组件复制到 CML 项目中,然后只需要在 CML 组件中声明式引入该组件即可使用。 还是以list组件为例,假设原有封装好的组件custom-list,目录结构如下:

<!-- index.cml -->
<template>
  <v-list></v-list>
</template>
...
<script cml-type="json">
{
  "base": {
    "usingComponents": {
      "v-list": "/components/vue-components/v-list"
    }
  }
}
</script>
<!-- components/vue-components/v-list.vue -->
<template>
  <ul>
    <li v-for="l in list">{{ l }}</li>
  </ul>
</template>
<script>
export default {
  props: {
    list: {
      type: Array,
      default: function() {
        return [1, 2, 3, 4];
      },
    },
  },
};
</script>
需要注意的是组件路径需要写到.vue 层级,但是不带后缀。

微信小程序

组件开发模式 

小程序组件接入

chameleon 允许在多态组件中引入原生微信小程序组件,对于已经熟悉小程序组件开发的朋友将非常 easy,之前封装的微信小程序组件可以直接使用,微信小程序相关的 ui 库可以直接使用,微信小程序自带的组件也可以直接使用。

为什么要接入微信小程序组件

多态组件存在的差异不过来自于各端需求不同,又或是各端实现方式的不同。微信小程序组件的接入跟第二个问题完美契合,在原有的小程序开发过程中或许已经产出了常用组件,又或是使用着某个微信小程序的组件库,当使用 CML 进行开发时,避免了二次开发原有组件的成本。

#怎么引入微信小程序组件

第一类是微信小程序支持的组件,比如view text等,详细请查看微信小程序组件列表,如果这样的组件已经满足开发需求,那么就可以直接使用了:

<!-- list.wx.cml -->
<template>
  <view>
    <text v-for="l in list">{{ l }}</text>
  </view>
</template>

第二类是原有的组件,首先你需要将组件复制到 CML 项目中,然后只需要在 CML 组件中声明式引入该组件即可使用。 还是以list组件为例,假设原有封装好的微信小程序的组件custom-list,目录结构如下:

├── components                      // 组件文件夹
|   ├── custom-list
|   |   ├── custom-list.wxml
|   |   ├── custom-list.wxss
|   |   ├── custom-list.js
|   |   └── custom-list.json

那么,在 list.wx.cml 中可以直接引用:

<!-- list.wx.cml -->
<template>
  <custom-list list="{{list}}"></custom-list>
</template>
...
<script cml-type="json">
{
  "base": {
    "usingComponents": {
      "custom-list": "/components/custom-list/custom-list"
    }
  }
}
需要注意的是组件路径需要写到.wxml 层级,但是不带后缀。

第三类是微信小程序的组件库,这里以iVew Webapp为例,首先需要将其代码下载下来放到 CML 项目中,假设目录结构如下:

├── components                      // 组件文件夹
|   ├── iview
|   |   ├── action-sheet
|   |   |   ├── index.js
|   |   |   ├── index.json
|   |   |   ├── index.wxml
|   |   |   └── index.wxss
|   |   ├── alert
|   |   ├── avatar
|   |   └── ...

这里我们需要使用 action-sheet 组件只需要如下即可:

<!-- component.wx.cml -->
<template>
  <action-sheet></action-sheet>
</template>
...
<script cml-type="json">
{
  "base": {
    "usingComponents": {
      "action-sheet": "/components/iview/action-sheet/index"
    }
  }
}
需要注意的是组件路径需要写到.wxml 层级,但是不带后缀。

支付宝小程序

组件开发模式 

小程序组件接入

chameleon 允许在多态组件中直接引入支付宝小程序组件,如果你已经封装了支付宝小程序的组件,如果你已经在用支付宝小程序的组件库,不用担心,在 CML 项目中你仍然可以使用。

为什么要接入支付宝小程序组件

多态组件存在的差异不过来自于各端需求不同,又或是各端实现方式的不同。支付宝小程序组件的接入跟第二个问题完美契合,在原有的小程序开发过程中或许已经产出了常用组件,又或是使用着某个支付宝小程序的组件库,当使用 CML 进行开发时,避免了二次开发原有组件的成本。

怎么引入支付宝小程序组件

第一类是支付宝小程序支持的组件,比如view text等,详细请查看支付宝小程序组件列表,如果这样的组件已经满足开发需求,那么就可以直接使用了:

<!-- list.alipay.cml -->
<template>
  <view>
    <text v-for="l in list">{{ l }}</text>
  </view>
</template>

第二类是原有的组件,首先你需要将组件复制到 CML 项目中,然后只需要在 CML 组件中声明式引入该组件即可使用。 还是以list组件为例,假设原有封装好的支付宝小程序的组件custom-list,目录结构如下:

├── components                      // 组件文件夹
|   ├── custom-list
|   |   ├── custom-list.axml
|   |   ├── custom-list.acss
|   |   ├── custom-list.js
|   |   └── custom-list.json

那么,在 list.alipay.cml 中可以直接引用:

<!-- list.alipay.cml -->
<template>
  <custom-list list="{{list}}"></custom-list>
</template>
...
<script cml-type="json">
{
  "base": {
    "usingComponents": {
      "custom-list": "/components/custom-list/custom-list"
    }
  }
}
需要注意的是组件路径需要写到.axml 层级,但是不带后缀。

百度小程序

组件开发模式 

小程序组件接入

chameleon 允许在多态组件中直接引入百度小程序组件,如果你已经封装了百度小程序的组件,如果你已经在用百度小程序的组件库,不用担心,在 CML 项目中你仍然可以使用。

为什么要接入百度小程序组件

多态组件存在的差异不过来自于各端需求不同,又或是各端实现方式的不同。百度小程序组件的接入跟第二个问题完美契合,在原有的小程序开发过程中或许已经产出了常用组件,又或是使用着某个百度小程序的组件库,当使用 CML 进行开发时,避免了二次开发原有组件的成本。

怎么引入百度小程序组件

第一类是百度小程序支持的组件,比如view text等,详细请查看百度小程序组件列表,如果这样的组件已经满足开发需求,那么就可以直接使用了:

<!-- list.baidu.cml -->
<template>
  <view>
    <text v-for="l in list">{{ l }}</text>
  </view>
</template>

第二类是原有的组件,首先你需要将组件复制到 CML 项目中,然后只需要在 CML 组件中声明式引入该组件即可使用。 还是以list组件为例,假设原有封装好的百度小程序的组件custom-list,目录结构如下:

├── components                      // 组件文件夹
|   ├── custom-list
|   |   ├── custom-list.swan
|   |   ├── custom-list.css
|   |   ├── custom-list.js
|   |   └── custom-list.json

那么,在 list.baidu.cml 中可以直接引用:

<!-- list.baidu.cml -->
<template>
  <custom-list list="{{list}}"></custom-list>
</template>
...
<script cml-type="json">
{
  "base": {
    "usingComponents": {
      "custom-list": "/components/custom-list/custom-list"
    }
  }
}
需要注意的是组件路径需要写到.swan 层级,但是不带后缀。


工程化
CML SDK
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }