CML 作為真正讓一套代碼運(yùn)行多端的框架,提供標(biāo)準(zhǔn)的 MVVM 模式,統(tǒng)一開發(fā)各類終端。
同時(shí),擁有各端獨(dú)立的運(yùn)行時(shí)框架(Runtime)、數(shù)據(jù)管理(Store)、組件庫(UI)、接口(API)。
此外,CML 在跨端能力加強(qiáng)、能力統(tǒng)一、表現(xiàn)一致等方面做了許多工作。
今天,為了讓大家的項(xiàng)目優(yōu)雅升級(jí),快速接入,給你帶來一份豐盛的CML 遷移指南~
和微信小程序一樣,CML 包含一個(gè)描述整體程序的 app 和多個(gè)描述各自頁面的 pages。
.
├── components // 包含各個(gè)組件
├── pages // 包含各個(gè)頁面
├── app.js // 應(yīng)用啟動(dòng)入口
├── app.json // 全局配置
├── app.wxss // 全局樣式
└── project.config.json // 項(xiàng)目配置文件
.
├── dist // 各個(gè)端構(gòu)建結(jié)果
│ ├── tt
│ ├── qq
│ ├── alipay
│ ├── baidu
│ ├── wx
│ ├── web
│ ├── weex
│ └── config.json // 跨端配置map映射表
├── node_modules // 第三方庫
├── mock // 模擬 接口數(shù)據(jù) 和 模板數(shù)據(jù)
├── src // 源代碼開發(fā)目錄
│ ├── app // 應(yīng)用啟動(dòng)入口
│ ├── assets // 靜態(tài)資源
│ ├── components // 包含組件
│ ├── pages // 包含頁面
│ ├── store //數(shù)據(jù)管理
│ └── router.config.json // 路由配置文件
├── chameleon.config.js // 項(xiàng)目配置文件
└── package.json // npm包配置文件
在小程序項(xiàng)目里面,分為:
可以在項(xiàng)目根目錄使用 project.config.json 文件對(duì)項(xiàng)目進(jìn)行配置。
配置示例:
{
"miniprogramRoot": "./src",
"debugOptions": {}
}
小程序根目錄下的 app.json 文件用來對(duì)微信小程序進(jìn)行全局配置,決定頁面文件的路徑、窗口表現(xiàn)、設(shè)置網(wǎng)絡(luò)超時(shí)時(shí)間、設(shè)置多 tab 等
配置示例:
{
"pages": ["pages/index/index", "pages/logs/index"],
"window": {
"navigationBarTitleText": "Demo"
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
}
}
每一個(gè)小程序頁面也可以使用 .json 文件來對(duì)本頁面的窗口表現(xiàn)進(jìn)行配置。
頁面的配置只能設(shè)置 app.json 中部分 window 配置項(xiàng)的內(nèi)容,頁面中配置項(xiàng)會(huì)覆蓋 app.json 的 window 中相同的配置項(xiàng)。
配置示例:
{
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信接口功能演示",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}
同樣,在 CML 項(xiàng)目里面,分為:
chameleon.config.js 為項(xiàng)目的配置文件,你可以定制化構(gòu)建,比如是否帶 hash,是否 壓縮等等。
配置示例:
// 設(shè)置靜態(tài)資源的線上路徑
const publicPath = '//www.static.chameleon.com/static';
// 設(shè)置 API 請(qǐng)求前綴
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 項(xiàng)目 app 目錄下的 app.cml 文件的 <script cml-type="json" /> 用來對(duì) CML 應(yīng)用 進(jìn)行全局配置,具有跨端配置和差異化的能力
配置示例:
<script cml-type="json">
{
"base": {
"window": {
"navigationBarTitleText": "各個(gè)端共同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 配置 組件路徑 注冊(cè)引用的組件。
配置示例:
<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 配置項(xiàng)列表的 pages 字段用于指定小程序由哪些頁面組成,每一項(xiàng)都對(duì)應(yīng)一個(gè)頁面的 路徑+文件名 信息。
數(shù)組的第一項(xiàng)代表小程序的初始頁面(首頁)。新增/減少頁面,需要對(duì) pages 數(shù)組進(jìn)行修改。
如果項(xiàng)目有 pages/index/index.wxml、pages/logs/logs.wxml 兩個(gè)頁面,則需要在 app.json 中寫
{
"pages": ["pages/index/index", "pages/logs/logs"]
}
src/router.config.json 是路由的配置文件,CML 內(nèi)置了一套各端統(tǒng)一的路由配置方式。相應(yīng)有 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 框架會(huì)自動(dòng)去尋找對(duì)于位置的 .cml 文件進(jìn)行處理。
依據(jù)統(tǒng)一資源索引 URI,自適應(yīng)打開不同環(huán)境同一路由 PATH:
在小程序項(xiàng)目里面,App() 函數(shù)用來注冊(cè)一個(gè)小程序。接受一個(gè) Object 參數(shù),其指定小程序的生命周期回調(diào)等。
示例代碼
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>
細(xì)心的你會(huì)發(fā)現(xiàn),
小程序中app.json app.js app.wxss和 src/app/app.cml的對(duì)應(yīng)關(guān)系如下
小程序 app.js | CML 項(xiàng)目 src/app/app.cml |
---|---|
app.js | <script></script> |
app.wxss | <style></style> |
app.json | <script cml-type="json"></script> |
在小程序項(xiàng)目里面,Page(Object) 函數(shù)用來注冊(cè)一個(gè)頁面。接受一個(gè) Object 類型參數(shù),其指定頁面的初始數(shù)據(jù)、生命周期回調(diào)、事件處理函數(shù)等。
示例代碼:
// 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>
在小程序項(xiàng)目里面, Component(Object) 構(gòu)造器可用于定義組件,調(diào)用 Component 構(gòu)造器時(shí)可以指定組件的屬性、數(shù)據(jù)、方法等。
示例代碼
Component({
properties: {
myProperty: {
// 屬性名
type: String, // 類型(必填)
value: '', // 屬性初始值(可選)
},
myProperty2: String, // 簡化的定義方式
},
data: {
text: '',
}, // 私有數(shù)據(jù),可用于模板渲染
// 生命周期函數(shù),可以為函數(shù),或一個(gè)在methods段中定義的方法名
attached() {},
ready() {},
methods: {
onMyButtonTap() {
this.setData({
// 更新屬性和數(shù)據(jù)的方法與更新頁面數(shù)據(jù)的方法類似
text: 'wx',
});
},
},
});
示例代碼
<script>
class MyComponent {
props = {
myProperty: {
// 屬性名
type: String, // 類型(必填)
default: '', // 屬性初始值(可選)
},
myProperty2: String, // 簡化的定義方式
};
data = {
text: '',
}; // 私有數(shù)據(jù),可用于模板渲染
beforeMount() {}
mounted() {}
methods = {
onMyButtonTap() {
this.text = 'cml';
},
};
computed = {};
watch = {};
}
export default new MyComponent();
</script>
統(tǒng)一各端應(yīng)用生命周期的定義,是跨端框架的重要組成,也是遷移的必經(jīng)之路。
可以在 App(Object)、Page(Object)、Component(Object) 傳入Object參數(shù),其指定小程序的生命周期回調(diào)等
代碼示例
// 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 /> 代碼塊返回的對(duì)象實(shí)例,其指定生命周期回調(diào)
示例代碼
<script>
class Index {
beforeCreate(query) {
// data數(shù)據(jù)掛載到this根節(jié)點(diǎn)上之前,以及methods所有方法掛載到實(shí)例根節(jié)點(diǎn)之前
// 注意:只用頁面的 beforeCreate鉤子 會(huì)返回頁面query
console.log('App beforeCreate: 打開當(dāng)前頁面路徑中的參數(shù)是 ', query);
}
created() {
// data,methods里面的這些events掛載完成
console.log('App created');
}
beforeMount() {
// 開始掛載已經(jīng)編譯完成的cml到對(duì)應(yīng)的節(jié)點(diǎn)時(shí)
console.log('App beforeMount');
}
mounted() {
// cml模板編譯完成,且渲染到dom中完成,在整個(gè)生命周期中只執(zhí)行一次
console.log('App mounted');
}
beforeDestroy() {
// 實(shí)例銷毀前
console.log('App beforeDestroy');
}
destroyed() {
// 實(shí)例銷毀后
console.log('App destroyed');
}
}
export default new Index();
</script>
小程序 app.js中的生命周期 -> cml src/app/app.cml
小程序 | CML |
---|---|
onLaunch | beforeCreate |
onShow | mounted |
onHide | destroyed |
小程序 Page()中的生命周期 -> cml src/pages/mypage/mypage.cml
小程序 | CML |
---|---|
onLoad | beforeCreate |
onShow | mounted |
onUnload | destroyed |
onReady | 生命周期多態(tài) |
onHide | 生命周期多態(tài) |
onShareAppMessage | 生命周期多態(tài) |
小程序 Component()中的生命周期 -> cml src/components/mycomponent/mycomponent.cml
小程序 | CML |
---|---|
created | created |
attached | beforeMount |
ready | mounted |
detached | destroyed |
每個(gè) CML 實(shí)例(App、Page、Component)在被創(chuàng)建時(shí)都要經(jīng)過一系列的初始化過程 ————
例如,需要設(shè)置數(shù)據(jù)監(jiān)聽、編譯模板、將實(shí)例掛載到 CML 節(jié)點(diǎn)并在數(shù)據(jù)變化時(shí)更新 CML 節(jié)點(diǎn)等。同時(shí)在這個(gè)過程中也會(huì)運(yùn)行一些叫做生命周期鉤子的函數(shù),這給開發(fā)者在不同階段添加自己的代碼的機(jī)會(huì)。
CML 為 App、Page、Component 提供了一系列生命周期事件,保障應(yīng)用有序執(zhí)行。
另外,如果你想使用某個(gè)端特定的生命周期,你可以從業(yè)務(wù)出發(fā)使用生命周期多態(tài)。
如今,雙向數(shù)據(jù)綁定&單向數(shù)據(jù)流 已深入開發(fā)者日常,MVMM 開發(fā)模式算是框架標(biāo)配。
數(shù)據(jù)綁定、條件渲染、列表渲染是如何書寫的呢?
示例代碼
<!--wxml-->
<view class="scroller-wrap">
<!--數(shù)據(jù)綁定-->
<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">
<!--數(shù)據(jù)綁定-->
<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 運(yùn)行時(shí)框架 提供了跨端響應(yīng)式數(shù)據(jù)綁定系統(tǒng),當(dāng)做數(shù)據(jù)修改的時(shí)候,只需要在邏輯層修改數(shù)據(jù),視圖層就會(huì)做相應(yīng)的更新。
只需要將 view<-->model 交互部分邏輯,作簡單遷移,便可使它成為跨多端的數(shù)據(jù)響應(yīng)系統(tǒng)。
CML 支持一些基礎(chǔ)的事件,保障各端效果(類型、綁定、事件對(duì)象)一致運(yùn)行。
示例代碼
<!--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) {
// 打印事件對(duì)象
console.log('事件對(duì)象:', e);
},
};
}
export default new Index();
</script>
同時(shí),還支持自定義事件,用于父子組件之間的通信。
另外,如果你想要使用某個(gè)端特定的事件,CML 并不會(huì)限制你的自由發(fā)揮,你可以從業(yè)務(wù)出發(fā)使用多態(tài)組件 或者多態(tài)接口差異化實(shí)現(xiàn)功能。
各端描述 布局和外觀 的層疊樣式表(CSS)實(shí)現(xiàn)存在差異,包括不限于 布局、盒模型、定位、文本。
所以,CML 框架內(nèi)置跨端一致性基礎(chǔ)樣式能力。
并且,定義了用于描述頁面的樣式規(guī)范CMSS(Chameleon Style Sheet)。
使用 @import 語句可以導(dǎo)入外聯(lián)樣式表,@import 后跟需要導(dǎo)入的外聯(lián)樣式表的相對(duì)路徑,用 ; 表示語句結(jié)束。
示例代碼:
/** common.wxss **/
.small-p {
padding: 5px;
}
/** app.wxss **/
@import 'common.wxss';
.middle-p {
padding: 15px;
}
詳細(xì)文檔
示例代碼:
/** common.css **/
.small-p {
padding: 5px;
}
<!-- app.cml -->
<style>
@import './common.css';
.middle-p {
padding: 15 cpx;
}
</style>
同時(shí),為了統(tǒng)一多端尺寸單位,呈現(xiàn)效果一致,同時(shí)頁面響應(yīng)式布局,CML 項(xiàng)目統(tǒng)一采用 cpx 作為尺寸單位,規(guī)定以屏幕 750px(占滿屏幕)視覺稿作為標(biāo)準(zhǔn)。
而且,各端樣式表擁有的能力不盡相同,是項(xiàng)目遷移的主要陣地之一。
另外,如果你想要使用某個(gè)端特定的樣式能力,CML 并不會(huì)限制你的自由發(fā)揮,你可以從業(yè)務(wù)出發(fā)使用樣式多態(tài)
注意:由于 CML 應(yīng)用是跨多端 Web Native 小程序框架,如果需要跨 Native,必須使用flexbox進(jìn)行樣式布局?。?!
CML 項(xiàng)目一切皆組件。組件是視圖的基本組成單元。
框架為開發(fā)者提供了一系列基礎(chǔ)組件,開發(fā)者可以通過組合這些基礎(chǔ)組件進(jìn)行快速開發(fā)。
如:
<template>
<view>
<view>view 基礎(chǔ)組件</view>
<text>text 基礎(chǔ)組件</text>
</view>
</template>
同時(shí),CML 支持簡潔的組件化編程。
開發(fā)者可以將頁面內(nèi)的功能模塊抽象成自定義組件,以便在不同的頁面中重復(fù)使用。自定義組件在使用時(shí)與基礎(chǔ)組件非常相似。
代碼示例:
Component({
properties: {
// 這里定義了innerText屬性,屬性值可以在組件使用時(shí)指定
innerText: {
type: String,
value: 'default value',
},
},
data: {
// 這里是一些組件內(nèi)部數(shù)據(jù)
someData: {},
},
methods: {
// 這里是一個(gè)自定義方法
customMethod() {},
},
});
示例代碼
<script>
class MyComponent {
props = {
// 這里定義了innerText屬性,屬性值可以在組件使用時(shí)指定
innerText: {
type: String,
value: 'default value',
},
};
data = {
// 這里是一些組件內(nèi)部數(shù)據(jù)
someData: {},
};
methods = {
// 這里是一個(gè)自定義方法
customMethod() {},
};
computed = {};
watch = {};
}
export default new MyComponent();
</script>
使用已注冊(cè)的自定義組件前,首先要進(jìn)行引用聲明。此時(shí)需要提供每個(gè)自定義組件的標(biāo)簽名和對(duì)應(yīng)的自定義組件文件路徑。
代碼示例:
在 page.json 中進(jìn)行引用聲明
{
"usingComponents": {
"component-tag-name": "path/to/the/custom/component"
}
}
在 page.wxml 中使用
<view>
<!-- 以下是對(duì)一個(gè)自定義組件的引用 -->
<component-tag-name inner-text="Some text"></component-tag-name>
</view>
代碼示例:
在 page.cml中<script cml-type='json' />進(jìn)行引用聲明
<script cml-type="json">
{
"base": {
"usingComponents": {
"component-tag-name": "path/to/the/custom/component"
}
}
}
</script>
在 page.cml中<template />使用
<template>
<view>
<!-- 以下是對(duì)一個(gè)自定義組件的引用 -->
<component-tag-name inner-text="Some text"></component-tag-name>
</view>
</template>
事件系統(tǒng)是組件間通信的主要方式之一。自定義組件可以觸發(fā)任意的事件,引用組件的頁面可以監(jiān)聽這些事件。
代碼示例:
<!-- 頁面 page.wxml -->
<view>
<my-component bindcustomevent="onMyEvent"></my-component>
</view>
// 頁面 page.js
Page({
methods: {
onMyEvent(e) {
console.log(e.detail); // 自定義組件觸發(fā)事件時(shí)提供的detail對(duì)象
},
},
});
<!-- 組件 my-component.wxml -->
<view>
<button bindtap="onTap">點(diǎn)擊這個(gè)按鈕將觸發(fā)“myevent”事件</button>
</view>
// 組件 my-component.js
Component({
methods: {
onTap() {
this.triggerEvent('customevent', {}); // 觸發(fā) 自定義組件事件
},
},
});
代碼示例:
<!-- 頁面 page.cml -->
<template>
<view>
<my-component c-bind:customevent="onMyEvent"></my-component>
</view>
</template>
<script>
class Index {
methods = {
// 這里是一個(gè)自定義方法
onMyEvent(e) {
console.log(e.detail); // 自定義組件觸發(fā)事件時(shí)提供的detail對(duì)象
},
};
}
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">點(diǎn)擊這個(gè)按鈕將觸發(fā)“myevent”事件</button>
</view>
</template>
<script>
class MyComponent {
methods = {
// 這里是一個(gè)自定義方法
onTap() {
this.$cmlEmit('customevent', {}); // 觸發(fā) 自定義組件事件
},
};
}
export default new MyComponent();
</script>
<script cml-type="json">
{
}
</script>
和小程序一樣,cml框架 提供了大量內(nèi)置組件和擴(kuò)展組件,抹平多端差異,便于開發(fā)者通過組合這些組件,創(chuàng)建出強(qiáng)大的應(yīng)用程序。
擴(kuò)展組件需要額外引入。如:
<script cml-type="json">
{
"base": {
"usingComponents": {
"c-dialog": "cml-ui/components/c-dialog/c-dialog"
}
}
}
</script>
在執(zhí)行 cml build 構(gòu)建打包時(shí),cml 框架 會(huì)按需打包引用的內(nèi)置組件和擴(kuò)展組件,為代碼瘦身。
內(nèi)置組件和擴(kuò)展組件都是支持跨多端的,對(duì)于一些沒有提供的某個(gè)端的組件,可以通過多態(tài)組件來實(shí)現(xiàn)。
如果希望使用小程序端的原生組件,那么可以在原生標(biāo)簽前加上 origin-*,CML 框架會(huì)渲染原生組件。
注意:origin-* 只能在灰度區(qū)文件中使用??!
如在 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,調(diào)起如獲取用戶信息,本地存儲(chǔ),支付功能等。
示例代碼
try {
wx.setStorageSync('name', 'Hanks');
} catch (e) {
console.error(e);
}
同樣,在 CML 項(xiàng)目里面可以這樣調(diào)用:
示例代碼
import cml from 'chameleon-api';
cml.setStorage('name', 'Hanks').then(
(res) => {
console.log(res);
},
function(e) {
console.error(e);
},
);
CML 框架提供了豐富的多態(tài)接口,可以調(diào)起各端提供的原生能力,如系統(tǒng)信息、元素節(jié)點(diǎn)信息、動(dòng)畫效果、本地存儲(chǔ)、網(wǎng)絡(luò)請(qǐng)求、地理位置等。請(qǐng)參考API文檔。
chameleon-api 提供的接口都是支持跨多端的,對(duì)于一些沒有提供的某個(gè)端的原生接口,可以通過多態(tài)接口來調(diào)用。
CML 作為一端代碼運(yùn)行多端的框架,所有接口設(shè)計(jì)都考慮的是具備跨端要求的設(shè)計(jì),沒有使用任何一端的接口設(shè)計(jì)規(guī)范,而是全新一套框架,所以不要”想當(dāng)然“用微信小程序或者 vue 的接口來開發(fā) CML。
例如,如果你是微信小程序開發(fā)者,當(dāng)你想使用 tabbar 功能時(shí),可能會(huì)在app.json 里面配置,這是錯(cuò)誤的,這是微信特有模式,只在微信里面有效。在 cml 中請(qǐng)使用c-tabbar來實(shí)現(xiàn),這樣所有端都有效。
下面給出各端(vue、weex、小程序)遷移cml指南 以及 cml 導(dǎo)出組件到各端指南的具體遷移文檔:
更多建議: