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 導出組件到各端指南
的具體遷移文檔: