出品 | 滴滴技術php
做者 | 許國棟css
▍前言:隨着各種小程序的百花齊放,業務對跨多端的需求愈來愈明顯。雖然各端環境變幻無窮,不管各種小程序、Weex、React-Native、Flutter、快應用,它們萬變不離其宗的是 MVVM 架構設計思想。今天,給你們帶來 Chameleon 遷移指南,一套代碼完成各端需求,滿滿乾貨~html
cml
做爲真正讓一套代碼運行多端的框架,提供標準的 MVVM 模式,統一開發各種終端。同時,擁有各端獨立的運行時框架 (runtime)、數據管理 (store)、組件庫 (ui)、接口 (api)。此外,cml
在跨端能力增強、能力統1、表現一致等方面作了許多工做。
前端
今天,爲了讓你們的項目優雅升級,快速接入,給你帶來一份豐盛的 cml 遷移指南。vue
視頻教程源碼地址:github.com/jalonjs/cml…
node
視頻教程地址:sfwb.didistatic.com/static/wb/5…webpack
▍閱讀索引git
目錄結構github
如何修改配置web
如何使用路由能力
如何註冊
如何聲明生命週期
數據如何響應到視圖
事件交互
佈局與外觀
自定義組件
如何實現父子組件事件通訊
組件使用總結
如何調用平臺接口能力
遷移實例
和微信小程序同樣,cml
包含一個描述總體程序的 app
和多個描述各自頁面的 page
。
1.
2├── components // 包含各個組件
3├── pages // 包含各個頁面
4├── app.js // 包含各個組件
5├── app.js // 應用啓動入口
6├── app.json // 全局配置
7├── app.wxss // 全局樣式
8└── project.config.json // 項目配置文件
複製代碼
1.
2├── dist // 各個端構建結果
3│ ├── alipay
4│ ├── baidu
5│ ├── wx
6│ ├── web
7│ ├── weex
8│ └── config.json // 跨端配置map映射表
9├── node_modules // 第三方庫
10├── mock // 模擬 接口數據 和 模板數據
11├── src // 源代碼開發目錄
12│ ├── app // 應用啓動入口
13│ ├── assets // 靜態資源
14│ ├── components // 包含組件
15│ ├── pages // 包含頁面
16│ ├── store //數據管理
17│ └── router.config.json // 路由配置文件
18├── chameleon.config.js // 項目配置文件
19└── package.json // npm包配置文件
複製代碼
在小程序項目裏面,分爲:
能夠在項目根目錄使用 project.config.json
文件對項目進行配置。
配置示例:
1{
2 "miniprogramRoot": "./src",
3 "debugOptions": {}
4}
複製代碼
小程序根目錄下的 app.json
文件用來對微信小程序進行全局配置,決定頁面文件的路徑、窗口表現、設置網絡超時時間、設置多 tab 等
配置示例:
1 {
2 "pages": ["pages/index/index", "pages/logs/index"],
3 "window": {
4 "navigationBarTitleText": "Demo"
5 },
6 "networkTimeout": {
7 "request": 10000,
8 "downloadFile": 10000
9 }
10}
複製代碼
每個小程序頁面也可使用 .json
文件來對本頁面的窗口表現進行配置。
頁面的配置只能設置 app.json
中部分 window
配置項的內容,頁面中配置項會覆蓋 app.json
的 window
中相同的配置項。
配置示例:
1{
2 "navigationBarBackgroundColor": "#ffffff",
3 "navigationBarTextStyle": "black",
4 "navigationBarTitleText": "微信接口功能演示",
5 "backgroundColor": "#eeeeee",
6 "backgroundTextStyle": "light"
7}
複製代碼
一樣,在 cml
項目裏面,分爲如下幾種配置方案:
chameleon.config.js
爲項目的配置文件,你能夠定製化構建,好比是否帶hash,是否壓縮等等。
配置示例:
1 // 設置靜態資源的線上路徑
2 const publicPath = '//www.static.chameleon.com/static';
3 // 設置api請求前綴
4 const apiPrefix = 'https://api.chameleon.com';
5 // 合併配置
6 cml.config.merge({
7 wx: {
8 build: {apiPrefix}
9 },
10 alipay: {
11 build: {apiPrefix}
12 },
13 baidu: {
14 build: {apiPrefix}
15 },
16 web: {
17 dev: {
18 hot: true,
19 console: true
20 },
21 build: {
22 publicPath: `${publicPath}/web`,
23 apiPrefix
24 }
25 },
26 weex: {
27 build: {
28 publicPath: `${publicPath}/weex`,
29 apiPrefix
30 }
31 }
32})
複製代碼
cml
項目 app
目錄下的 app.cml
文件的 <script cml-type="json" />
用來對 cml
應用 進行全局配置,具備 跨端配置 和 差別化 的能力。
配置示例:
1<script cml-type="json">
2{
3 "base": {
4 "window": {
5 "navigationBarTitleText": "各個端共同title",
6 },
7 "permission": {
8 "scope.userLocation": {
9 "desc": "你的位置信息將用於小程序位置接口的效果展現"
10 }
11 }
12 },
13 "wx": {
14 "window": {
15 "backgroundTextStyle":"light",
16 "navigationBarBackgroundColor": "#fff",
17 "navigationBarTitleText": "差別化 title",
18 "navigationBarTextStyle":"black"
19 }
20 },
21 "baidu": {
22 "window": {
23 "backgroundTextStyle": "light"
24 }
25 },
26 "alipay": {
27 "window": {
28 "defaultTitle": "Chameleon"
29 }
30 }
31}
32</script>
複製代碼
經過 usingComponents
配置 組件路徑
註冊引用的組件。
配置示例:
1 <script cml-type="json">
2 {
3 "base": {
4 "usingComponents": {
5 "navi": "/components/navi/navi",
6 "navi-npm": "cml-test-ui/navi/navi"
7 }
8 },
9 "wx": {
10 },
11 "alipay": {
12 },
13 "baidu": {
14 },
15 "web": {
16 },
17 "weex": {
18 }
19}
20</script>
複製代碼
app.json 配置項列表的 pages
字段用於指定小程序由哪些頁面組成,每一項都對應一個頁面的 路徑+文件名
信息。
數組的第一項表明小程序的初始頁面(首頁)。新增/減小頁面,須要對 pages
數組進行修改。
若是項目有 pages/index/index.wxml
、pages/logs/logs.wxml
兩個頁面,則須要在 app.json
中寫。
1{
2 "pages": ["pages/index/index", "pages/logs/logs"]
3}
複製代碼
src/router.config.json 是路由的配置文件,cml 內置了一套各端統一的路由管理方式。相應有 cml 路由配置映射以下:
1 {
2 "mode": "history",
3 "domain": "https://www.chameleon.com",
4 "routes":[
5 {
6 "url": "/cml/h5/index",
7 "path": "/pages/index/index",
8 "mock": "index.php"
9 },
10 {
11 "url": "/cml/h5/logs",
12 "path": "pages/logs/logs",
13 "mock": "logs.php"
14 }
15 ]
16}
複製代碼
文件名不須要寫文件後綴,cml
框架會自動去尋找對於位置的 .cml
文件進行處理。
打開新頁面:調用 API
wx.navigateTo
頁面重定向:調用 API
wx.redirectTo
頁面返回:調用 API
wx.navigateBack
打開另外一個小程序:調用 API
wx.navigateToMiniProgram
返回到上一個小程序:調用 API
wx.navigateBackMiniProgram
依據統一資源索引URI,自適應打開不一樣環境同一路由PATH:
打開新頁面:調用 chameleon-api
cml.navigateTo
頁面重定向:調用 chameleon-api
cml.redirectTo
頁面返回:調用 chameleon-api
cml.navigateBack
打開另外一個跨端應用:調用 chameleon-api
cml.open
返回到上一個跨端應用:調用 chameleon-api
cml.close
在小程序項目裏面,App()
函數用來註冊一個小程序。接受一個 Object
參數,其指定小程序的生命週期回調等。
示例代碼:
1 App({
2 onLaunch(options) {
3 // Do something initial when launch.
4 },
5 globalData: 'I am global data'
6})
複製代碼
示例代碼:
1 <script>
2 import store from '../store/index.js'
3 import routerConfig from '../router.config.json';
4
5 class App {
6 data = {
7 store,
8 routerConfig
9 }
10 created(res) {
11 }
12 }
13
14 export default new App();
15 </script>
複製代碼
細心的你會發現,小程序中app.json app.js app.wxss
和 src/app/app.cml
的對應關係以下:
在小程序項目裏面,Page(Object)
函數用來註冊一個頁面。接受一個 Object
類型參數,其指定頁面的初始數據、生命週期回調、事件處理函數等。
示例代碼:
1 // index.js
2 Page({
3 data: {
4 text: 'This is page data.'
5 },
6 changeText: function(e) {
7 // sent data change to view
8 this.setData({
9 text: 'CML'
10 })
11 }
12})
複製代碼
示例代碼:
1 <script>
2 class Index {
3 data = {
4 text: 'Chameleon'
5 }
6 methods = {
7 changeText: function(e) {
8 // sent data change to view
9 this.text = 'CML';
10 }
11 }
12 computed = {}
13 watch = {}
14 };
15 export default new Index();
16 </script>
複製代碼
在小程序項目裏面,
Component(Object)
構造器可用於定義組件,調用 Component
構造器時能夠指定組件的屬性、數據、方法等。
示例代碼:
1 Component({
2 properties: {
3 myProperty: { // 屬性名
4 type: String, // 類型(必填)
5 value: '' // 屬性初始值(可選)
6 },
7 myProperty2: String // 簡化的定義方式
8 },
9 data: {
10 text: ''
11 }, // 私有數據,可用於模板渲染
12
13 // 生命週期函數,能夠爲函數,或一個在methods段中定義的方法名
14 attached() { },
15 ready() { },
16 methods: {
17 onMyButtonTap() {
18 this.setData({
19 // 更新屬性和數據的方法與更新頁面數據的方法相似
20 text: 'wx'
21 })
22 }
23 }
24})
複製代碼
示例代碼:
1 <script>
2 class MyComponent {
3 props = {
4 myProperty: { // 屬性名
5 type: String, // 類型(必填)
6 default: '' // 屬性初始值(可選)
7 },
8 myProperty2: String // 簡化的定義方式
9 }
10 data = {
11 text: ''
12 } // 私有數據,可用於模板渲染
13
14 beforeMount() {}
15 mounted() {}
16 methods = {
17 onMyButtonTap() {
18 this.text = 'cml'
19 }
20 }
21 computed = {}
22 watch = {}
23 };
24 export default new MyComponent();
25 </script>
複製代碼
統一各端應用生命週期的定義,是跨端框架的重要組成,也是遷移的必經之路。
能夠在 App(Object)
、Page(Object)
、Component(Object)
傳入Object
參數,其指定小程序的生命週期回調等。
代碼示例:
1 // index.js
2 Page({
3 onLoad(options) {
4 // Do some initialize when page load.
5 },
6 onReady() {
7 // Do something when page ready.
8 },
9 onShow() {
10 // Do something when page show.
11 },
12 onHide() {
13 // Do something when page hide.
14 },
15 onUnload() {
16 // Do something when page close.
17 },
18 onShareAppMessage() {
19 // return custom share data when user share.
20 }
21})
複製代碼
在.cml
文件 <script />
代碼塊返回的對象實例,其指定生命週期回調。
示例代碼:
1 <script>
2 class Index {
3 beforeCreate(query) {
4 // data數據掛載到this根節點上以前,以及methods全部方法掛載到實例根節點以前
5 // 注意:只用頁面的 beforeCreate鉤子 會返回頁面query
6 console.log('App beforeCreate: 打開當前頁面路徑中的參數是 ', query)
7 }
8 created() {
9 // data,methods裏面的這些events掛載完成
10 console.log('App created')
11 }
12 beforeMount() {
13 // 開始掛載已經編譯完成的cml到對應的節點時
14 console.log('App beforeMount')
15 }
16 mounted() {
17 // cml模板編譯完成,且渲染到dom中完成,在整個生命週期中只執行一次
18 console.log('App mounted')
19 }
20 beforeDestroy() {
21 // 實例銷燬前
22 console.log('App beforeDestroy')
23 }
24 destroyed() {
25 // 實例銷燬後
26 console.log('App destroyed')
27 }
28 };
29 export default new Index();
30 </script>
複製代碼
小程序 app.js
中的生命週期 -> cml src/app/app.cml
小程序 Page()
中的生命週期 -> cml src/pages/mypage/mypage.cml
小程序 Component()
中的生命週期 -> cml src/components/mycomponent/mycomponent.cml
每一個 cml
實例( App
、Page
、Component
)在被建立時都要通過一系列的初始化過程。
例如,須要設置數據監聽、編譯模板、將實例掛載到 CML節點
並在數據變化時更新 CML節點
等。同時在這個過程當中也會運行一些叫作生命週期鉤子的函數,這給開發者在不一樣階段添加本身的代碼的機會。
cml
爲 App
、頁面 Page
、組件 Component
提供了一系列生命週期事件,保障應用有序執行。
另外,若是你想使用某個端特定的生命週期,你能夠從業務出發使用 生命週期多態。
現在,雙向數據綁定&單向數據流 已深刻開發者平常,MVMM開發模式算是框架標配。
示例代碼:
1 <template>
2 <!--index.cml-->
3 <view class="scroller-wrap">
4 <!--數據綁定-->
5 <view>{{message}}</view>
6 <!--條件渲染-->
7 <view c-if="{{view == 'WEBVIEW'}}">WEBVIEW</view>
8 <view c-else-if="{{view == 'APP'}}">APP</view>
9 <view c-else="{{view == 'MINA'}}">MINA</view>
10 <!--列表渲染-->
11 <view c-for="{{array}}" c-for-index="index" c-for-item="item">{{item}}</view>
12 </view>
13 </template>
14 <script>
15 class Index {
16 data = {
17 message: 'Hello MINA!',
18 view: 'MINA',
19 array: [1, 2, 3, 4, 5]
20 }
21
22 beforeCreate () {
23 this.message = 'cml'
24 }
25 };
26 export default new Index();
27 </script>
複製代碼
cml
運行時框架 提供了跨端響應式數據綁定系統(Data binding),當作數據修改的時候,只須要在邏輯層修改數據,視圖層就會作相應的更新。
只須要將 view<-->model
交互部分邏輯,做簡單遷移,即可使它成爲跨多端的數據響應系統。
cml
支持一些基礎的事件,保障各端效果(類型
、綁定
、事件對象
)一致運行。
示例代碼:
1<!--wxml-->
2<view id="tapTest" data-hi="WeChat" bindtap="tapName">Click me!</view>
複製代碼
1// page.js
2Page({
3 tapName(event) {
4 console.log(event)
5 }
6})
複製代碼
1 <template>
2 <view id="tapTest" data-hi="WeChat" c-bind:tap="tapName">
3 <text>Click me!</text>
4 </view>
5 </template>
6 <script>
7 class Index {
8 methods = {
9 tapName(e) {
10 // 打印事件對象
11 console.log('事件對象:', e);
12 }
13 }
14}
15 export default new Index();
16 </script>
複製代碼
同時,還支持自定義事件,用於父子組件之間的通訊。
另外,若是你想要使用某個端特定的事件,cml
並不會限制你的自由發揮,你能夠從業務出發使用 組件多態 或者 接口多態 差別化實現功能。
各端描述 佈局和外觀 的層疊樣式表(CSS)實現存在差別,包括不限於 佈局、盒模型、定位、文本。
因此, cml 框架內置跨端一致性基礎樣式能力。
而且,定義了用於描述頁面的樣式規範CMSS(Chameleon Style Sheet)。
使用 @import 語句能夠導入外聯樣式表,@import 後跟須要導入的外聯樣式表的相對路徑,用 ;
表示語句結束。
示例代碼:
示例代碼:
同時,爲了統一多端尺寸單位,呈現效果一致,同時頁面響應式佈局,cml
項目統一採用 cpx 做爲尺寸單位,規定以屏幕750px(佔滿屏幕)視覺稿做爲標準。
並且,各端樣式表擁有的能力 不盡相同,是項目遷移的主要陣地之一。
另外,若是你想要使用某個端特定的樣式能力,cml
並不會限制你的自由發揮,你能夠從業務出發使用 樣式多態
注意:因爲chameleon應用是 跨多端web native 小程序
框架,若是須要跨native
,必須使用 flexbox 進行樣式佈局。
開發者能夠將頁面內的功能模塊抽象成自定義組件,以便在不一樣的頁面中重複使用。自定義組件在使用時與基礎組件很是類似。
代碼示例:
1 Component({
2 properties: {
3 // 這裏定義了innerText屬性,屬性值能夠在組件使用時指定
4 innerText: {
5 type: String,
6 value: 'default value',
7 }
8 },
9 data: {
10 // 這裏是一些組件內部數據
11 someData: {}
12 },
13 methods: {
14 // 這裏是一個自定義方法
15 customMethod() {}
16 }
17})
複製代碼
示例代碼:
1 <script>
2 class MyComponent {
3 props = {
4 // 這裏定義了innerText屬性,屬性值能夠在組件使用時指定
5 innerText: {
6 type: String,
7 value: 'default value',
8 }
9 }
10 data = {
11 // 這裏是一些組件內部數據
12 someData: {}
13 }
14 methods = {
15 // 這裏是一個自定義方法
16 customMethod() {}
17 }
18 computed = {}
19 watch = {}
20};
21export default new MyComponent();
22</script>
複製代碼
使用已註冊的自定義組件前,首先要進行引用聲明。此時須要提供每一個自定義組件的標籤名和對應的自定義組件文件路徑。
代碼示例:
在 page.json
中進行引用聲明
1{
2 "usingComponents": {
3 "component-tag-name": "path/to/the/custom/component"
4 }
5}
複製代碼
在 page.wxml
中使用
1 <view>
2 <!-- 如下是對一個自定義組件的引用 -->
3 <component-tag-name inner-text="Some text"></component-tag-name>
4 </view>
複製代碼
代碼示例:
在 page.cml
中<script cml-type='json' />
進行引用聲明
1<script cml-type="json">
2{
3 "base": {
4 "usingComponents": {
5 "component-tag-name": "path/to/the/custom/component"
6 }
7 }
8}
9</script>
複製代碼
在 page.cml
中<template />
使用
1<template>
2<view>
3 <!-- 如下是對一個自定義組件的引用 -->
4 <component-tag-name inner-text="Some text"></component-tag-name>
5</view>
6</template>
複製代碼
事件系統是組件間通訊的主要方式之一。自定義組件能夠觸發任意的事件,引用組件的頁面能夠監聽這些事件。
代碼示例:
代碼示例:
和小程序同樣,cml框架
提供了大量內置組件和擴展組件,抹平多端差別,便於開發者經過組合這些組件,建立出強大的應用程序。
擴展組件須要額外引入。如:
1<script cml-type="json">
2{
3 "base": {
4 "usingComponents": {
5 "c-dialog": "cml-ui/components/c-dialog/c-dialog"
6 }
7 }
8}
9</script>
複製代碼
在執行 cml build
構建打包時,cml 框架
會按需打包引用的內置組件和擴展組件,爲代碼瘦身。
內置組件和擴展組件 都是支持跨多端的,對於一些沒有提供的某個端的組件,能夠經過組件多態來實現。
若是但願使用小程序端的原生組件,那麼能夠在原生標籤前加上 origin-*
,cml
框架會渲染原生組件參考。
注意:origin-*
只能在灰度區文件中使用。
如在 map.wx.cml
文件中使用原生地圖組件 <map/>
:
1 <!-- map.wx.cml -->
2 <template>
3 <origin-map
4 id="map"
5 longitude="113.324520"
6 latitude="23.099994"
7 controls="{{controls}}"
8 bindcontroltap="controltap"
9 style="width: 100%; height: 300px;"
10 ></origin-map>
11 </template>
複製代碼
在小程序裏面,能夠經過微信原生 API
,調起如獲取用戶信息,本地存儲,支付功能等。
示例代碼:
1try {
2 wx.setStorageSync('name', 'Hanks')
3} catch (e) {
4 console.error(e)
5}
複製代碼
一樣,在 cml 項目裏面能夠這樣調用:
示例代碼:
1import cml from 'chameleon-api'
2cml.setStorage('name', 'Hanks').then((res)=>{
3 console.log(res)
4},function(e){
5 console.error(e)
6})
複製代碼
cml
框架提供了豐富的多態接口,能夠調起各端提供的原生能力,如系統信息、元素節點信息、動畫效果、本地存儲、網絡請求、地理位置等。請參考 API 文檔。
chameleon-api
提供的接口都是支持跨多端的,對於一些沒有提供的某個端的原生接口,能夠經過接口多態來調用。
下面給出各端(vue、weex、小程序)遷移cml指南
以及 cml 導出組件到各端指南
的具體遷移文檔:
點擊圖片瞭解更多:
有關安裝、使用過程以及常見問題解答,請查看如下連接:
GitHub:github.com/didi/chamel…
同時歡迎加入「Chameleon 用戶交流羣」
請在滴滴技術公衆號後臺回覆「Chameleom」便可加入
許 國 棟
滴滴 | 高級軟件開發工程師
Chameleon 成員,主要負責框架運行時、組件生態化等相關開發工做。喜歡專研前端技術,對領域前沿技術感興趣。有跨端相關的看法的同窗,歡迎來相互探討。