新框架太多?學不動啦?有這一套跨端標準,從此不再用學習新框架了。html
各個小程序按本身喜愛「各自爲政」?有了這套標準,不再用重複開發各類新平臺啦。前端
現在前端比較流行的 React Native、Weex、Flutter 等跨平臺開發框架,對於開發來講屬於技術方案的選擇,好比,咱們會考慮用這個技術開發,性能會不會超過 h5,開發效率會不會超過原生開發等等。git
可是從 2017 年微信推出小程序,到至今各大廠商都推出本身的小程序,跨端開發就不只僅是技術的問題了。已經變成了必爭的流量入口。如今的小程序大戰像極了當前的瀏覽器大戰。大戰中受苦的是咱們一線開發者,一樣的應用要開發 N 次,面對了史無前例的挑戰,因此跨端框架的產生是大趨勢下的必然產物。github
chameleon 基於對跨端工做的積累, 規範了一套跨端標準,稱之爲 MVVM+協議;開發者只須要按照標準擴展流程,便可快速擴展任意 MVVM 架構模式的終端。並讓已有項目無縫運行新端。因此若是你但願讓 chameleon 快速支持淘寶小程序、React Native?只需按標準實現便可擴展。npm
最終讓開發者只須要用 chameleon 開發,就能夠在任意端運行,不再用學習新平臺框架啦。json
滴滴、芒果 TV、阿里的同窗合做,正在按照跨端協議流程進行字節跳動小程序的共建開發。小程序
快應用官方研發團隊也正在接入中api
跨端框架最核心的工做是統一,chameleon 定義了標準的跨端協議,經過編譯時+運行時的手段去實現各端的代碼和功能,其實現原理以下圖所示。瀏覽器
其中運行時和基礎庫部分利用多態協議實現各端的獨立性與框架的統一性。chameleon 目前支持的端都是採用這種方式,咱們定義了擴展一個新端所須要實現的全部標準,用戶只須要按照這些標準實現便可完成一個新端的擴展。微信
咱們再來看一張 chameleon 的設計圖,可以實現標準化的擴展新端,得益於多態協議中對各層代碼進行了接口的定義,各端代碼按照接口定義進行實現,向用戶代碼提供統一調用,同時還提供」多態協議「讓用戶代碼保障可維護性的前提下,直接觸達各端原生能力的方式。
簡單來講只須要實現 6 個 npm 包。
chameleon-api
提供了網絡請求,數據存儲,獲取系統信息,交互反饋等方法,用戶須要建立一個 npm 包,結構參考cml-demo-api。將chameleon-api
中提供的每一個方法利用多態接口擴展語法擴展新端的實現。
以擴展一個alert
方法爲例,chameleon-api
中alert
方法的接口定義文件爲chameleon-api/src/interfaces/alert.interface
,其中的接口定義內容以下:
<script cml-type="interface"> type alertOpt = { message: String, confirmTitle: String } type successCallBack = (result: String) => void; type failCallBack = (result: String) => void; interface uiInterface { alert(opt: alertOpt, successCallBack: successCallBack, failCallBack: failCallBack): void, } </script>
用戶實現的interface
文件中採用<include></include>
語法引入chameleon-api
中alert
方法的 interface 文件, 實現uiInterface
。
// 引入官方標準interface文件 <include src="chameleon-api/src/interfaces/alert/index.interface"></include> // 擴展實現新端(以頭條小程序爲例,假設端擴展標識爲:tt) <script cml-type="tt"> class Method implements uiInterface { alert(opt, successCallBack, failCallBack) { // 根據頭條小程序實現alert彈窗 let { message, confirmTitle} = opt; tt.showModal({ content: message, confirmText: confirmTitle, ...... }); } } export default new Method(); </script>
組件分爲內置組件chameleon-ui-builtin和擴展組件cml-ui。因此用戶須要建立兩個 npm 包分別實現這兩個組件庫,結構參考cml-demo-ui-builtin和cml-demo-ui。利用多態組件擴展語法,對原有組件庫中的每個組件進行新端的實現。
原有組件庫中的組件也分爲兩種,一種爲各端都有分別實現的多態組件,例如chameleon-ui-builtin
中的button
組件。實現起來新端基本上也是要單獨實現。另外一種例如chameleon-ui-builtin
中的radio
組件,各端的實現都是用的chameleon-ui-builtin/components/radio/radio.cml
。因此新端基本也能夠複用這個實現,(還須要測試狀況確實是否能夠複用)。
例如:
編寫 my-ui-builtin/components/button/button.interface
// 引入官方標準interface文件 <include src="chameleon-ui-builtin/components/button/button.interface" />
編寫 my-ui-builtin/components/button/button.demo.cml
<template> <origin-button c-bind:tap="onclick" open-type="{{openType}}"> </origin-button> </template> <script> // js實現部分 </script> <style scoped> // 樣式部分 </style> <script cml-type="json"> // json配置 </script>
獨立實現的my-ui-builtin/components/button/button.demo.cml
文件屬於多態組件的灰度層,能夠調用各端底層組件和 api,具體例子參見button和scroller的實現。
編寫 my-ui-builtin/components/radio/button.interface
// 引入官方標準interface文件 <include src="chameleon-ui-builtin/components/radio/radio.interface"></include> // 複用官方的實現 <script cml-type="demo" src="chameleon-ui-builtin/components/radio/radio.cml"></script>
chameleon 內部會將整個項目文件編譯爲以下編譯圖結構,節點中的內容通過了標準編譯,好比script
節點的babel
處理,style
節點的less
與stylus
處理等等。
節點的數據結構以下:
class CMLNode { constructor(options = {}) { this.realPath; // 文件物理地址 會帶參數 this.moduleType; // template/style/script/json/asset this.dependencies = []; // 該節點的直接依賴 app.cml依賴pages.cml pages.cml依賴components.cml js依賴js this.childrens = []; // 子模塊 cml文件纔有子模塊 this.source; // 模塊源代碼 this.output; // 模塊輸出 各類過程操做該字段 ...... } }
用戶只須要實現一個編譯插件類,利用鉤子方法實現對節點的編譯,全部節點編譯完後再進行文件的組織。編譯類以下:
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() }) ...... } }
運行時主要是對 cml 文件的邏輯對象進行適配,chameleon 內部將 cml 文件的邏輯對象分爲三類 App、Page、Component。對應會調用用戶運行時 npm 包的createApp、createPage、createComponent
方法,因此對外只須要實現這三個方法。
例如一個 Page 的邏輯對象以下:
class PageIndex { data = { name: 'chameleon' } computed = { sayName () { return 'Hello' + this.name; } } mounted() { } } export default new PageIndex();
編譯時就會自動插入用戶的運行時方法處理邏輯對象,例如cml-demo-runtime
:
class PageIndex { ...... } export default new PageIndex(); // 編譯時自動插入用戶配置的運行時方法 import {createPage} from 'cml-demo-runtime'; createPage(exports.default);
createApp、createPage、createComponent
方法,參考cml-demo-runtime的結構進行實現,須要include
chameleon-runtime
中相應的接口進行實現,纔可以實現對chameleon-runtime
的擴展。用戶的工做量主要在於對邏輯對象的處理,能夠參考chameleon-runtime中的實現方式,通常須要以下方面的適配工做。
從宏觀來看,運行時處理可分爲:
從微觀來看,有如下處理:
例如: createPage 方法的實現
<include src="chameleon-runtime/src/interfaces/createPage/index.interface"></include> <script cml-type="demo"> class Method implements createPageInterface { createPage(options) { // 各端自行實現adapter adapter(options); //例如調用小程序原生頁面構造函數 Page(options); return {}; } } export default new Method(); </script>
chameleon-store
提供了相似 Vuex 的數據管理解決方案,具體標準參見數據管理。一樣利用多態協議實現其功能。
期待更多人的加入開源。想了解更多 chameleon 信息請訪問官網 cmljs.org
預告:chameleon 1.0正式版即將到來