号外!Chameleon 支持字节跳动小程序啦「终于解决」

小程序 (89) 2023-05-21 09:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说号外!Chameleon 支持字节跳动小程序啦「终于解决」,希望能够帮助你!!!。

前言

Chameleon(简写 CML ) 团队秉承 " 一套代码运行多端,一端所见即多端所见 " 的初心,在最初支持 web weex wx 三端之后,凭借着多态协议设计思想,灵活的工程化配置,以及跨端标准协议, CML 团队可以很快的扩展新端,比如支付宝小程序和百度小程序。

对于字节跳动小程序,考虑到 CML 开源社区的比较活跃,以及很多同行有着极大的兴趣共建,内部讨论之后,一致决定由外部贡献者根据跨端标准协议去扩展字节跳动小程序,目前已经完成了支持字节跳动小程序的开发。

对于已有的 CML 的项目按照接入字节跳动小程序步骤,即可直接复用;

CML 官方的体验仓库包括:cml-demo 、cml-flexbox、cml-yanxuan、cml-todomvc

地址: https://github.com/chameleon-team

下载字节跳动小程序开发者工具: https://microapp.bytedance.com/docs/devtool/versionUpdate.html

clone 下来以上仓库之后,切换到 master-tt 分支,执行 cml tt dev,在开发者工具中即可预览效果;

以上项目在字节跳动小程序中的预览效果图如下:

那么具体该如何接入字节跳动小程序呢?

如何接入字节跳动小程序

  • 升级最新的 chameleon-tool

 复制代码

npm i chameleon-tool@0.4.0-g
  • 升级 npm 包,引入字节跳动小程序相关包。升级的包如下:

 复制代码

"chameleon-api":"^0.5.3",
"chameleon-runtime":"0.1.4",
"chameleon-store":"0.0.3",
"chameleon-ui-builtin":"^0.4.1",
"cml-ui":"^0.3.1"

引入的新包如下:

 复制代码

"cml-tt-api":"0.2.3",
"cml-tt-plugin":"0.2.3",
"cml-tt-runtime":"0.2.3",
"cml-tt-store":"0.2.3",
"cml-tt-ui":"0.2.3",
"cml-tt-ui-builtin":"0.2.3"

修改 chameleon.config.js 相关配置:

  1. 引入 path 模块

 复制代码

constpath=require('path')
  1. 新增配置

 复制代码

builtinNpmName:'cml-tt-ui-builtin',
extPlatform: {
tt:'cml-tt-plugin',
},
babelPath: [
path.join(__dirname,'node_modules/cml-tt-ui-builtin'),
path.join(__dirname,'node_modules/cml-tt-runtime'),
path.join(__dirname,'node_modules/cml-tt-api'),
path.join(__dirname,'node_modules/cml-tt-ui'),
path.join(__dirname,'node_modules/cml-tt-store'),
path.join(__dirname,'node_modules/cml-tt-mixins'),
path.join(__dirname,'node_modules/mobx'),
]

修改项目代码

  1. 修改项目中相关包的引用

 复制代码

importcmlfrom"chameleon-api";
importstorefrom"chameleon-store";
<scriptcml-type="json">
{
"base": {
"usingComponents": {
"c-actionsheet":"cml-ui/components/c-actionsheet/c-actionsheet"
},
}
}
</script>

改为

 复制代码

importcmlfrom"cml-tt-api";
importstorefrom"cml-tt-stroe";
<scriptcml-type="json">
{
"base": {
"usingComponents": {
"c-actionsheet":"cml-tt-ui/components/c-actionsheet/c-actionsheet"
},
}
}
</script>
  1. 项目中开发者自定义的多态组件 以及 多态接口 都要增加字节跳动这一端的实现
  2. 详细教程参考: https://cmljs.org/doc/example/tt_miniapp.html

扩展字节跳动小程序日志

参与方:

  • 阿里影业 @怀虚
  • 主要负责 cml-tt-ui 和 cml-tt-runtime

- 芒果 Tv @Jeany

主要负责 cml-tt-api

  • 滴滴普惠出行 @不懂小彬
  • 主要负责 cml-tt-ui-built 和 cml-tt-store
  • 滴滴普惠出行 @榮
  • 主要负责 cml-tt-plugin 和 cml-tt-mixins

以下适合有一定 CML 基础的人阅读。

了解扩展新端标准编译侧的 webpack 原理

