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