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