小程序模板網(wǎng)

megalo -- 網(wǎng)易考拉小程序解決方案

發(fā)布時(shí)間:2018-10-29 16:15 所屬欄目:小程序開發(fā)教程

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)]有限制,官方的用法都支持。

<!-- 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

支持 slot 和 scoped-slot。

<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>

復(fù)雜表達(dá)式 & filter

可以在模版里面寫復(fù)雜表達(dá)式、調(diào)用實(shí)例上的方法,當(dāng)然也可以用更簡潔的 filter 語法,跟平時(shí)用 Vue 開發(fā)一樣。

<div>
  <div>{{ message.toUpperCase() }}</div>
  <div>{{ toUpperCase( message ) }</div>
  <div>{{ message | toUpperCase }}</div>
</div>

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ì)同步到小程序的對象上的。

<div>{{ user.name }}</div>
<script>
export default {
  data() {
    return {
      user: {
        name: 'kaola',
        age: 3,
        favorite: [
          'encalyptus',
          'sleeping'
        ]
      }
    }
  }
}
</script>

支持更多平臺(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' 。

const { createMegaloTarget } = require('@megalo/target');
module.exports = {
  target: createMegaloTarget( {
    compiler: require('@megalo/template-compiler'),
    platform: 'wechat'
  } )
  // 其他 webpack 配置,如 vue-loader 等
};

接著,就可以像開發(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)上。

<!-- 編譯前的 Vue 模版 .vue -->
<div :class=“classObj”>
  {{ date | format( 'YYYY' ) }}
</div>

<!-- 編譯后的小程序模版 .wxml -->
<view class="{{ node_1.class }}">
  {{ node_1.text }}
</view>

以這種方式實(shí)現(xiàn)的數(shù)據(jù)映射,只有視圖層需要的兩個(gè)字符串?dāng)?shù)據(jù)會(huì)被同步到小程序的 Page 實(shí)例上,其余數(shù)據(jù)則被認(rèn)為與視圖無關(guān)則不會(huì)進(jìn)行同步。

export default {
  data() {
    return {
      classObj: {
        'kaola': true
      },
      date: new Date(),
      users: {
        // big object
      }
    }
  }
}

如下圖所示,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ā)效率。



易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開源 碼云倉庫:starfork
本文地址:http://22321a.com/wxmini/doc/course/24915.html 復(fù)制鏈接 如需定制請聯(lián)系易優(yōu)客服咨詢:800182392 點(diǎn)擊咨詢
QQ在線咨詢