megalo 是基于 Vue 的小程序框架(沒錯(cuò),又是基于 Vue 的小程序框架),但是它不僅僅支持微信小程序,還支持支付寶小程序,同時(shí)還支持在開發(fā)時(shí)使用更多 Vue 的特性。 背景對于用戶而言,小程序能提供更好的體驗(yàn),但對于開發(fā)者而言,要讓一個(gè)應(yīng)用跑在多個(gè)平臺(tái)上,則需要寫多套代碼。如何提高小程序開發(fā)效率讓很多開發(fā)者都感到頭疼。 業(yè)界也有相關(guān)的解決方案,如 taro 和 mpvue,二者都是基于 react 和 vue 的開發(fā)模式實(shí)現(xiàn),讓開發(fā)者能夠以他們熟知的 react 或 vue 模式來開發(fā)小程序,提高開發(fā)效率。 mpvue 的發(fā)布給了我們很多啟發(fā),更早的時(shí)候,我們基于 RegularJS(網(wǎng)易自研的前端框架)開發(fā)了一個(gè)名為 mpregular 的小程序框架。在 mpregular 的開發(fā)和實(shí)際使用過程中,我們發(fā)現(xiàn)如果小程序框架所支持的特性只是原框架的子集(例如不支持 filter、模版復(fù)雜表達(dá)式等),開發(fā)效率會(huì)大打折扣。 所以,我們在方案上做了很多嘗試,目的是支持更多的特性,減少小程序與 H5 開發(fā)之前的差異。目前 mpregular 已經(jīng)在考拉的小程序業(yè)務(wù)中大量應(yīng)用,相關(guān)業(yè)務(wù)的開發(fā)同學(xué)紛紛表示,學(xué)習(xí)成本變低,跨端業(yè)務(wù)(H5 和小程序)的開發(fā)效率提升近一倍。 方案經(jīng)過一段時(shí)間驗(yàn)證后,我們決定把這套方案用 vue 再實(shí)現(xiàn)一次,一是為了適應(yīng)技術(shù)棧的變更升級,二是為社區(qū)做一點(diǎn)微小的工作,于是就便有了 megalo。 特性支持更多模版語法特性相比于其他小程序開發(fā)模式,由于支持更多特性,megalo 更貼近 Vue 原生的開發(fā)模式。
從表格可以看到,megalo 最大的特點(diǎn)之一是,支持更多的 Vue 語法特性。這意味著,如果你有一個(gè)需求是要把現(xiàn)有的 Vue 代碼遷移到小程序上,不需要太多改動(dòng)。因?yàn)槟愕拇a中可能大量使用 filter、scoped-slot、復(fù)雜表達(dá)式插值。 基本語法支持 vue 的基本模版語法,包括 v-for 、 v-if 。class 和 style 的綁定方式?jīng)]有限制,官方的用法都支持。
slot & scoped-slot支持 slot 和 scoped-slot。
復(fù)雜表達(dá)式 & filter可以在模版里面寫復(fù)雜表達(dá)式、調(diào)用實(shí)例上的方法,當(dāng)然也可以用更簡潔的 filter 語法,跟平時(shí)用 Vue 開發(fā)一樣。
v-html要使用 v-html 需要添加插件 @megalo/vhtml-plugin ,并引入模版解析庫 octoparse ,在頁面入口安裝一下插件: import Vue from 'vue' import VHtmlPlugin from '@megalo/vhtml-plugin' Vue.use(VHtmlPlugin) 復(fù)制代碼 利用 v-html 指令然后就可以在小程序中渲染 html 了。 <div v-html="'<h1>megalo</h1>'"></div> 復(fù)制代碼 更好的數(shù)據(jù)更新性能小程序的官方明確說明,在調(diào)用 setData 更新數(shù)據(jù)時(shí)如果數(shù)據(jù)量過大或頻率更高,會(huì)引發(fā)性能問題。megalo 在框架底層已經(jīng)幫開發(fā)者對此進(jìn)行優(yōu)化,每次數(shù)據(jù)發(fā)生變化時(shí),megalo 只會(huì)將視圖中要展示的、且發(fā)生變化的數(shù)據(jù)進(jìn)行更新,將 setData 的數(shù)據(jù)更新量最小化,同時(shí)對更新數(shù)據(jù)頻率進(jìn)行了限制。 像下面這段代碼,如果視圖只需要展示 user.name 這個(gè)字段的話,在進(jìn)行數(shù)據(jù)同步時(shí)只會(huì)將 user.name 這個(gè)字符串更新到視圖層,其余字段是不會(huì)同步到小程序的對象上的。
支持更多平臺(tái)今年以來,各大流量平臺(tái)都在小程序領(lǐng)域有所動(dòng)作,螞蟻金服成立小程序事業(yè)部,百度、今日頭條也紛紛推出自己的小程序。megalo 目前已經(jīng)支持微信和支付寶小程序,百度小程序等平臺(tái)的支持也在計(jì)劃當(dāng)中。 使用使用 megalo 開發(fā)非常簡單,只需在常見的 Vue 項(xiàng)目 webpack 構(gòu)建配置上配置 @megalo/target 并引入 @magalo/template-compiler 即可。如果需要編譯成支付寶小程序,只需要設(shè)置 platform: 'alipay' 。
接著,就可以像開發(fā) Vue 應(yīng)用一樣去開發(fā)小程序。示例可以參考 megalo-demo 。 如果想用 typescript 進(jìn)行開發(fā),可以參考 megalo-ts-simple 。 實(shí)現(xiàn)小程序在結(jié)構(gòu)上主要有 Service(JavascriptCore) 和 View(WebView) 兩部分組成(微信和支付寶小程序有著類似的結(jié)構(gòu),下文均以微信小程序?yàn)槔?,并簡稱為小程序),分別運(yùn)行在獨(dú)立的環(huán)境上,之間不具備共享數(shù)據(jù)通道,二者的通信方式是將數(shù)據(jù)封裝在 js 腳本后傳遞。Page 實(shí)例就在 Service 中,通過 setData 方法將數(shù)據(jù)傳遞到 View。View 則通過事件綁定將視圖層觸發(fā)的事件傳遞給 Service。Service 層中無法操作視圖層的 DOM 節(jié)點(diǎn)。
實(shí)際開發(fā)中,小程序的邏輯和模版需要寫在 .js 和 .wxml 兩個(gè)文件中,分別在 Service 和 View 中執(zhí)行。如果要將在瀏覽器端的 Vue 放到小程序中跑,需要將 .vue 文件中的 template 片段轉(zhuǎn)換成 .wxml 文件,并對 Vue 的 runtime 部分改造,將其中的 DOM 操作移除,通過小程序的 Service 中的頁面實(shí)例上的 API 與 View 進(jìn)行通信。 最終的運(yùn)行效果是,當(dāng) Vue 的 vm 上數(shù)據(jù)發(fā)生更新時(shí),會(huì)重新渲染出 vdom,在的 patch 階段,框架不在去操作 DOM,而是通過 Page 上的 setData 方法將變化的數(shù)據(jù)更新到視圖層,完成 Vue 和 小程序的視圖更新,這就是 megalo 底層所做的工作。
megalo 的實(shí)現(xiàn),主要分成以下四個(gè)部分,下面本文將對每個(gè)部分進(jìn)行介紹。 生命周期小程序中,每一個(gè)頁面(Page)是一個(gè)實(shí)例,頁面的生命周期鉤子有很多,但和實(shí)例創(chuàng)建的兩個(gè)關(guān)鍵生命周期分別是 onLoad 和 onReady ,它們分別在「 頁面加載,實(shí)例初始化后 」和「 初次頁面渲染完成 」時(shí)觸發(fā)。Vue 的實(shí)例要和小程序?qū)嵗⑵鹇?lián)系,則需要在小程序 Page 實(shí)例創(chuàng)建好以后,即在 onLoad 的鉤子函數(shù)里,去初始化對應(yīng)的 Vue 根實(shí)例,將頁面實(shí)例 page 掛載到 Vue 實(shí)例的 $mp 上,此時(shí)也會(huì)觸發(fā) Vue 的生命周期鉤子 created 。在頁面初次渲染完成后,則會(huì)調(diào)用 $mount 方法,與在瀏覽器中掛載 DOM 節(jié)點(diǎn)不同,這里會(huì)將 Vue 實(shí)例上的數(shù)據(jù)初始化到視圖層中。由此,Vue 實(shí)例就與小程序的 Page 實(shí)建立起了聯(lián)系。
除了這兩個(gè)生命周期鉤子以外,像小程序的 onShow 、 onHide 等生命周期鉤子在觸發(fā)時(shí),也會(huì)嘗試觸發(fā) Vue 實(shí)例上的同名鉤子函數(shù),實(shí)現(xiàn)兩種實(shí)例間生命周期的綁定。在小程序頁面退出銷毀時(shí),會(huì)觸發(fā) onUnload 鉤子,此時(shí) Vue 的實(shí)例也會(huì)跟著銷毀。 模版轉(zhuǎn)換小程序有它特有的模版語法和文件名后綴,所以在構(gòu)建階段,我們會(huì)將 .vue 文件中的 template 部分提取出來并轉(zhuǎn)換成對應(yīng)的 .wxml 文件。標(biāo)簽名、語法都會(huì)進(jìn)行相應(yīng)的轉(zhuǎn)換,如圖所示。
這一部分是在構(gòu)建階段完成的,這意味著,megalo 不支持 render 函數(shù)的寫法。在構(gòu)建階段除了將模版轉(zhuǎn)換成 .wxml 以外,還需要對模版中的每個(gè)節(jié)點(diǎn)進(jìn)行轉(zhuǎn)換,并在生產(chǎn)的 render 函數(shù)中加入相關(guān)的節(jié)點(diǎn)標(biāo)記信息,數(shù)據(jù)映射和事件代理需要這些信息。 數(shù)據(jù)映射由于無法直接操作視圖層的 DOM,所以我們只能利用 page.setData 這個(gè)方法完成數(shù)據(jù)到視圖層的映射。最簡單暴力的方法,是將 Vue 實(shí)例上的所有數(shù)據(jù)統(tǒng)統(tǒng)收集起來,通過調(diào)用 page.setData 方法更新 Page 實(shí)例的數(shù)據(jù),這個(gè)方法會(huì)將數(shù)據(jù)掛載到 Page 實(shí)例上,同時(shí)也會(huì)把數(shù)據(jù)傳遞給視圖層。
但是,這種粗暴的更新方式有兩個(gè)弊端: i. 全量更新 vm 上的數(shù)據(jù)是無法區(qū)分哪些數(shù)據(jù)是視圖層需要的,冗余無用的數(shù)據(jù)會(huì)被更新到 page 實(shí)例上。像下圖這個(gè)例子,視圖層只需要展現(xiàn)兩個(gè)字符串,如果 vm 上還存在兩個(gè)大數(shù)組,它們也會(huì)被無腦同步到 page 上。
ii. 同步到 page 實(shí)例上的數(shù)據(jù)其實(shí)就是原始數(shù)據(jù),并不是視圖層實(shí)際要展示的數(shù)據(jù),所以展示數(shù)據(jù)的格式化與轉(zhuǎn)換需要依賴小程序模版的解析能力,導(dǎo)致一些 Vue 支持的模版語法無法支持,例如 filter、復(fù)雜表達(dá)式、傳遞 class 對象等。
當(dāng)然以上兩個(gè)弊端不會(huì)對功能開發(fā)造成影響,但在實(shí)際的業(yè)務(wù)開發(fā)中,會(huì)讓開發(fā)體驗(yàn)不一致,尤其是 h5 代碼遷移到小程序時(shí),對效率影響頗大。為了解決這個(gè)問題,megalo 采用另一種方式,即將 render 時(shí)生成的 vnode 上的數(shù)據(jù)更新到視圖,vnode 的數(shù)據(jù)就是已經(jīng)處理好的展示數(shù)據(jù),根據(jù) vnode 構(gòu)造每個(gè)節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu),再同步到視圖層。 例如以下這段代碼,在構(gòu)建階段 megalo 會(huì)對每個(gè)節(jié)點(diǎn)進(jìn)行標(biāo)記,使 render 時(shí)生成的 vnode 和模版中每個(gè)插值能夠?qū)?yīng)上。
以這種方式實(shí)現(xiàn)的數(shù)據(jù)映射,只有視圖層需要的兩個(gè)字符串?dāng)?shù)據(jù)會(huì)被同步到小程序的 Page 實(shí)例上,其余數(shù)據(jù)則被認(rèn)為與視圖無關(guān)則不會(huì)進(jìn)行同步。
如下圖所示,Vue 渲染出來的 vnode 會(huì)被以特定的數(shù)據(jù)結(jié)構(gòu)映射到 page 上,再同步到小程序視圖層。
以這種方式實(shí)現(xiàn)的數(shù)據(jù)映射,可以更好地支持 Vue 的模版語法,且更大限度地減少更新視圖時(shí)傳輸?shù)臄?shù)據(jù)量,從框架層面規(guī)避 setData 的性能問題。 事件代理小程序視圖觸發(fā)事件后,會(huì)將 event 對象通知到 Page 實(shí)例,那么我們只需要將視圖層中所有的事件都代理到 page.proxy 這個(gè)方法中,然后再靠這個(gè)方法從 Vue 的實(shí)例樹上找到對應(yīng)的 vm和 handler 做事件處理。為了實(shí)現(xiàn)這一目的,在構(gòu)建階段對模版進(jìn)行編譯時(shí),除了要將事件監(jiān)聽方法轉(zhuǎn)換為 proxy 以外,還需要通過 data- 在元素上標(biāo)記對應(yīng)的組件 compid 和節(jié)點(diǎn) nodeid。 <!-- 編譯前的 Vue 模版 .vue --> <div @click="onClick"></div> <!-- 編譯后的小程序模版 .wxml --> <view bindtap="proxy" data-compid="0" data-nodeid="0"></view> 復(fù)制代碼 事件觸發(fā)時(shí),proxy 方法會(huì)從 event 對象上獲取對應(yīng)的 id 信息和事件類型,進(jìn)而從 Vue 的根 vm 開始查找,最終在 vnode 上找到對應(yīng)的 handler 并執(zhí)行事件處理,完成小程序事件到 Vue 實(shí)例的事件代理。
現(xiàn)在與未來目前,megalo 已經(jīng)逐步在考拉的小程序應(yīng)用開發(fā)中投入使用,但 megalo 的數(shù)據(jù)映射方案早已通過 mpregular 在考拉的大量小程序應(yīng)用中得到了驗(yàn)證。現(xiàn)在,megalo 支持 typescript 開發(fā),支持支付寶小程序。 百度智能小程序的支持也在計(jì)劃之內(nèi),同時(shí),我們還計(jì)劃開發(fā)一個(gè)兼容個(gè)平臺(tái)的 UI 組件庫、API 庫,嘗試將跨 H5 和各小程序平臺(tái)的應(yīng)用開發(fā)之間的差異最小化,提升開發(fā)效率。 |
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)