滴滴跨端框架 Chameleon 正式支持快应用
小编推荐:抗击疫情,提醒大家出门带好口罩,勤洗手,多通风。武汉加油!中国加油!在大家开工之际,邀您阅读来滴滴开源专栏内容,为你分享滴滴跨端框架 Chameleon 的最新分享。
0.
目录
进入chameleon项目页
(https://github.com/didi/chameleon/)
快应用简介
快应用技术方案
快应用平台服务架构
快应用技术架构
对比小程序
参与方
新端扩展配置化实现
实现cml-quickapp-plugin
实现cml-quickapp-runtime
实现cml-quickapp-api
实现cml-quickapp-store
实现cml-quickapp-ui-builtin和实现cml-quickapp-ui
体验快应用
接入快应用
全局安装最新的 chameleon-tool 工具
启动快应用服务
安装和更新 package
修改 app.cml
修改 chameleon.config.js 配置文件
修改项目代码
快应用启动命令
1.
背景
2.
造型与思考
chameleon-tool 最新版本对分包进行了更大的细化,主包体积有效的较之前减少了40% 左右;具体参考 chameleon-分包(https://github.com/beatles-chameleon/cml-subpage)
得益于chameleon的灵活的工程化配置,chameleon可以灵活的支持微信小程序的云开发,具体参考 chameleon支持云开发(https://github.com/beatles-chameleon/cml-cloud)
3.
快应用介绍
▍快应用简介
快应用是基于手机硬件平台的新型应用形态,覆盖 10 亿安卓设备;
快应用标准是由主流手机厂商组成的快应用联盟联合制定;
快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放;
快应用具备传统 APP 完整的应用体验,无需安装、即点即用;
▍快应用技术方案
使⽤前端技术栈(Html / CSS / JavaScript);
使⽤前端主流的模版+数据绑定框架编写代码(Vue.js);
平台提供⼤量扩展能⼒,包括系统能⼒和服务集成能⼒;
默认使⽤原⽣ JS-Native 渲染,达到原⽣应⽤的性能和体验;
<template>
<!-- template里只能有一个根节点 -->
<div class="container">
<text class="title">测一测{{ title }}</text>
</div>
</template>
<style>
.container {
flex-direction: column;
justify-content: center;
}
.title {
font-size: 40px;
text-align: center;
}
</style>
<script>
export default {
data: {
title: '快应用'
}
}
</script>
▍快应用平台服务架构
Platform Service: 平台服务,提供如何存储,场景接口,分发下载,数据统计 —————— Developer Service: 开发者服务,提供开发者文档及相关工具(IDE,Sign等) —————— Application Platform: 小程序运行平台,包含运行时,沙箱运行环境,小程序管理等 —————— EntranceApp: 接入小程序平台sdk的应用,如商店等 —————— Application: 具体小程序 |
▍快应用技术架构
App: 具体小程序,采用vue.js框架语法 ———————————— JS Framework: JS框架 ———————————— JS Engine: JS引擎,目前采用Google V8 ———————————— JS-Native Bridge: JS Native通信,目前采用开源J2V8方案 |
▍对比小程序
目前小程序普遍采用 Webview 方案进行渲染,快应用则基于原生 JS-Native 进行渲染,两者对比如下:
4.
新端扩展快应用—原理篇
▍参与方
@zheyizhifeng
@whuhenrylee
@TingKun
@sgoddon
@jimwmg
@chen28683
@eraychen1984
@wanglikun7342
@yylgit
@MicroConan
▍参与方
entry: {
app: path.join(cml.projectRoot, 'src/app/app.cml')
},
module: {
rules: [
...utils.styleLoaders({
type
}),
{
test: /\.cml$/,
use: [{
loader: 'mvvm-cml-loader',
options: {
loaders: getCmlLoaders(),
cmlType: type,
media,
check: cml.config.get().check
}
}]
}
]
},
plugins: [
new MvvmGraphPlugin({
cmlType: type,
media
}, platformPlugin)
]
compiler.plugin('should-emit', function(compilation) {
try {
mvvmCompiler.run(compilation.modules);
} catch (e) {
cml.log.error(e);
}
// 返回false 不进入emit阶段
return false;
})
▍实现cml-quickapp-plugin
module.exports = class DemoPlugin {
constructor(options) {
......
}
/**
* @description 注册插件
* @param {compiler} 编译对象
* */
register(compiler) {
// 编译script节点,比如做模块化
compiler.hook('compile-script', function (currentNode, parentNodeType) {})
// 编译template节点 语法转义
compiler.hook('compile-template', function (currentNode, parentNodeType) {})
// 编译style节点 比如尺寸单位转义
compiler.hook('compile-style', function (currentNode, parentNodeType) {})
// 编译结束进入打包阶段
compiler.hook('pack', function (projectGraph) {
// 遍历编译图的节点,进行各项目的拼接
// 调用writeFile方法写入文件
// compiler.writeFile()
})
......
}
}
以扩展快应用的 template 编译为例。判断原始 CML 文件中的依赖关系,改写成快应用 <import ... ></import> 的写法。
compiler.hook("compile-template", function (currentNode, parentNodeType) {
let {componentFiles, uximports} = currentNode.parent.extra || {};
currentNode.output = templateParser(currentNode.source);
let components = '';
if(componentFiles) {
Object.keys(componentFiles).forEach(key=>{
let targetEntry = cmlUtils.getPureEntryName(componentFiles[key], self.cmlType, cml.projectRoot);
let sourceEntry = cmlUtils.getPureEntryName(currentNode.realPath, self.cmlType, cml.projectRoot);
let relativePath = cmlUtils.handleRelativePath(sourceEntry, targetEntry);
components += `<import name='${key}' src='${relativePath}'></import>\n`
})
// 记录引入的组件
currentNode.importComponents = components;
} else if(uximports) {
uximports.forEach(item=>{
components += `<import name='${item.name}' src='${item.src}'></import>\n`
})
// 记录引入的组件
currentNode.importComponents = components;
}
});
▍实现cml-quickapp-runtime
// 左侧的 key 为 CML 生命周期函数,右侧的 value 为快应用对应的生命周期函数
{
app: {
beforeCreate: "onCreate",
created: "onCreate",
beforeMount: "onCreate",
mounted: "onCreate",
beforeDestroy: "onCreate",
destroyed: "onDestroy"
},
page: {
beforeCreate: "onInit",
created: "onInit",
beforeMount: "onInit",
mounted: "onReady",
beforeDestroy: "onHide",
destroyed: "onDestroy",
onShow: "onShow",
onHide: "onHide"
},
component: {
beforeCreate: "onCreate",
created: "onCreate",
beforeMount: "onCreate",
mounted: "onReady",
beforeDestroy: "onInit",
destroyed: "onDestroy"
}
}
cml-quickapp-runtime/index.js
import { createApp } from './src/interfaces/createApp/index.js';
import { createPage } from './src/interfaces/createPage/index.js';
import { createComponent } from './src/interfaces/createComponent/index.js';
export default {
createApp,
createPage,
createComponent
}
cml-quickapp-runtime/src/interfaces/createPage/index.js
import createPgInterface from './index.interface';
export function createPage(options) {
return createPgInterface.createPage(options)
}
cml-quickapp-runtime/src/interfaces/createPage/index.interface
//这里要将 chameleon-runtime中的 createPage接口 include 进来
<include src="chameleon-runtime/src/interfaces/createPage/index.interface"></include>
<script cml-type="cml-quickapp">
import {Page} from '../../cml-quickapp'
class Method implements createPageInterface {
createPage(options) {
return new Page(options);
}
}
export default new Method();
</script>
▍实现cml-quickapp-api
引入 Chameleon 官方的标准 interface 文件
扩展实现新端,实现对应端的方法
// 引入官方标准interface文件
<include src="chameleon-api/src/interfaces/alert/index.interface"></include>
// 扩展实现新端
<script cml-type="quickapp">
class Method implements uiInterface {
alert(opt, successCallBack, failCallBack) {
const { message, confirmTitle} = opt;
// quickapp 变量已经提前在 cml-quickapp-plugin 阶段即编译转化阶段注入,
// 参考 https://github.com/quickappcn/cml-extplatform-quickapp/blob/master/packages/cml-quickapp-plugin/index.js
quickapp.prompt.showDialog({
title: '',
message,
buttons: [
{
text: confirmTitle
}
],
success: () => successCallBack(confirmTitle),
cancel: () => failCallBack(confirmTitle),
fail: () => failCallBack(confirmTitle)
})
}
}
export default new Method();
</script>
▍实现cml-quickapp-store
引入 Chameleon 官方的标准 interface 文件
扩展实现新端,实现对应端的方法
//引入官方标准 interface 文件
<include src="chameleon-store/src/interfaces/createStore/index.interface"></include>
<script cml-type="quickapp">
import createStore from '../../platform/quickapp'
class Method implements createStoreInterface {
createStore(options) {
return createStore(options)
}
}
export default new Method();
</script>
▍实现cml-quickapp-ui-builtin
创建和 Chameleon 官方的组件同名的 interface 文件
用新端自有的组件和语法呈现 CML 组件即可
// button.interface
<include src="chameleon-ui-builtin/components/button/button.interface"></include>
<script cml-type="quickapp" src="./button.quickapp.cml"></script>
// button.quickapp.cml
// template 实现 button 组件的 dom 结构,script 实现 button 的逻辑,style 实现 button 的样式效果
<template>
<div class="{{btnClasses}}" style="{{mrBtnStyle}}" onclick="clickHandler" ontouchstart="touchstart"
ontouchend="touchend">
<text c-if="{{text}}" class="{{textClasses}}" style="{{mrTextStyle}}">{{text}}</text>
<block c-else></block>
</div>
</template>
<script>...</script>
<style>...</style>
▍实现cml-quickapp-ui-builtin和实现cml-quickapp-ui
// c-popup.interface
<include src="cml-ui/components/c-popup/c-popup.interface"></include>
// c-popup.cml
<template>
<view>
<view c-if="{{show}}" class="c-popup" style="{{containerStyle}}">
<view class="{{maskClass}}" c-bind:tap="closeevent"></view>
<!-- c-catch禁止事件冒泡 -->
<view class="c-popup-content" style="{{contentStyle}}" c-catch:tap="contentTap">
<slot></slot>
</view>
</view>
</view>
</template>
<script>...</script>
<style>...</style>
5.
接入和使用
▍体验快应用
cml-demo:组件库&API 实例(https://github.com/beatles-chameleon/cml-demo)
cml-flexbox :Flex 布局案例集合(https://github.com/startheart/cml-flexbox)
cml-yanxuan: 仿网易严选 APP(https://github.com/beatles-chameleon/cml-yanxuan)
cml-todomvc:Todo-List(https://github.com/beatles-chameleon/cml-todoMVC)
组件库 && API 实例 + 仿网易严选 APP(https://github.com/quickappcn/cml-extplatform-quickapp/blob/master/com.applicatio)
flexbox 布局案例集合(https://github.com/quickappcn/cml-extplatform-quickapp/blob/master/com.application.flexbox.release.rpk)
Todo-List(https://github.com/quickappcn/cml-extplatform-quickapp/blob/master/com.application.todo.release.rpk)
拥有一台 Android 操作系统的移动设备
在移动设备上下载并安装最新版 快应用预览版 APP(https://www.quickapp.cn/docCenter/post/69) ,当前最新版本为1050.10
将以上 demo 项目编译打包生成的 .rpk 文件放入移动设备的 /sdcard/rpks 文件夹,再次打开预览版即可体验
▍接入快应用
• 全局安装最新的 chameleon-tool 工具
npm i -g chameleon-tool
• 启动快应用服务
此处以及下文均以 quickapp-demo 为例命名该快应用项目,并以 ~/quickapp-demo 目录为例启动快应用服务,此外,建议开发者在快应用项目根目录 ~/quickapp-demo/ 执行 hap watch,进行代码热更新。
• 安装和更新package
cd ~/quickapp-demo
hap server # 启动快应用服务
# 再新打开另外一个terminal
cd ~/quickapp-demo
hap watch # 热更新
"chameleon-api": "^0.5.3",
"chameleon-bridge": "^0.2.0",
"chameleon-runtime": "0.1.4",
"chameleon-store": "0.0.3",
"chameleon-ui-builtin": "^0.4.0-mvvm.3",
"cml-ui": "^0.3.0-alpha.1",
"cml-quickapp-api": "1.0.0",
"cml-quickapp-plugin": "1.0.0",
"cml-quickapp-runtime": "1.0.0",
"cml-quickapp-ui-builtin": "1.0.0",
"cml-quickapp-ui": "1.0.0",
"cml-quickapp-store": "1.0.0"
修改app.cml
vim ~/cml-flexbox/src/app/app.cml
<script cml-type="json">
{
"wx": {
"window": {
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Chameleon",
"navigationBarTextStyle":"black"
}
},
"baidu": {
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTitleText": "Chameleon",
"navigationBarTextStyle": "black"
}
},
"alipay": {
"window": {
"defaultTitle": "Chameleon"
}
}
}
</script>
<script cml-type="json">
{
"quickapp": {
"package": "com.application.demo", // 快应用包名
"name": "demo", // 快应用名称
"versionName": "1.0.0", // 版本名称
"versionCode": "1", // 版本代码
"minPlatformVersion": "1020", // 最小平台版本号
"icon": "../assets/images/chameleon.png", // 快应用logo
"features": [{ // 引用的快应用的能力
"name": "system.router"
},
{
"name": "system.webview"
},
{
"name": "system.prompt"
},
{
"name": "system.clipboard"
},
{
"name": "system.calendar"
},
{
"name": "system.device"
},
{
"name": "system.fetch"
},
{
"name": "system.file"
},
{
"name": "system.geolocation"
},
{
"name": "system.image"
},
{
"name": "system.media"
},
{
"name": "system.notification"
},
{
"name": "system.barcode"
},
{
"name": "system.sensor"
},
{
"name": "system.share"
},
{
"name": "system.shortcut"
},
{
"name": "system.storage"
},
{
"name": "system.vibrator"
},
{
"name": "system.network"
},
{
"name": "system.request"
},
{
"name": "system.audio"
},
{
"name": "system.volume"
},
{
"name": "system.battery"
},
{
"name": "system.brightness"
},
{
"name": "system.package"
},
{
"name": "system.record"
},
{
"name": "system.sms"
},
{
"name": "system.websocketfactory"
},
{
"name": "system.wifi"
},
{
"name": "system.contact"
}
],
"permissions": [{
"origin": "*"
}],
"config": {
"logLevel": "debug"
},
"display": {
"titleBarBackgroundColor": "#f2f2f2",
"titleBarTextColor": "#414141",
"titleBarText": "",
"menu": true
}
}
}
</script>
const path = require('path')
builtinNpmName: 'cml-quickapp-ui-builtin',
extPlatform: {
quickapp: 'cml-quickapp-plugin',
},
babelPath: [
path.join(__dirname,'node_modules/cml-quickapp-ui-builtin'),
path.join(__dirname,'node_modules/cml-quickapp-runtime'),
path.join(__dirname,'node_modules/cml-quickapp-api'),
path.join(__dirname,'node_modules/cml-quickapp-ui'),
path.join(__dirname,'node_modules/cml-quickapp-store'),
path.join(__dirname,'node_modules/cml-quickapp-mixins'),
path.join(__dirname,'node_modules/mobx'),
],
baseStyle:{wx: true,
web: true,
weex: true,
alipay: true,
baidu: true,
qq: true,
quickapp: true,
},
• 修改项目代码
chameleon-store 改为 cml-quickapp-store
chameleon-api 改为 cml-quickapp-api
import cml from "chameleon-api";
import store from "chameleon-store";
import cml from "cml-quickapp-api";
import store from "cml-quickapp-store";
<script cml-type="json">{
"base": {
"usingComponents": {
"c-actionsheet": "cml-ui/components/c-actionsheet/c-actionsheet"
},
}
}
</script>
<script cml-type="json">{
"base": {
"usingComponents": {
"c-actionsheet": "cml-quickapp-ui/components/c-actionsheet/c-actionsheet"
},
}
}
</script>
poly-comp.interface
poly-comp.web.cml
poly-comp.weex.cml
poly-comp.wx.cml
poly-comp.alipay.cml
poly-comp.baidu.cml
<script cml-type="interface">
type res = [String];
interface UnsupportedInterface {
getUnsupportApis(): res;
}
</script>
<script cml-type="web">
class Method implements UnsupportedInterface {
getUnsupportApis() {
return [];
}
}
export default new Method();
</script>
<script cml-type="weex">
class Method implements UnsupportedInterface {
getUnsupportApis() {
return ["设置页面标题", "WebSocket", "地理位置"];
}
}
export default new Method();
</script>
<script cml-type="wx">
class Method implements UnsupportedInterface {
getUnsupportApis() {
// 线上版微信小程序未配置demo请求的域名为可信域名,发版时去掉'网络请求'
// return ['网络请求', 'WebSocket'];
return [];
}
}
export default new Method();
</script>
<script cml-type="alipay">
class Method implements UnsupportedInterface {
getUnsupportApis() {
return ["启动参数", "beatles-bridge能力"];
}
}
export default new Method();
</script>
<script cml-type="baidu">
class Method implements UnsupportedInterface {
getUnsupportApis() {
return ["beatles-bridge能力"];
}
}
export default new Method();
</script>
<script cml-type="qq">
class Method implements UnsupportedInterface {
getUnsupportApis() {
// 线上版微信小程序未配置demo请求的域名为可信域名,发版时去掉'网络请求'
// return ['网络请求', 'WebSocket'];
return [];
}
}
export default new Method();
</script>
<script cml-type="quickapp">
class Method implements UnsupportedInterface {
getUnsupportApis() {
// 线上版微信小程序未配置demo请求的域名为可信域名,发版时去掉'网络请求'
// return ['网络请求', 'WebSocket'];
return [];
}
}
export default new Method();
</script>
▍快应用启动命令
cml quickapp dev
cml quickapp build
cml quickapp dev
进行快应用的开发了。在 CML 项目根目录 ~/cml-flexbox/
执行 cml quickapp dev
,会生成 dist/quickapp
文件夹,拷贝 /dist/quickapp
目录下所有文件到已经提前启动的快应用的项目根目录 ~/quickapp-demo/
,更新快应用代码,即可进行调试和预览。cd ~/cml-flexbox
cml quickapp dev
cp -r dist/quickapp/* ~/quickapp-demo/
6.
Bug & Tips
这些尚未完成的 feature 包括:
1. 动画相关。Chameleon 的动画调用方式和快应用差异颇大,当前未进行适配;
2. CSS 特性。受制于部分快应用的特性,诸如 box-sizing、box-shadow、white-space 等属性暂未支持;