megalo 是基於 Vue 的小程序框架(沒錯,又是基於 Vue 的小程序框架),可是它不只僅支持微信小程序,還支持支付寶小程序,同時還支持在開發時使用更多 Vue 的特性。javascript
對於用戶而言,小程序能提供更好的體驗,但對於開發者而言,要讓一個應用跑在多個平臺上,則須要寫多套代碼。如何提升小程序開發效率讓不少開發者都感到頭疼。html
業界也有相關的解決方案,如 taro 和 mpvue,兩者都是基於 react 和 vue 的開發模式實現,讓開發者可以以他們熟知的 react 或 vue 模式來開發小程序,提升開發效率。前端
mpvue 的發佈給了咱們不少啓發,更早的時候,咱們基於 RegularJS(網易自研的前端框架)開發了一個名爲 mpregular 的小程序框架。在 mpregular 的開發和實際使用過程當中,咱們發現若是小程序框架所支持的特性只是原框架的子集(例如不支持 filter、模版複雜表達式等),開發效率會大打折扣。vue
因此,咱們在方案上作了不少嘗試,目的是支持更多的特性,減小小程序與 H5 開發以前的差別。目前 mpregular 已經在考拉的小程序業務中大量應用,相關業務的開發同窗紛紛表示,學習成本變低,跨端業務(H5 和小程序)的開發效率提高近一倍。java
方案通過一段時間驗證後,咱們決定把這套方案用 vue 再實現一次,一是爲了適應技術棧的變動升級,二是爲社區作一點微小的工做,因而就便有了 megalo。node
相比於其餘小程序開發模式,因爲支持更多特性,megalo 更貼近 Vue 原生的開發模式。react
特性 | 小程序 | mpvue | megalo |
---|---|---|---|
computed 計算屬性 | ❌ | ⭕️ | ⭕️ |
v-model 雙向綁定 | ❌ | ⭕ | ⭕️ |
slot 插槽 | ⭕️ | ⭕️ | ⭕️ |
scoped-slot 插槽 | ❌ | ❌ | ⭕️ |
filter 過濾器 | ❌ | ❌ | ⭕️ |
v-html 富文本 | ❌ | ❌ | ⭕️ |
複雜表達式插值 | ❌ | ❌ | ⭕️ |
從表格能夠看到,megalo 最大的特色之一是,支持更多的 Vue 語法特性。這意味着,若是你有一個需求是要把現有的 Vue 代碼遷移到小程序上,不須要太多改動。由於你的代碼中可能大量使用 filter、scoped-slot、複雜表達式插值。webpack
支持 vue 的基本模版語法,包括 v-for
、v-if
。class 和 style 的綁定方式沒有限制,官方的用法都支持。git
<!-- v-if & v-for -->
<div v-for="(item, i) in list">
<div v-if="isEven(i)">{{ i }} - {{ item }}</div>
</div>
<!-- style & class -->
<div :class="classObject"></div>
<div :class="{ active: true }"></div>
<div :class="[activeClass, errorClass]"></div>
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div :style="styleObject"></div>
<div :style="[baseStyles, overridingStyles]"></div>
複製代碼
支持 slot 和 scoped-slot。github
<div>
<Container>
<Card>
<div slot="head"> {{ title }} </div>
<div> I'm body </div>
<div slot="foot"> I'm footer </div>
</Card>
</Container>
<List :list="list">
<span slot-scope="scopeProps">{{ scopeProps.item.label }}</span>
</List>
<div>
複製代碼
能夠在模版裏面寫複雜表達式、調用實例上的方法,固然也能夠用更簡潔的 filter 語法,跟平時用 Vue 開發同樣。
<div>
<div>{{ message.toUpperCase() }}</div>
<div>{{ toUpperCase( message ) }</div>
<div>{{ message | toUpperCase }}</div>
</div>
複製代碼
要使用 v-html
須要添加插件 @megalo/vhtml-plugin
,並引入模版解析庫 octoparse,在頁面入口安裝一下插件:
import Vue from 'vue'
import VHtmlPlugin from '@megalo/vhtml-plugin'
Vue.use(VHtmlPlugin)
複製代碼
利用 v-html
指令而後就能夠在小程序中渲染 html 了。
<div v-html="'<h1>megalo</h1>'"></div>
複製代碼
小程序的官方明確說明,在調用 setData
更新數據時若是數據量過大或頻率更高,會引起性能問題。megalo 在框架底層已經幫開發者對此進行優化,每次數據發生變化時,megalo 只會將視圖中要展現的、且發生變化的數據進行更新,將 setData
的數據更新量最小化,同時對更新數據頻率進行了限制。
像下面這段代碼,若是視圖只須要展現 user.name
這個字段的話,在進行數據同步時只會將 user.name
這個字符串更新到視圖層,其他字段是不會同步到小程序的對象上的。
<div>{{ user.name }}</div>
<script> export default { data() { return { user: { name: 'kaola', age: 3, favorite: [ 'encalyptus', 'sleeping' ] } } } } </script>
複製代碼
今年以來,各大流量平臺都在小程序領域有所動做,螞蟻金服成立小程序事業部,百度、今日頭條也紛紛推出本身的小程序。megalo 目前已經支持微信和支付寶小程序,百度小程序等平臺的支持也在計劃當中。
使用 megalo 開發很是簡單,只需在常見的 Vue 項目 webpack 構建配置上配置 @megalo/target
並引入 @magalo/template-compiler
便可。若是須要編譯成支付寶小程序,只須要設置 platform: 'alipay'
。
const { createMegaloTarget } = require('@megalo/target');
module.exports = {
target: createMegaloTarget( {
compiler: require('@megalo/template-compiler'),
platform: 'wechat'
} )
// 其餘 webpack 配置,如 vue-loader 等
};
複製代碼
接着,就能夠像開發 Vue 應用同樣去開發小程序。示例能夠參考 megalo-demo。
若是想用 typescript 進行開發,能夠參考 megalo-ts-simple。
小程序在結構上主要有 Service(JavascriptCore) 和 View(WebView) 兩部分組成(微信和支付寶小程序有着相似的結構,下文均以微信小程序爲例,並簡稱爲小程序),分別運行在獨立的環境上,之間不具有共享數據通道,兩者的通訊方式是將數據封裝在 js 腳本後傳遞。Page 實例就在 Service 中,經過 setData
方法將數據傳遞到 View。View 則經過事件綁定將視圖層觸發的事件傳遞給 Service。Service 層中沒法操做視圖層的 DOM 節點。
實際開發中,小程序的邏輯和模版須要寫在 .js
和.wxml
兩個文件中,分別在 Service 和 View 中執行。若是要將在瀏覽器端的 Vue 放到小程序中跑,須要將 .vue
文件中的 template 片斷轉換成 .wxml
文件,並對 Vue 的 runtime 部分改造,將其中的 DOM 操做移除,經過小程序的 Service 中的頁面實例上的 API 與 View 進行通訊。
最終的運行效果是,當 Vue 的 vm 上數據發生更新時,會從新渲染出 vdom,在的 patch 階段,框架不在去操做 DOM,而是經過 Page 上的 setData
方法將變化的數據更新到視圖層,完成 Vue 和 小程序的視圖更新,這就是 megalo 底層所作的工做。
megalo 的實現,主要分紅如下四個部分,下面本文將對每一個部分進行介紹。
小程序中,每個頁面(Page)是一個實例,頁面的生命週期鉤子有不少,但和實例建立的兩個關鍵生命週期分別是 onLoad
和 onReady
,它們分別在「頁面加載,實例初始化後
」和「初次頁面渲染完成
」時觸發。Vue 的實例要和小程序實例創建起聯繫,則須要在小程序 Page 實例建立好之後,即在 onLoad
的鉤子函數裏,去初始化對應的 Vue 根實例,將頁面實例 page
掛載到 Vue 實例的 $mp
上,此時也會觸發 Vue 的生命週期鉤子 created
。在頁面初次渲染完成後,則會調用 $mount
方法,與在瀏覽器中掛載 DOM 節點不一樣,這裏會將 Vue 實例上的數據初始化到視圖層中。由此,Vue 實例就與小程序的 Page 實創建起了聯繫。
除了這兩個生命週期鉤子之外,像小程序的 onShow
、onHide
等生命週期鉤子在觸發時,也會嘗試觸發 Vue 實例上的同名鉤子函數,實現兩種實例間生命週期的綁定。在小程序頁面退出銷燬時,會觸發 onUnload
鉤子,此時 Vue 的實例也會跟着銷燬。
小程序有它特有的模版語法和文件名後綴,因此在構建階段,咱們會將 .vue
文件中的 template 部分提取出來並轉換成對應的 .wxml
文件。標籤名、語法都會進行相應的轉換,如圖所示。
這一部分是在構建階段完成的,這意味着,megalo 不支持 render 函數的寫法。在構建階段除了將模版轉換成 .wxml
之外,還須要對模版中的每一個節點進行轉換,並在生產的 render 函數中加入相關的節點標記信息,數據映射和事件代理須要這些信息。
因爲沒法直接操做視圖層的 DOM,因此咱們只能利用 page.setData
這個方法完成數據到視圖層的映射。最簡單暴力的方法,是將 Vue 實例上的全部數據通通收集起來,經過調用 page.setData
方法更新 Page 實例的數據,這個方法會將數據掛載到 Page 實例上,同時也會把數據傳遞給視圖層。
可是,這種粗暴的更新方式有兩個弊端:
i. 全量更新 vm
上的數據是沒法區分哪些數據是視圖層須要的,冗餘無用的數據會被更新到 page
實例上。像下圖這個例子,視圖層只須要展示兩個字符串,若是 vm
上還存在兩個大數組,它們也會被無腦同步到 page
上。
ii. 同步到 page
實例上的數據其實就是原始數據,並非視圖層實際要展現的數據,因此展現數據的格式化與轉換須要依賴小程序模版的解析能力,致使一些 Vue 支持的模版語法沒法支持,例如 filter、複雜表達式、傳遞 class 對象等。
固然以上兩個弊端不會對功能開發形成影響,但在實際的業務開發中,會讓開發體驗不一致,尤爲是 H5 代碼遷移到小程序時,對效率影響頗大。爲了解決這個問題,megalo 採用另外一種方式,即將 render 時生成的 vnode 上的數據更新到視圖,vnode 的數據就是已經處理好的展現數據,根據 vnode 構造每一個節點的數據結構,再同步到視圖層。
例如如下這段代碼,在構建階段 megalo 會對每一個節點進行標記,使 render 時生成的 vnode 和模版中每一個插值可以對應上。
<!-- 編譯前的 Vue 模版 .vue -->
<div :class=「classObj」>
{{ date | format( 'YYYY' ) }}
</div>
<!-- 編譯後的小程序模版 .wxml -->
<view class="{{ node_1.class }}">
{{ node_2.text }}
</view>
複製代碼
以這種方式實現的數據映射,只有視圖層須要的兩個字符串數據會被同步到小程序的 Page 實例上,其他數據則被認爲與視圖無關則不會進行同步。
export default {
data() {
return {
classObj: {
'kaola': true
},
date: new Date(),
users: {
// big object
}
}
}
}
複製代碼
以下圖所示,Vue 渲染出來的 vnode 會被以特定的數據結構映射到 page 上,再同步到小程序視圖層。
以這種方式實現的數據映射,能夠更好地支持 Vue 的模版語法,且更大限度地減小更新視圖時傳輸的數據量,從框架層面規避 setData
的性能問題。
小程序視圖觸發事件後,會將 event
對象通知到 Page 實例,那麼咱們只須要將視圖層中全部的事件都代理到 page.proxy
這個方法中,而後再靠這個方法從 Vue 的實例樹上找到對應的 vm
和 handler
作事件處理。爲了實現這一目的,在構建階段對模版進行編譯時,除了要將事件監聽方法轉換爲 proxy
之外,還須要經過 data-
在元素上標記對應的組件 compid
和節點 nodeid
。
<!-- 編譯前的 Vue 模版 .vue -->
<div @click="onClick"></div>
<!-- 編譯後的小程序模版 .wxml -->
<view bindtap="proxy" data-compid="0" data-nodeid="0"></view>
複製代碼
事件觸發時,proxy 方法會從 event 對象上獲取對應的 id 信息和事件類型,進而從 Vue 的根 vm 開始查找,最終在 vnode 上找到對應的 handler 並執行事件處理,完成小程序事件到 Vue 實例的事件代理。
目前,megalo 已經逐步在考拉的小程序應用開發中投入使用,但 megalo 的數據映射方案早已經過 mpregular 在考拉的大量小程序應用中獲得了驗證。如今,megalo 支持 typescript 開發,支持支付寶小程序。
百度智能小程序的支持也在計劃以內,同時,咱們還計劃開發一個兼容個平臺的 UI 組件庫、API 庫,嘗試將跨 H5 和各小程序平臺的應用開發之間的差別最小化,提高開發效率。