前言
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,在开发者工具中即可预览效果;
以上项目在字节跳动小程序中的预览效果图如下:
那么具体该如何接入字节跳动小程序呢?
如何接入字节跳动小程序
npm i chameleon-tool@0.4.0 -g
复制代码
"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 相关配置:
引入 path 模块
const path = require('path')
复制代码
新增配置
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'),]
复制代码
修改项目代码
修改项目中相关包的引用
import cml from "chameleon-api";import store from "chameleon-store";<script cml-type="json"> {"base": {"usingComponents": {"c-actionsheet": "cml-ui/components/c-actionsheet/c-actionsheet" }, } }</script>
复制代码
改为
import cml from "cml-tt-api";import store from "cml-tt-stroe";<script cml-type="json"> {"base": {"usingComponents": {"c-actionsheet": "cml-tt-ui/components/c-actionsheet/c-actionsheet" }, } }</script>
复制代码
项目中开发者自定义的多态组件 以及 多态接口 都要增加字节跳动这一端的实现
详细教程参考:https://cmljs.org/doc/example/tt_miniapp.html
扩展字节跳动小程序日志
参与方:
-芒果 Tv @Jeany
主要负责 cml-tt-api
以下适合有一定 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: [new MvvmGraphPlugin({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 生成构建图。
compiler.plugin('should-emit', function(compilation) {try { mvvmCompiler.run(compilation.modules); } catch (e) { cml.log.error(e); }// 返回false 不进入emit阶段return false; })
复制代码
以上就是 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 = 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() }) ...... }}
复制代码
更为详细的 开发编译插件的教程请参考:https://cml.js.org/doc/extend/start.html
以扩展字节跳动的 template 编译为例:
compiler.hook('compile-template', function(currentNode, parentNodeType) {// 部分template处理也需要用到optionsconst options = currentNode.extra; currentNode.output = templateParser(currentNode.source, options)
})
复制代码
这个 templateParser 函数就是将 CML 协议下的模板语法 转化为符合字节跳动小程序的语法。
<view c-if="{{value}}"></view>
复制代码
转化为
<view tt: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) { let hasCompiledNode = []; let bootstrapCode = compiler.amd.getModuleBootstrap(); compiler.writeFile('/static/js/manifest.js', bootstrapCode); let commonjsContent = `var manifest = require('./manifest.js');\n`; commonjsContent += `var cmldefine = 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';export default { createApp, createPage, createComponent}cml-tt-runtime/src/interfaces/createPage/index.jsimport createPgInterface from './index.interface';export function createPage(options) { return createPgInterface.createPage(options)}
复制代码
cml-tt-runtime/src/interfaces/createPage/index.interface
//这里要将 chameleon-runtime中的 createPage接口 include 进来<include src="chameleon-runtime/src/interfaces/createPage/index.interface"></include><script cml-type="tt"> import {Page} from '../../tt'class Method implements createPageInterface { createPage(options) {return new Page(options); } }
export default new Method();</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文件<include src="chameleon-api/src/interfaces/alert/index.interface"></include>
// 扩展实现新端,实现对应端的方法<script cml-type="tt">class Method implements uiInterface { alert(opt, successCallBack, failCallBack) {let { message, confirmTitle} = opt; tt.showModal({title: confirmTitle,content: message,showCancel: false, success() { successCallBack(confirmTitle); }, fail(){ failCallBack(confirmTitle); } }); }}
export default new Method();</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 文件<include src="chameleon-store/src/interfaces/createStore/index.interface"></include>
<script cml-type="tt">
import createStore from '../../platform/tt'//扩展实现新端,实现对应端的方法class Method implements createStoreInterface { createStore(options) {return createStore(options) } }
export default new Method();</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 团队会带着"一套代码运行多端,一端所见即多端所见"的初心,继续支持各个小程序的开发和应用。
评论