chameleon-tool 中对于外部扩展的 webpack 配置的源码 参考: https://github.com/didi/chameleon/blob/0.3.x-alpah-merge-mvvm/packages/chameleon-tool/configs/mvvm/getExtendConfig.js

这里重点讲下处理 .cml 后缀文件的 mvvm-cml-loader 和 mvvm-pack 中的 MvvmGraphPlugin

配置如下:

 复制代码

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:[
newMvvmGraphPlugin({
cmlType:type,
media
}, platformPlugin)
]

mvvm-cml-loader 源码:

https://github.com/didi/chameleon/blob/0.3.x-alpah-merge-mvvm/packages/mvvm-cml-loader/index.js

主要作用是以 app.cml 为入口,通过内联 loader 的形式循环递归的添加依赖,将所有的依赖添加到 webpack 构建过程中。

mvvm-pack 源码:

https://github.com/didi/chameleon/tree/0.3.x-alpah-merge-mvvm/packages/mvvm-pack

mvvmGraphPlugin.js 中劫持了 webpack 的输出,通过 mvvmCompiler 生成构建图。

号外!Chameleon 支持字节跳动小程序啦「终于解决」_https://bianchenghao6.com/blog_小程序_第1张

 复制代码

compiler.plugin('should-emit',function(compilation){
try{
mvvmCompiler.run(compilation.modules);
}catch(e) {
cml.log.error(e);
}
// 返回 false 不进入 emit 阶段
returnfalse;
})

以上就是 CML 脚手架产生的树状结构图,递归传递给 cml-XX-plugin 来转义目标语法。

实现 cml-tt-plugin

目标:将 template、script、style 等节点编译成符合字节跳动小程序的语法,然后打包输出成符合字节跳动小程序的结构;

cml-tt-plugin: https://github.com/chameleon-team/cml-tt-sets/blob/master/packages/cml-tt-plugin/index.js

根据上面生成的构建图,贡献者在编译插件 cml-tt-plugin 中可以对构建图中所有节点(包括 js 节点 json 节点 style 节点 template 节点等)编译成符合要扩展的端的语法,然后在 pack 中可以进行打包输出成符合对应端的结构。

 复制代码

module.exports =classDemoPlugin{
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()
})
......
}}

更为详细的 开发编译插件的教程请参考: https://cml.js.org/doc/extend/start.html

以扩展字节跳动的 template 编译为例:

 复制代码

compiler.hook('compile-template',function(currentNode, parentNodeType){
// 部分 template 处理也需要用到options
constoptions= currentNode.extra;
currentNode.output = templateParser(currentNode.source,options)
})

这个 templateParser 函数就是将 CML 协议下的模板语法 转化为符合字节跳动小程序的语法。

  • CML 协议下模板语法: https://cml.js.org/doc/extend/template.html
  • 字节跳动小程序模板语法:
  • https://developer.toutiao.com/docs/framework/ttml.html

 复制代码

<viewc-if="{{value}}"></view>

转化为

 复制代码

<viewtt:if="{{value}}"></view>

模板编译的具体实现参考:

https://github.com/chameleon-team/cml-tt-sets/blob/master/packages/cml-tt-plugin/index.js

各个节点编译完之后,可以再 pack 阶段自定义打包,部分代码如下:

 复制代码

compiler.hook('pack',function(projectGraph){
lethasCompiledNode = [];
letbootstrapCode = compiler.amd.getModuleBootstrap();
compiler.writeFile('/static/js/manifest.js', bootstrapCode);
letcommonjsContent = `varmanifest =require('./manifest.js');\n`;
commonjsContent += `varcmldefine = manifest.cmldefine;\n`;
// 遍历节点
outputNode(projectGraph);
compiler.writeFile('/static/js/common.js', commonjsContent);
//...})

具体实现参考:

https://github.com/chameleon-team/cml-tt-sets/blob/master/packages/cml-tt-plugin/index.js

实现 cml-tt-runtime

目标:实现运行时,代理字节跳动小程序 App,Page,Component 方法以及生命周期。

cml-tt-runtime: https://github.com/chameleon-team/cml-tt-sets/tree/master/packages/cml-tt-runtime

运行时的主要作用是抹平各端的生命周期差异性,进行数据响应式绑定等 实现运行时的参考教程 : https://cml.js.org/doc/extend/runtime.html

这里以 page 实现逻辑为例:

 复制代码

// 编译时自动插入用户配置的运行时方法
import{createPage}from'cml-tt-runtime';
createPage(exports.default);

以上两行代码是在 mvvm-cml-loader 中插入的,具体实现参考源码: https://github.com/didi/chameleon/blob/0.3.x-alpah-merge-mvvm/packages/mvvm-cml-loader/selector.js

中对于 script 节点的处理;

我们只需要实现对应端的以下几个方法:createApp createPage createComponent

具体实现参考:

https://github.com/chameleon-team/cml-tt-sets/tree/master/packages/cml-tt-runtime/src/tt/instance

以 cml-tt-runtime 中的 createPage 实现为例:

https://github.com/chameleon-team/cml-tt-sets/tree/master/packages/cml-tt-runtime

cml-tt-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';
exportdefault{
createApp,
createPage,
createComponent
}
cml-tt-runtime/src/interfaces/createPage/index.js
importcreatePgInterfacefrom'./index.interface';
export function createPage(options) {
returncreatePgInterface.createPage(options)
}

cml-tt-runtime/src/interfaces/createPage/index.interface

 复制代码

// 这里要将 chameleon-runtime 中的 createPage 接口include进来
<includesrc="chameleon-runtime/src/interfaces/createPage/index.interface"></include>
<script cml-type="tt">import{Page}from'../../tt'
classMethodimplements createPageInterface {
createPage(options) {
returnnewPage(options);
}
}
exportdefaultnewMethod();
</script>

实现 cml-tt-api

目标:基于跨端协议,实现字节跳动小程序对应的 API

cml-tt-api: https://github.com/chameleon-team/cml-tt-sets/tree/master/packages/cml-tt-api

实现 API 的部分特别简单,只需两步:

  • 第一:引入官方标准 interface 文件。
  • 第二:扩展实现新端,实现对应端的方法。

具体实现如下:

cml-tt-api/src/interfaces/alert/index.interface

 复制代码

// 引入官方标准 interface 文件
<includesrc="chameleon-api/src/interfaces/alert/index.interface"></include>
// 扩展实现新端,实现对应端的方法
<scriptcml-type="tt">classMethodimplementsuiInterface{
alert(opt, successCallBack, failCallBack) {
let { message, confirmTitle} = opt;
tt.showModal({
title: confirmTitle,
content: message,
showCancel:false,
success() {
successCallBack(confirmTitle);
},
fail(){
failCallBack(confirmTitle);
}
});
}}
exportdefaultnewMethod();
</script>

实现 cml-tt-store

目标:基于 mobx 实现一套响应式数据系统

cml-tt-store: https://github.com/chameleon-team/cml-tt-sets/tree/master/packages/cml-tt-store

实现 store 同样只需要两步:

  • 第一:引入官方标准 interface 文件。
  • 第二:扩展实现新端,实现对应端的方法。

 复制代码

// 引入官方标准 interface 文件
<includesrc="chameleon-store/src/interfaces/createStore/index.interface"></include>
<scriptcml-type="tt">
importcreateStorefrom'../../platform/tt'
// 扩展实现新端,实现对应端的方法
classMethodimplementscreateStoreInterface{
createStore(options) {
returncreateStore(options)
}
}
exportdefaultnewMethod();
</script>

具体实现代码参考: https://github.com/chameleon-team/cml-tt-sets/blob/master/packages/cml-tt-store/src/platform/common/mini/index.js

实现 cml-tt-ui-builtin 和 cml-tt-ui

目标:UI 组件库是用来实现多端一致性的基础,参考原有的组件库实现,可以快速扩展出字节跳动小程序端的 UI 组件。

cml-tt-ui: https://github.com/chameleon-team/cml-tt-sets/tree/master/packages/cml-tt-ui

cml-tt-ui-builtin: https://github.com/chameleon-team/cml-tt-sets/tree/master/packages/cml-tt-ui-builtin

扩展新端组件教程参考:

https://cml.js.org/doc/extend/ui-builtin.html

共建成果

应社区内广大开发者的提议,CML 团队制定了一套 MVVM+ 的 协议标准,并且在此标准上,规定了 扩展新端标准流程 。随着越来越多的小程序厂商入局,CML 团队会带着 " 一套代码运行多端,一端所见即多端所见 " 的初心,继续支持各个小程序的开发和应用。

原文

https://www.infoq.cn/article/lAA1kcAw0cHQpAD2eaKm

发表回复