cml
(Chameleon 变色龙) 做为真正让一套代码运行多端的框架,提供标准的MVVM模式,统一开发各种终端。javascript
同时,拥有各端独立的 运行时框架(runtime)
、数据管理(store)
、组件库(ui)
、接口(api)
。php
此外,cml
在跨端能力增强
、能力统一
、表现一致
等方面作了许多工做。css
今天,为了让你们的项目优雅升级,快速接入,给你带来一份丰盛的cml迁移指南~html
送上5分钟视频教程vue
源码地址:github.com/jalonjs/cml…java
和微信小程序同样,cml
包含一个描述总体程序的 app
和多个描述各自页面的 page
。node
.
├── components // 包含各个组件
├── pages // 包含各个页面
├── app.js // 包含各个组件
├── app.js // 应用启动入口
├── app.json // 全局配置
├── app.wxss // 全局样式
└── project.config.json // 项目配置文件
复制代码
.
├── dist // 各个端构建结果
│ ├── alipay
│ ├── baidu
│ ├── wx
│ ├── web
│ ├── weex
│ └── config.json // 跨端配置map映射表
├── node_modules // 第三方库
├── mock // 模拟 接口数据 和 模板数据
├── src // 源代码开发目录
│ ├── app // 应用启动入口
│ ├── assets // 静态资源
│ ├── components // 包含组件
│ ├── pages // 包含页面
│ ├── store //数据管理
│ └── router.config.json // 路由配置文件
├── chameleon.config.js // 项目配置文件
└── package.json // npm包配置文件
复制代码
在小程序项目里面,分为:webpack
能够在项目根目录使用 project.config.json
文件对项目进行配置。git
配置示例:github
{
"miniprogramRoot": "./src",
"debugOptions": {}
}
复制代码
小程序根目录下的 app.json
文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等
配置示例:
{
"pages": ["pages/index/index", "pages/logs/index"],
"window": {
"navigationBarTitleText": "Demo"
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
}
}
复制代码
每个小程序页面也可使用 .json
文件来对本页面的窗口表现进行配置。
页面的配置只能设置 app.json
中部分 window
配置项的内容,页面中配置项会覆盖 app.json
的 window
中相同的配置项。
配置示例:
{
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信接口功能演示",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}
复制代码
一样,在 cml
项目里面,分为:
chameleon.config.js
为项目的配置文件,你能够定制化构建,好比是否带hash,是否压缩等等。
配置示例:
// 设置静态资源的线上路径
const publicPath = '//www.static.chameleon.com/static';
// 设置api请求前缀
const apiPrefix = 'https://api.chameleon.com';
// 合并配置
cml.config.merge({
wx: {
build: {apiPrefix}
},
alipay: {
build: {apiPrefix}
},
baidu: {
build: {apiPrefix}
},
web: {
dev: {
hot: true,
console: true
},
build: {
publicPath: `${publicPath}/web`,
apiPrefix
}
},
weex: {
build: {
publicPath: `${publicPath}/weex`,
apiPrefix
}
}
})
复制代码
cml
项目 app
目录下的 app.cml
文件的 <script cml-type="json" />
用来对 cml
应用 进行全局配置,具备 跨端配置 和 差别化 的能力
配置示例:
<script cml-type="json"> { "base": { "window": { "navigationBarTitleText": "各个端共同title", }, "permission": { "scope.userLocation": { "desc": "你的位置信息将用于小程序位置接口的效果展现" } } }, "wx": { "window": { "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "差别化 title", "navigationBarTextStyle":"black" } }, "baidu": { "window": { "backgroundTextStyle": "light" } }, "alipay": { "window": { "defaultTitle": "Chameleon" } } } </script>
复制代码
经过 usingComponents
配置 组件路径
注册引用的组件。
配置示例:
<script cml-type="json"> { "base": { "usingComponents": { "navi": "/components/navi/navi", "navi-npm": "cml-test-ui/navi/navi" } }, "wx": { }, "alipay": { }, "baidu": { }, "web": { }, "weex": { } } </script>
复制代码
app.json 配置项列表的 pages
字段用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径+文件名
信息。
数组的第一项表明小程序的初始页面(首页)。新增/减小页面,须要对 pages
数组进行修改。
若是项目有 pages/index/index.wxml
、pages/logs/logs.wxml
两个页面,则须要在 app.json
中写
{
"pages": ["pages/index/index", "pages/logs/logs"]
}
复制代码
src/router.config.json 是路由的配置文件,cml
内置了一套各端统一的路由管理方式。相应有 cml
路由配置映射以下:
{
"mode": "history",
"domain": "https://www.chameleon.com",
"routes":[
{
"url": "/cml/h5/index",
"path": "/pages/index/index",
"mock": "index.php"
},
{
"url": "/cml/h5/logs",
"path": "pages/logs/logs",
"mock": "logs.php"
}
]
}
复制代码
文件名不须要写文件后缀,cml
框架会自动去寻找对于位置的 .cml
文件进行处理。
依据统一资源索引URI,自适应打开不一样环境同一路由PATH:
在小程序项目里面,App()
函数用来注册一个小程序。接受一个 Object
参数,其指定小程序的生命周期回调等。
示例代码
App({
onLaunch(options) {
// Do something initial when launch.
},
globalData: 'I am global data'
})
复制代码
示例代码
<script> import store from '../store/index.js' import routerConfig from '../router.config.json'; class App { data = { store, routerConfig } created(res) { } } export default new App(); </script>
复制代码
细心的你会发现,
小程序中app.json app.js app.wxss
和 src/app/app.cml
的对应关系以下
小程序 app.js | cml项目 src/app/app.cml |
---|---|
app.js | <script></script> |
app.wxss | <style></style> |
app.json | <script cml-type="json"></script> |
在小程序项目里面,Page(Object)
函数用来注册一个页面。接受一个 Object
类型参数,其指定页面的初始数据、生命周期回调、事件处理函数等。
示例代码:
// index.js
Page({
data: {
text: 'This is page data.'
},
changeText: function(e) {
// sent data change to view
this.setData({
text: 'CML'
})
}
})
复制代码
示例代码
<script> class Index { data = { text: 'Chameleon' } methods = { changeText: function(e) { // sent data change to view this.text = 'CML'; } } computed = {} watch = {} }; export default new Index(); </script>
复制代码
在小程序项目里面, Component(Object)
构造器可用于定义组件,调用 Component
构造器时能够指定组件的属性、数据、方法等。
示例代码
Component({
properties: {
myProperty: { // 属性名
type: String, // 类型(必填)
value: '' // 属性初始值(可选)
},
myProperty2: String // 简化的定义方式
},
data: {
text: ''
}, // 私有数据,可用于模板渲染
// 生命周期函数,能够为函数,或一个在methods段中定义的方法名
attached() { },
ready() { },
methods: {
onMyButtonTap() {
this.setData({
// 更新属性和数据的方法与更新页面数据的方法相似
text: 'wx'
})
}
}
})
复制代码
示例代码
<script> class MyComponent { props = { myProperty: { // 属性名 type: String, // 类型(必填) default: '' // 属性初始值(可选) }, myProperty2: String // 简化的定义方式 } data = { text: '' } // 私有数据,可用于模板渲染 beforeMount() {} mounted() {} methods = { onMyButtonTap() { this.text = 'cml' } } computed = {} watch = {} }; export default new MyComponent(); </script>
复制代码
统一各端应用生命周期的定义,是跨端框架的重要组成,也是迁移的必经之路。
能够在 App(Object)
、Page(Object)
、Component(Object)
传入Object
参数,其指定小程序的生命周期回调等
代码示例
// index.js
Page({
onLoad(options) {
// Do some initialize when page load.
},
onReady() {
// Do something when page ready.
},
onShow() {
// Do something when page show.
},
onHide() {
// Do something when page hide.
},
onUnload() {
// Do something when page close.
},
onShareAppMessage() {
// return custom share data when user share.
}
})
复制代码
在.cml
文件 <script />
代码块返回的对象实例,其指定生命周期回调
示例代码
<script> class Index { beforeCreate(query) { // data数据挂载到this根节点上以前,以及methods全部方法挂载到实例根节点以前 // 注意:只用页面的 beforeCreate钩子 会返回页面query console.log('App beforeCreate: 打开当前页面路径中的参数是 ', query) } created() { // data,methods里面的这些events挂载完成 console.log('App created') } beforeMount() { // 开始挂载已经编译完成的cml到对应的节点时 console.log('App beforeMount') } mounted() { // cml模板编译完成,且渲染到dom中完成,在整个生命周期中只执行一次 console.log('App mounted') } beforeDestroy() { // 实例销毁前 console.log('App beforeDestroy') } destroyed() { // 实例销毁后 console.log('App destroyed') } }; export default new Index(); </script>
复制代码
小程序 app.js
中的生命周期 -> cml src/app/app.cml
小程序 | chameleon |
---|---|
onLaunch | beforeCreate |
onShow | mounted |
onHide | destroyed |
小程序 Page()
中的生命周期 -> cml src/pages/mypage/mypage.cml
小程序 | chameleon |
---|---|
onLoad | beforeCreate |
onShow | mounted |
onUnload | destroyed |
onReady | 生命周期多态 |
onHide | 生命周期多态 |
onShareAppMessage | 生命周期多态 |
小程序 Component()
中的生命周期 -> cml src/components/mycomponent/mycomponent.cml
小程序 | chameleon |
---|---|
created | created |
attached | beforeMount |
ready | mounted |
detached | destroyed |
每一个 cml
实例(App
、Page
、Component
)在被建立时都要通过一系列的初始化过程 ————
例如,须要设置数据监听、编译模板、将实例挂载到 CML节点
并在数据变化时更新 CML节点
等。同时在这个过程当中也会运行一些叫作生命周期钩子的函数,这给开发者在不一样阶段添加本身的代码的机会。
cml
为App
、页面Page
、组件Component
提供了一系列生命周期事件,保障应用有序执行。
另外,若是你想使用某个端特定的生命周期,你能够从业务出发使用 生命周期多态。
现在,双向数据绑定&单向数据流 已深刻开发者平常,MVMM开发模式算是框架标配。
示例代码
<!--wxml-->
<view class="scroller-wrap">
<!--数据绑定-->
<view>{{message}}</view>
<!--条件渲染-->
<view wx:if="{{view == 'WEBVIEW'}}">WEBVIEW</view>
<view wx:elif="{{view == 'APP'}}">APP</view>
<view wx:else="{{view == 'MINA'}}">MINA</view>
<!--列表渲染-->
<view wx:for="{{array}}" wx:for-index="index" wx:for-item="item">{{item}}</view>
</view>
复制代码
// page.js
Page({
data: {
message: 'Hello MINA!',
view: 'MINA',
array: [1, 2, 3, 4, 5]
},
onLoad() {
this.setData({
message: 'wx'
})
}
})
复制代码
<template>
<!--index.cml-->
<view class="scroller-wrap">
<!--数据绑定-->
<view>{{message}}</view>
<!--条件渲染-->
<view c-if="{{view == 'WEBVIEW'}}">WEBVIEW</view>
<view c-else-if="{{view == 'APP'}}">APP</view>
<view c-else="{{view == 'MINA'}}">MINA</view>
<!--列表渲染-->
<view c-for="{{array}}" c-for-index="index" c-for-item="item">{{item}}</view>
</view>
</template>
<script> class Index { data = { message: 'Hello MINA!', view: 'MINA', array: [1, 2, 3, 4, 5] } beforeCreate () { this.message = 'cml' } }; export default new Index(); </script>
复制代码
cml
运行时框架 提供了跨端响应式数据绑定系统(Data binding),当作数据修改的时候,只须要在逻辑层修改数据,视图层就会作相应的更新。
只须要将 view<-->model
交互部分逻辑,做简单迁移,即可使它成为跨多端的数据响应系统。
cml
支持一些基础的事件,保障各端效果(类型
、绑定
、事件对象
)一致运行。
示例代码
<!--wxml-->
<view id="tapTest" data-hi="WeChat" bindtap="tapName">Click me!</view>
复制代码
// page.js
Page({
tapName(event) {
console.log(event)
}
})
复制代码
<template>
<view id="tapTest" data-hi="WeChat" c-bind:tap="tapName">
<text>Click me!</text>
</view>
</template>
<script> class Index { methods = { tapName(e) { // 打印事件对象 console.log('事件对象:', e); } } } export default new Index(); </script>
复制代码
同时,还支持自定义事件,用于父子组件之间的通讯。
另外,若是你想要使用某个端特定的事件,cml
并不会限制你的自由发挥,你能够从业务出发使用 组件多态 或者 接口多态 差别化实现功能。
各端描述 布局和外观
的层叠样式表(CSS)实现存在差别,包括不限于 布局
、盒模型
、定位
、文本
。
因此, cml
框架内置跨端一致性基础样式能力。
而且,定义了用于描述页面的样式规范CMSS(Chameleon Style Sheet)。
使用 @import
语句能够导入外联样式表,@import
后跟须要导入的外联样式表的相对路径,用 ;
表示语句结束。
示例代码:
/** common.wxss **/
.small-p {
padding:5px;
}
复制代码
/** app.wxss **/
@import "common.wxss";
.middle-p {
padding:15px;
}
复制代码
示例代码:
/** common.css **/
.small-p {
padding: 5px;
}
复制代码
<!-- app.cml -->
<style> @import './common.css'; .middle-p { padding:15 cpx; } </style>
复制代码
同时,为了统一多端尺寸单位,呈现效果一致,同时页面响应式布局,cml
项目统一采用 cpx 做为尺寸单位,规定以屏幕750px(占满屏幕)视觉稿做为标准。
并且,各端样式表拥有的能力 不尽相同,是项目迁移的主要阵地之一。
另外,若是你想要使用某个端特定的样式能力,cml
并不会限制你的自由发挥,你能够从业务出发使用 样式多态
注意:因为chameleon应用是 跨多端web native 小程序
框架,若是须要跨native
,必须使用 flexbox 进行样式布局!!!
cml
项目一切皆组件。组件(Component)是视图的基本组成单元。
框架为开发者提供了一系列基础组件,开发者能够经过组合这些基础组件进行快速开发。
如:
<template>
<view>
<view>view 基础组件</view>
<text>text 基础组件</text>
</view>
</template>
复制代码
同时,cml
支持简洁的组件化编程。
开发者能够将页面内的功能模块抽象成自定义组件,以便在不一样的页面中重复使用。自定义组件在使用时与基础组件很是类似。
代码示例:
Component({
properties: {
// 这里定义了innerText属性,属性值能够在组件使用时指定
innerText: {
type: String,
value: 'default value',
}
},
data: {
// 这里是一些组件内部数据
someData: {}
},
methods: {
// 这里是一个自定义方法
customMethod() {}
}
})
复制代码
示例代码
<script> class MyComponent { props = { // 这里定义了innerText属性,属性值能够在组件使用时指定 innerText: { type: String, value: 'default value', } } data = { // 这里是一些组件内部数据 someData: {} } methods = { // 这里是一个自定义方法 customMethod() {} } computed = {} watch = {} }; export default new MyComponent(); </script>
复制代码
使用已注册的自定义组件前,首先要进行引用声明。此时须要提供每一个自定义组件的标签名和对应的自定义组件文件路径。
代码示例:
在 page.json
中进行引用声明
{
"usingComponents": {
"component-tag-name": "path/to/the/custom/component"
}
}
复制代码
在 page.wxml
中使用
<view>
<!-- 如下是对一个自定义组件的引用 -->
<component-tag-name inner-text="Some text"></component-tag-name>
</view>
复制代码
代码示例:
在 page.cml
中<script cml-type='json' />
进行引用声明
<script cml-type="json"> { "base": { "usingComponents": { "component-tag-name": "path/to/the/custom/component" } } } </script>
复制代码
在 page.cml
中<template />
使用
<template>
<view>
<!-- 如下是对一个自定义组件的引用 -->
<component-tag-name inner-text="Some text"></component-tag-name>
</view>
</template>
复制代码
事件系统是组件间通讯的主要方式之一。自定义组件能够触发任意的事件,引用组件的页面能够监听这些事件。
代码示例:
<!-- 页面 page.wxml -->
<view>
<my-component bindcustomevent="onMyEvent"></my-component>
</view>
复制代码
// 页面 page.js
Page({
methods: {
onMyEvent(e) {
console.log(e.detail) // 自定义组件触发事件时提供的detail对象
}
}
})
复制代码
<!-- 组件 my-component.wxml -->
<view>
<button bindtap="onTap">点击这个按钮将触发“myevent”事件</button>
</view>
复制代码
// 组件 my-component.js
Component({
methods: {
onTap() {
this.triggerEvent('customevent', {}) // 触发 自定义组件事件
}
}
})
复制代码
代码示例:
<!-- 页面 page.cml -->
<template>
<view>
<my-component c-bind:customevent="onMyEvent"></my-component>
</view>
</template>
<script> class Index { methods = { // 这里是一个自定义方法 onMyEvent(e) { console.log(e.detail) // 自定义组件触发事件时提供的detail对象 } } }; export default new Index(); </script>
<script cml-type="json"> { "base": { "usingComponents": { "my-component": "path/to/the/custom/component" } } } </script>
复制代码
<!-- 页面 path/to/the/custom/component.cml -->
<template>
<view>
<button c-bind:tap="onTap">点击这个按钮将触发“myevent”事件</button>
</view>
</template>
<script> class MyComponent { methods = { // 这里是一个自定义方法 onTap() { this.$cmlEmit('customevent', {}) // 触发 自定义组件事件 } } }; export default new MyComponent(); </script>
<script cml-type="json"> {} </script>
复制代码
和小程序同样,cml框架
提供了大量内置组件和扩展组件,抹平多端差别,便于开发者经过组合这些组件,建立出强大的应用程序。
扩展组件须要额外引入。如:
<script cml-type="json"> { "base": { "usingComponents": { "c-dialog": "cml-ui/components/c-dialog/c-dialog" } } } </script>
复制代码
在执行 cml build
构建打包时,cml 框架
会按需打包引用的内置组件和扩展组件,为代码瘦身。
内置组件和扩展组件 都是支持跨多端的,对于一些没有提供的某个端的组件,能够经过组件多态来实现。
若是但愿使用小程序端的原生组件,那么能够在原生标签前加上 origin-*
,cml
框架会渲染原生组件参考
注意:origin-*
只能在灰度区文件中使用!!
如在 map.wx.cml
文件中使用原生地图组件 <map/>
:
<!-- map.wx.cml -->
<template>
<origin-map id="map" longitude="113.324520" latitude="23.099994" controls="{{controls}}" bindcontroltap="controltap" style="width: 100%; height: 300px;" ></origin-map>
</template>
复制代码
在小程序里面,能够经过微信原生 API
,调起如获取用户信息,本地存储,支付功能等。
示例代码
try {
wx.setStorageSync('name', 'Hanks')
} catch (e) {
console.error(e)
}
复制代码
一样,在 cml 项目里面能够这样调用:
示例代码
import cml from 'chameleon-api'
cml.setStorage('name', 'Hanks').then((res)=>{
console.log(res)
},function(e){
console.error(e)
})
复制代码
cml
框架提供了丰富的多态接口,能够调起各端提供的原生能力,如系统信息、元素节点信息、动画效果、本地存储、网络请求、地理位置等。请参考 API 文档。
chameleon-api
提供的接口都是支持跨多端的,对于一些没有提供的某个端的原生接口,能够经过接口多态来调用。
下面给出各端(vue、weex、小程序)迁移cml指南
以及 cml 导出组件到各端指南
的具体迁移文档: