Chameleon:原来写一个跨多端项目,能够这么容易!

出品 | 滴滴技术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 // 项目配置文件
复制代码

▍cml目录结构

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.jsonwindow 中相同的配置项。

配置示例:

1{
2  "navigationBarBackgroundColor": "#ffffff",
3  "navigationBarTextStyle": "black",
4  "navigationBarTitleText": "微信接口功能演示",
5  "backgroundColor": "#eeeeee",
6  "backgroundTextStyle": "light"
7}
复制代码

一样,在 cml项目里面,分为如下几种配置方案:

▍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 — 全局设置

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>
复制代码

▍cml — 页面 / 组件配置

经过 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.wxmlpages/logs/logs.wxml 两个页面,则须要在 app.json 中写。

1{
2  "pages": ["pages/index/index", "pages/logs/logs"]
3}
复制代码

▍cml 配置路由

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 文件进行处理。

▍小程序使用路由

▍cml 使用路由

依据统一资源索引URI,自适应打开不一样环境同一路由PATH:

▍如何注册

▍小程序注册应用

在小程序项目里面,App() 函数用来注册一个小程序。接受一个 Object 参数,其指定小程序的生命周期回调等。

示例代码:

1 App({
2  onLaunch(options) {
3    // Do something initial when launch.
4  },
5  globalData: 'I am global data'
6})
复制代码

▍cml 注册应用

示例代码:

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.wxsssrc/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})
复制代码

▍cml 注册页面

示例代码:

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})
复制代码

▍cml 注册组件

示例代码:

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 声明生命周期

.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 生命周期映射

小程序 app.js中的生命周期 -> cml src/app/app.cml

▍Page 生命周期映射

小程序 Page()中的生命周期 -> cml src/pages/mypage/mypage.cml

  1. 小程序                                                            chameleon
  2. onLoad                                                           beforeCreate
  3. onShow                                                          mounted
  4. onUnload                                                       destroyed
  5. onReady                                                         生命周期多态
  6. onHide                                                            生命周期多态
  7. onShareAppMessage                                    生命周期多态

▍Component 生命周期映射

小程序 Component()中的生命周期 -> cml src/components/mycomponent/mycomponent.cml


▍生命周期总结

每一个 cml 实例( AppPageComponent )在被建立时都要通过一系列的初始化过程。

例如,须要设置数据监听、编译模板、将实例挂载到 CML节点 并在数据变化时更新 CML节点 等。同时在这个过程当中也会运行一些叫作生命周期钩子的函数,这给开发者在不一样阶段添加本身的代码的机会。

cmlApp页面 Page组件 Component 提供了一系列生命周期事件,保障应用有序执行。

另外,若是你想使用某个端特定的生命周期,你能够从业务出发使用 生命周期多态

▍数据如何响应到视图

现在,双向数据绑定&单向数据流 已深刻开发者平常,MVMM开发模式算是框架标配。

数据绑定条件渲染列表渲染是如何书写的呢?

示例代码:

▍小程序使用数据响应用


▍cml 使用数据响应

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})
复制代码

▍cml 使用事件

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 导入外部样式

详细文档

示例代码:


▍样式使用总结

同时,为了统一多端尺寸单位,呈现效果一致,同时页面响应式布局,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})
复制代码

▍cml 建立自定义组件

示例代码:

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>
复制代码

▍cml 使用自定义组件

代码示例:

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 组件通讯

代码示例:


▍组件使用总结

和小程序同样,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…

快速开始:cml.js.org/doc/quick_s…


同时欢迎加入「Chameleon 用户交流群」
请在滴滴技术公众号后台回复「Chameleom」便可加入


▍END


                                    


                                                                               许 国 栋

                                                        滴滴 | 高级软件开发工程师

Chameleon 成员,主要负责框架运行时、组件生态化等相关开发工做。喜欢专研前端技术,对领域前沿技术感兴趣。有跨端相关的看法的同窗,欢迎来相互探讨。

相关文章
相关标签/搜索