cml
作為真正讓一套程式碼執行多端的框架,提供標準的MVVM模式,統一開發各類終端。
同時,擁有各端獨立的 執行時框架(runtime)
、資料管理(store)
、元件庫(ui)
、介面(api)
。
此外,cml
在跨端能力加強
、能力統一
、表現一致
等方面做了許多工作。
今天,為了讓大家的專案優雅升級,快速接入,給你帶來一份豐盛的cml遷移指南~
原始碼地址:github.com/jalonjs/cml…
目錄結構
和微信小程式一樣,cml
包含一個描述整體程式的 app
和多個描述各自頁面的 page
。
小程式目錄結構
.
├── components // 包含各個元件
├── pages // 包含各個頁面
├── app.js // 包含各個元件
├── app.js // 應用啟動入口
├── app.json // 全域性配置
├── app.wxss // 全域性樣式
└── project.config.json // 專案配置檔案
複製程式碼
cml目錄結構
.
├── 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包配置檔案
複製程式碼
如何修改配置
在小程式專案裡面,分為:
小程式 —— 專案配置
可以在專案根目錄使用 project.config.json
檔案對專案進行配置。
配置示例:
{
"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
專案裡面,分為:
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 —— 全域性配置
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>
複製程式碼
cml —— 頁面/元件配置
通過 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"]
}
複製程式碼
cml配置路由
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
檔案進行處理。
小程式使用路由
- 開啟新頁面:呼叫 API wx.navigateTo
- 頁面重定向:呼叫 API wx.redirectTo
- 頁面返回:呼叫 API wx.navigateBack
- 開啟另一個小程式:呼叫 API wx.navigateToMiniProgram
- 返回到上一個小程式:呼叫 API wx.navigateBackMiniProgram
cml使用路由
依據統一資源索引URI,自適應開啟不同環境同一路由PATH:
- 開啟新頁面:呼叫 chameleon-api cml.navigateTo
- 頁面重定向:呼叫 chameleon-api cml.redirectTo
- 頁面返回:呼叫 chameleon-api cml.navigateBack
- 開啟另一個跨端應用:呼叫 chameleon-api cml.open
- 返回到上一個跨端應用:呼叫 chameleon-api cml.close
如何註冊
如何註冊程式
小程式註冊程式
在小程式專案裡面,App()
函式用來註冊一個小程式。接受一個 Object
引數,其指定小程式的生命週期回撥等。
示例程式碼
App({
onLaunch(options) {
// Do something initial when launch.
},
globalData: 'I am global data'
})
複製程式碼
cml註冊程式
示例程式碼
<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'
})
}
})
複製程式碼
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'
})
}
}
})
複製程式碼
cml註冊元件
示例程式碼
<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宣告生命週期
在.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 生命週期 對映
小程式 app.js
中的生命週期 -> cml src/app/app.cml
小程式 | chameleon |
---|---|
onLaunch | beforeCreate |
onShow | mounted |
onHide | destroyed |
Page 生命週期 對映
小程式 Page()
中的生命週期 -> cml src/pages/mypage/mypage.cml
小程式 | chameleon |
---|---|
onLoad | beforeCreate |
onShow | mounted |
onUnload | destroyed |
onReady | 生命週期多型 |
onHide | 生命週期多型 |
onShareAppMessage | 生命週期多型 |
Component 生命週期 對映
小程式 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'
})
}
})
複製程式碼
cml使用資料響應
<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)
}
})
複製程式碼
cml使用事件
<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;
}
複製程式碼
cml匯入外部樣式
示例程式碼:
/** 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() {}
}
})
複製程式碼
cml建立自定義元件
示例程式碼
<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>
複製程式碼
cml使用自定義元件
程式碼示例:
在 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', {}) // 觸發 自定義元件事件
}
}
})
複製程式碼
cml元件通訊
程式碼示例:
<!-- 頁面 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 匯出元件到各端指南
的具體遷移文件: