Categories
程式開發

跨平台開發領域老兵:我眼中小程序的當下和未來可能


隨著各大平台小程序的快速放量,開發者遇到越來越多的平台適配問題。各平台小程序的性能優化方法也各不相同,我們該如何應對? DCloud CTO 崔紅保在 GMTC深圳2019(全球大前端技術大會)分享了《小程序的未來方向》,介紹了小程序技術架構、性能卡點以及各平台優化方案,對於小程序未來的技術更迭,提出了小程序在未來可能的發展方向。本文根據演講內容整理而成。

簡單介紹一下我自己,中年碼農,跨平台開發領域的老兵。在那個翻蓋摩托羅拉手機代表著先進和時髦的年代,我就開始參與 “window mobile/j2me/symbain” 等系統的跨平台研發管理工作,可能很多同學都沒見過那些手機。到後來的移動互聯網時代及當下的小程序時代,我也一直在深度參與其中,持續輸出“Hybrid App”引擎、前端UI庫(mui)及小程序跨端開發框架(uni-app)。目前在 DCloud 任職 CTO,同時兼 “uni-app” 產品負責人。

羅馬不是一天建成的,小程序也不是一天發明的。小程序這種介於 H5 和 Native App 之間的特殊應用形態,從探索到成熟,經歷了哪些過程?我們首先帶大家回顧梳理一下。然後,從現有技術架構出發,分析小程序當下幾個主要性能坑點。各家小程序引擎為解決這些坑點,做了哪些完善工作。比如,大家知道小程序是以 Web 渲染為主、原生渲染為輔,那引入原生渲染後,引發了哪些新的問題?為解決這些問題,微信提出了同層渲染的方案,同層渲染在技術層面上又是如何實現的?最後從當前已知問題出發,對於小程序未來的技術更迭,拋出一些我們認為的可能方向,供大家參考。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 1

一、小程序歷史

HTML5 於 2007年在 W3C 立項,與 iPhone 發布同年。

喬布斯曾期待 HTML5 能幫助 iPhone 打造起應用生態系統。但 HTML5 的發展速度並不如預期,雖然它成功地打破了 IE+Flash 壟斷的局面,卻沒有達到承載優秀的移動互聯網體驗的地步。

蘋果公司在 iPhone 站穩腳跟後,緊接著發布了自己的 App Store,開啟了移動互聯網的原生應用時代。

大家知道現在手機端主要是 iOS、Android 兩大系統,實際上在早期有3大系統競爭,還有一個就是諾基亞的 MeeGo 系統,MeeGo 採用 C + HTML5 的雙模應用生態策略。然而,C 的開發難度太大,HTML5 體驗又不行,所以後來 MeeGo 就掉隊了;與之對應,Android 依靠 Java 技術生態,在競爭中脫穎而出。

於是在移動互聯網初期,應用生態被定了基調 —— 原生開發。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 2

國內有一批做瀏覽器的廠商,嘗試去改進 HTML5。比如,百度在2013年的百度世界大會上發布了輕應用,通過給 WebView 擴展原生能力,補充 JS API,讓 HTML5 應用可以實現更多功能。

這類業務發展的頂峰,是微信在2015年初發布的微信JS SDK,作為國內事實上最大的手機瀏覽器,微信為它的瀏覽器內核擴充了大量JS API,讓開發者可以用JS 調用微信支付、掃碼等眾多HTML5 做不到的功能。

不過這類業務沒有取得成功,HTML5 的問題不止是功能不足,性能體驗是更嚴重的問題。而體驗問題,不是簡單地擴展 JS 能力能搞定的。

與瀏覽器不同,Hybrid 應用是另一個細分領域,開發者使用 JS 編寫應用,為了讓 JS 應用更接近原生應用的功能體驗,這個行業的從業者做出了很多嘗試。我們DCloud 公司是業內主流Hybrid App 引擎提供方之一,我們提出了改進HTML5 的“性能功能”障礙的解決方案—— 通過工具、引擎優化、開發模式調整,讓開發者可以通過JS 寫出更接近原生App 體驗的應用。

多 WebView 模式,原生接管轉場動畫、下拉刷新、Tab 分頁,預載 WebView……各種優化技術不停迭代,終於讓 Hybrid 應用取得了性能體驗的突破。

Hybrid 應用和輕應用、微信 JS SDK 等基於瀏覽器增加方案相比,還有一個巨大的差別:一個是 Client/Server,一個是 Browser/Server。簡單來說,Hybrid 應用是 JS 編寫的需要安裝的 App,而輕應用是在線網頁。

C/S 的應用在每次頁面加載時,僅需要聯網獲取JSON 數據;而B/S 應用除了JSON 數據外,還需要每次從服務器加載頁面DOM、樣式、邏輯代碼,所以B/S 應用的頁面加載很慢,體驗很差。

可是這樣的 C/S 應用雖然體驗好,卻失去了 HTML5 的動態性,仍然需要安裝、更新,無法即點即用、直達二級頁面。

那麼 C/S 應用的動態性是否可以解決呢?對此, DCloud 率先提出了“流應用”概念,把之前Hybrid 應用裡的運行於客戶端的JS 代碼,先打包發佈到服務器,制定流式加載協議,手機端引擎動態下載這些JS 代碼到本地,並且為了第一次加載速度更快,實現了應用的邊下載邊運行。

就像流媒體的邊下邊播一樣,應用也可以實現邊用邊下。

在這套方案的保障下,終於解決了之前的各種難題:讓 JS 應用功能體驗達到原生,並且可即點即用、直達二級頁面。

接著就是微信小程序,最初的名字實際上是微信應用號,之後改名為小程序,2016年9月份內測,2017年1月正式發行,再之後阿里巴巴、手機廠商聯盟、百度、今日頭條,陸續推出了自己的小程序平台,小程序時代滾滾而來。

2018年9月,微信推出雲開發,這個功能我們認為是小程序發展歷史上的一個重要節點,它可以讓前端工程師從前到後將所有業務閉環實現,減少前後端的溝通成本、人力成本、運維成本,屬於開發模式的重大升級。與之前的前端同學既可通過JS/CSS 編寫前端UI,又可通過“Node.js” 寫後端業務,這種所謂全棧開發模式相比,雲開發有更好的優勢,因為前端同學對於DB 優化、彈性擴容、攻擊防護、災備處理等方面還是有經驗欠缺的,但云開發將這些都封裝好了,真正做到僅專注業務實現,其它都委託雲廠商服務。

二、小程序架構

這是一個比較通用的小程序架構,目前幾家小程序架構設計大致都是這樣的(快應用的區別是視圖層只有原生渲染)。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 3

大家知道小程序是一個邏輯、視圖層分離的架構。

邏輯層就是上圖左上角這塊,小程序中開發的所有頁面JS 代碼,最後都會打包合併到邏輯層,邏輯層除了執行開發者的業務JS 代碼外,還需處理小程序框架的內置邏輯,比如App 生命週期管理。

視圖層就是上圖右上角這塊,用戶可見的UI 效果、可觸發的交互事件在視圖層完成,視圖層包含Web 組件、原生組件兩種,也就是小程序是原生+Web 混合渲染的模式,這塊後面會詳細講。

邏輯層最後運行在JS CORE 或V8 環境中;JS CORE 既不是DOM 環境,也不是Node 環境,你是無法使用JS 中的DOM 或BOM 對象的,你能調用的僅僅是ECMAScript 標準規範中所給出的方法。

那如果你要發送網絡請求怎麼辦? window.XMLHttpRequest 是無法使用的(當然即使可以調用,在 iOS 的 WKWebView 中也存在更嚴格的跨域限制,會有問題)。這時候,網絡請求就需要通過原生的網絡模塊來發送,JS CORE 和原生之間呢,就需要這個JS Bridge 來通訊。

三、架構引發的性能坑點

小程序這種架構,最大的好處是新頁面加載可以並行,讓頁面加載更快,且不卡轉場動畫;但同時也引發了部分性能坑點,今天主要介紹3點:

跨平台開發領域老兵:我眼中小程序的當下和未來可能 4

1.邏輯層/視圖層通訊阻塞

我們從“swipeaction”這個例子講起,需求是用戶在列表項上向左滑動,右側隱藏的菜單跟隨用戶手勢平滑移動。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 5

若想在小程序架構上實現流暢的跟手滑動,是很困難的,為什麼?

回顧一下小程序架構,小程序的運行環境分為邏輯層和視圖層,分別由2個線程管理,小程序在視圖層與邏輯層兩個線程間提供了數據傳輸和事件系統。這樣的分離設計,帶來了顯而易見的好處:

環境隔離,既保證了安全性,同時也是一種性能提升的手段,邏輯和視圖分離,即使業務邏輯計算非常繁忙,也不會阻塞渲染和用戶在視圖層上的交互。

但同時也帶來了明顯的壞處:

視圖層(WebView)中不能運行 JS,而邏輯層 JS 又無法直接修改頁面 DOM,數據更新及事件系統只能靠線程間通訊,但跨線程通信的成本極高,特別是需要頻繁通信的場景。

基於這樣的架構設計,我們回到“swipeaction”,分析一次 touchmove 的操作,小程序內部的響應過程:

(1)用戶拖動列表項,視圖層觸發 touchmove 事件,經 Native 層中轉通知邏輯層(邏輯層、視圖層不是直接通訊的,需 Native 中轉),即下圖中的⓵、⓶兩步;

(2)邏輯層計算需移動的位置,然後再通過 setData 傳遞位置數據到視圖層,中間同樣會由微信客戶端(Native)做中轉,即下圖中的⓷、⓸兩步。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 6

實際上,用戶滑動過程中,touchmove 的回調觸發是非常頻繁的,每次回調都需要4個步驟的通訊過程,高頻率回調導致通訊成本大幅增加,極有可能導致頁面卡頓或抖動。為什麼會卡頓,因為通訊太過頻繁,視圖層無法在16ms內完成UI更新。

為解決這種通訊阻塞的問題,各家小程序都在逐步提供對應的解決方案,比如微信的 WXS、支付寶的 SJS、百度的 Filter,但每家小程序支持情況不同,詳細見下表。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 7

另外,微信的“關鍵幀動畫”、百度的“animation-view” Lottie 動畫,也是為減少頻繁通訊的一種變更方式。

其實,通訊阻塞是業界普遍存在的一個問題,不止小程序,“React Native”“Weex”等同樣存在通訊阻塞的問題。只不過“React Native”“Weex”的視圖層是原生渲染,而小程序是 Web 渲染。我們下面以“Weex”為例來說明。

大家知道,“Weex”底層使用的 JS-Native Bridge,這個 Bridge 使得 JS 和 Native 之間的通信會有固定的性能損耗。

繼續以上述“swipeaction”為例,要實現列表項菜單的跟手滑動,大致需經如下流程:

(1)在 UI 視圖上綁定 touch 事件(或 pan 事件);

(2)當手勢觸發時, Native UI 層將手勢事件通過 Bridge 傳遞給 JS邏輯層 , 這產生了一次 Native UI 到 JS 邏輯的通信,即下圖中的⓵、⓶兩步;

(3)JS 邏輯在接收到事件後,根據手指移動的偏移量驅動界面變化,這又會產生一次 JS 到 Native UI的通信,即下圖中的⓷、⓸兩步。

同樣,手勢回調事件觸發的頻率是非常高的,頻繁的的通信帶來的時間成本很可能導致界面無法在16ms中完成繪製,卡頓也就產生了。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 8

“Weex”為解決通訊阻塞,提供了“BindingX”解決方案,這是一種稱之為“Expression Binding”的機制,簡要介紹一下:

(1)接收手勢事件的視圖,在移動過程中的偏移量以“x,y”兩個變量表示;

(2)期望改變(跟隨移動)的視圖,變化的屬性為“translateX”和“translateY”,對應變化的偏移量以“f(x),f(y)”表達式表示;

(3)將”交互行為”以表達式的方式描述,並提前預置到 Native UI 層;

(4)交互觸發時,Native UI 根據其內置的表達式解析引擎,去執行表達式,並根據表達式執行的結果驅動視圖變換,這個過程無需和 JS 邏輯通訊。

偽代碼 – 摘錄自Weex官網

{
   
   anchor: foo_view.ref                    // ----> 这是"产生手势的视图"的引用  
   props:
            (
                {
                    element: foo_view.ref, // ----> 这是"期望改变的视图"的引用
                    expression: f(x) = x,  // ----> 这是具体的表达式
                    property: translateX   // ----> 这是期望改变的属性
                },
                {
                    element: foo_view.ref,
                    expression: f(y) = y,  // ----> y 属性
                    property: translateY
                }
            )
}

“React Native”同樣存在類似問題,為避免頻繁的通信,“React Native”生態也有對應方案,比如“Animated”組件及 Lottie 動畫支持。以“Animated”組件為例,為實現流暢的動畫效果,該組件採用了聲明式的API,在JS 端僅定義了輸入與輸出以及具體的transform 行為,而真正的動畫是通過Native Driver 在Native 層執行,這樣就避免了頻繁的通信。然而,聲明式的方式能夠定義的行為有限,無法勝任交互場景。

“uni-app”在App 端同樣面臨通訊阻塞的問題,我們目前的方案是採用類似微信WXS 的機制(內部叫“renderjs”),但放開了WXS 中無法獲取頁面DOM 元素的限制,比如下圖中多個小球同時移動的canvas 動畫,“uni-app”在App 端的實現方案是:

(1)renderjs 中獲取canvas對象;

(2)基於web的canvas繪製動畫,而非原生canvas繪製。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 9

Tips:大家需要注意,並不是所有場景都是原生性能更好,小程序架構下,如上多球同時移動的動畫,原生canvas並不如在WXS(renderjs)中直接調用Web canvas

下表總結了跨端框架在通訊阻塞方面的解決方案:

跨平台開發領域老兵:我眼中小程序的當下和未來可能 10

2.數據/組件差量更新

小程序架構存在通訊阻塞問題,廠商為解決這個問題,創造了“WXS”腳本語言及關鍵幀動畫等方式,但這些都是廠商維度的優化方案。我們作為小程序開發者,在性能優化方面,又能做哪些工作呢?

小程序開發性能優化,核心就是“setData”的調用,你能做只有兩件事情:

  • 盡量少調用“setData”;
  • 每次調用“setData”,傳遞盡可能少的數據量,即數據差量更新。

(1)減少setData調用次數

假設我們有更改多個變量值的需求,示例如下:

change:function(){
    this.setData({a:1});
    ... //其它业务逻辑
    this.setData({b:2});
    ... //其它业务逻辑
    this.setData({c:3});
    ... //其它业务逻辑
    this.setData({d:4});
}

如上,4次調用“setData”,會引發4次邏輯層、視圖層數據通訊。這種場景,開發者需意識到“setData”有極高的調用代價,自己需手動調整代碼,合併數據,減少數據通訊次數。

部分小程序三方框架已內置數據合併的能力,比如“uni-app”在 Vue runtime 上進行了深度定制,開發者無需關注“setData”的調用代價,可放心編寫如下代碼:

change:function(){
    this.a = 1;
    ... //其它业务逻辑
    this.b = 2;
    ... //其它业务逻辑
    this.c = 3;
    ... //其它业务逻辑
    this.d = 4;
}

如上4次賦值,uni-app 運行時會自動合併成“{“a”:1,“b”:2,“c”:3,“d”:4}”一條記錄,調用一次“setData”完成所有數據傳遞,大幅降低setData 的調用頻次,結果如下圖:

跨平台開發領域老兵:我眼中小程序的當下和未來可能 11

減少“setData”調用次數,還有個注意點:後台頁面(用戶不可見的頁面)應避免調用“setData”。

(2)數據差量更新

假設我們有一個 “列表頁 + 上拉加載” 的場景,初始化列表項為 “item1 ~ item4”,用戶上拉後要向列表追加4條新記錄 “item5 ~ item8”,小程序代碼如下:

page({
    data:{
        list:('item1','item2','item3','item4')
    },
    change:function(){
        let newData = ('item5','item6','item7','item8');
        this.data.list.push(...newData); //列表项新增记录
        this.setData({
            list:this.data.list
        })
    }
})

如上代碼,change 方法執行時,會將 list 中的 “item1 ~ item8”8個列表項通過“setData”全部傳輸過去,而實際上變化的數據只有“item5 ~ item8”。

開發者在這種場景下,應通過差量計算,僅通過“setData”傳遞變化的數據,如下是一個示例代碼:

page({
    data:{
        list:('item1','item2','item3','item4')
    },
    change:function(){
        // 通过长度获取下一次渲染的索引
        let index = this.data.list.length;
        let newData = ('item5','item6','item7','item8');
        let newDataObj = {};//变化的数据
        newData.forEach((item) => {
            newDataObj('list(' + (index++) + ')') = item;//通过list下标精确控制变更内容
        });
        this.setData(newDataObj) //设置差量数据
    }
})

每次都手動計算差量變更數據是繁瑣的,新手不理解小程序原理的話,也容易忽略這些性能點,給 App 埋下性能坑點。

此處,建議開發者選擇成熟的第三方小程序框架,這些框架已經自動封裝差量數據計算,對開發者更友好。比如,“uni-app”借鑒了“westore JSON Diff”庫,在調用setData 之前,會先比對歷史數據,精確高效計算出有變化的差量數據,然後再調用setData,僅傳輸變化的數據,這樣可實現傳遞數據量的最小化,提升通訊性能。如下,是一個示例代碼:

export default{
    data(){
        return {
            list:('item1','item2','item3','item4')
        }
    },
    methods:{
        change:function(){
            let newData = ('item5','item6','item7','item8');
            this.list.push(...newData) // 直接赋值,框架会自动计算差量数据
        }
    }
}

Tips:如上 change 方法執行時,僅會將 list 中的 “item5 ~ item8”4個新增列表項傳輸過去,實現了 setData 傳輸量的極簡化。

(3)組件差量更新

下圖是一個微博列表截圖:

跨平台開發領域老兵:我眼中小程序的當下和未來可能 12

假設當前有200條微博,用戶對某條微博點贊,需實時變更其點贊數據(狀態);在傳統模式下,一條微博的點贊狀態變更,會將整個頁面(Page) 的數據全部通過setData 傳遞過去,這個消耗是非常高的;而即使通過之前介紹,通過差量計算的方式獲取變更數據,這個Diff 遍歷範圍也很大,計算效率極低。

如何實現更高性能的微博點贊?這其實就是組件更新的典型場景。

合適的方式應該是,將每條微博封裝成一個組件,用戶點贊後,僅在當前組件範圍內計算差量數據(可理解為Diff 範圍縮小為原來的1/200),這樣效率才是最高的。

提醒大家注意,並不是所有小程序三方框架都已實現自定義組件,只有在基於自定義組件模式封裝的框架中,性能才會大幅提升;如果三方框架是基於老的“template”模板封裝的組件開發,則性能並不會有明顯改善,其Diff 對比範圍依然是Page 頁面級的。

3.混合渲染

大家知道,小程序當中有一類特殊的內置組件——原生組件,這類組件有別於 WebView 渲染的內置組件,他們是由原生客戶端渲染的。

小程序中的原生組件,從使用方式上來說,主要分為三類:

  • 通過配置項創建的:選項卡、導航欄,還有下拉刷新;
  • 通過組件名稱創建的,比如:camera、canvas、input、live-player、live-pusher、map、textarea、video;
  • 通過API接口創建的,比如:showModal、showActionSheet等。

除了上面提到的這些之外,其它基本都是 Web 渲染。所以說,小程序是混合渲染模式,Web渲染為主,原生渲染為輔。

(1)為什麼要引入混合渲染

接下來的問題,為什麼要引入原生渲染?以及為什麼僅針對這幾個組件提供了原生增強?其他組件為什麼沒有做原生實現?

這就需要我們針對每個組件單獨進行分析思考,這裡舉了幾個例子:

  • tabs/navigationbar:避免切換頁面白屏,提升新窗口進入時的用戶體驗。雖然不使用原生的tabbar和導航欄,可以做出更靈活的界面,但在切換頁面那短短300ms內,想保證頁面不白屏,還是需要使用渲染更快的原生tabbar和導航欄;
  • video:全屏後的滑動控制(聲音、進度、亮度等);
  • map:更流暢的雙指縮放、位置拖動;
  • input:Web 端的 input,鍵盤彈出時,只有“完成”按鈕,無法讓鍵盤顯示“發送”“下一個”這樣的按鍵。

提到“input”控件的原生化,可以稍微發散一下。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 13

小程序中原生input 控件的通用做法是,未獲取焦點時以Web 控件顯示,但在獲取焦點時,繪製一個原生input,蓋在Web input 上方,此時,用戶看見的鍵盤即為原生input 所對應的鍵盤,原生彈出鍵盤是可自定義按鈕(如上圖中下一步、send 按鈕)。這種做法存在一個缺陷: Web 和原生,畢竟不同渲染引擎,在鍵盤彈出和關閉時,對應 input 的“placeholder”會閃爍。

在 Android 平台,還有一種做法是基於 WebKit 改造,定制彈出鍵盤樣式;這種方案,在鍵盤彈出和關閉時,input 控件都是 Web 實現的,故不存在“placeholder”閃爍的問題。

(2)混合渲染引發的問題

原生組件雖然帶來了更豐富的特性及更好的性能,但同時也引入了一些新的問題,比如:

  • 層級問題:原生永遠在最高層,無法通過“z-index”設置不同元素的層級,無法與view、image 等內置組件相互覆蓋,不支持在“picker-view”“scroll-view”“swiper”等組件中使用;

跨平台開發領域老兵:我眼中小程序的當下和未來可能 14

  • 通訊問題:比如一個長列表中內嵌視頻組件,頁面滾動時,需通知原生的視頻組件一起滾動,通訊阻塞,可能導致組件抖動或拖影;
  • 字體問題:在 Android 手機上,調整系統主題字體,所有原生渲染的控件的字體都會變化,而 Web 渲染的字體則不會變化。如下圖,系統rom 字體為一款“你的名字”的三方字體,設置後,小程序頂部標題字體變了,底部選項卡字體也變了,但小程序中間內容區字體不變,這就是比較尷尬的一種情況,一個頁面,兩種字體。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 15

當然,並不是所有小程序都存在這種問題,部分小程序通過修改自帶的WebView 內核,實現了WebView 也可以使用rom 主題字體,比如微信、QQ、支付寶;其他小程序(百度、頭條), WebView 仍然無法渲染為rom主題字體。

(3) 混合渲染改進方案

既然混合渲染有這些問題,對應就會有解決方案,目前已有的方案如下。

  • 方案:創造層級更高的組件

既然其它組件無法覆蓋到原生組件上,那就創造出一種新的組件,讓這個新組件可以覆蓋到 video 或 map 上。 “cover-view/cover-image”就是基於這種需求創造出來的新組件;其實它們也是原生組件,只不過層級略高,可以覆蓋在 map、video、canvas、camera 等原生組件上。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 16

目前除了字節跳動外,其它幾家小程序均已支持“cover-view/cover-image”。

cover-view/cover-image 在一定程度上緩解了分層覆蓋的問題,但也有部分限制,比如嚴格的嵌套順序。

  • 方案:消除分層,同層渲染

既然分層有問題,那就消除分層,從2層變成1層,所有組件都在一個層中,“z-index”豈不就可生效了?

跨平台開發領域老兵:我眼中小程序的當下和未來可能 17

這個小目標說起來簡單,具體實現還是很複雜的。

4.同層渲染

拋開小程序當前架構實現,解決混合渲染最直接的方案,應該更換渲染引擎,全部基於原生渲染,video/map 和 image/view 均為原生控件,層級相同,層級遮蓋問題自然消失。這正是“uni-app”在 App 端的推薦方案。

當前 Web 渲染為主、原生渲染為輔的主流小程序現狀,如何實現同層渲染?

基於我們的分析研究,這裡簡單講解一下同層渲染實現的方案,和微信真實實現可能會有出入(目前僅微信一家實現了同層渲染)。

(1) iOS平台

小程序在 iOS 端使用 WKWebView 進行渲染,WKWebView 在內部採用的是分層渲染,一般會將多個 DOM 節點,合併到一個層上進行渲染。因此,DOM 節點和層之間不存在一一對應關係。但是,一旦將一個DOM 節點的CSS 屬性設置為“overflow: scroll” 後,WKWebView 便會為其生成一個WKChildScrollView,且WebKit 內核已經處理了WKChildScrollView 與其他DOM 節點之間的層級關係,這時DOM節點就和層之間有一一對應關係了。

小程序 iOS 端的同層渲染可基於 WKChildScrollView 實現,主要流程如下:

  • 創建一個 DOM 節點並設置其 CSS 屬性為 overflow: scroll;
  • 通知原生層查找到該 DOM 節點對應的原生 WKChildScrollView 組件;
  • 將原生組件掛載到該 WKChildScrollView 節點上作為其子 View。

(2)Android 平台

小程序在 Android 端採用 Chromium 作為 WebView 渲染層,和 iOS的 WKWebView 不同,是統一渲染的,不會分層渲染。但 Chromium 支持 WebPlugin 機制,WebPlugin 是瀏覽器內核的一個插件機制,可用來解析“”。 Android 端的同層渲染可基於 “”加 Chromium 內核擴展來實現,大致流程如下:

  • 原生層創建一個原生組件(如video);
  • WebView 創建一個 “”節點並指定其類型為 video;
  • Chromium 內核創建一個 WebPlugin 實例* 並生成一個 RenderLayer;
  • 原生層將原生組件的畫面繪製到 RenderLayer 所綁定的 SurfaceTexture 上;
  • Chromium 渲染該 RenderLayer。

這個流程相當於給 WebView 添加了一個外置插件,且“”節點是真正的 DOM 節點,可將更多的樣式作用於該節點上。

四、未來可能

如果要探討小程序接下來的技術升級方向,我們認為應該在用戶體驗、開發效率兩個方向上努力。

1.更優秀的用戶體驗

先說用戶體驗的問題,主要也是兩個方面:

  • 解決現有的性能坑點,比如前面分析的這幾項,通訊阻塞、分層限制等,這裡不再贅述;
  • 支持更多 App 的體驗,更自由靈活的配置,比如,高斯模糊。

如果你也想快速搭建的自己的小程序引擎,並更優的解決如上體驗問題,該怎麼辦?

uni-app 發行到 App 端,實際上就是一個完整的小程序引擎,DCloud 會在近期將這個引擎完整開源,歡迎大家基於 uni-app 小程序 SDK 快速打造自己的小程序平台。

uni-app 小程序 SDK 具備如下幾個特徵:

  • 性能:支持 Native 渲染,擴展 WXS,更高的通訊性能;
  • 開放性:更靈活的配置,支持更多 App 的體驗;
  • 開源不受限:無需簽訂任何協議,拿走就用;
  • 生態豐富:支持微信小程序自定義組件,支持所有“uni-app”插件,且其插件市場目前已有上千款成熟插件。

跨平台開發領域老兵:我眼中小程序的當下和未來可能 18

2.開發效率

開發效率應該從跨端、跨雲兩個維度進行分析。

(1)跨端開發

目前的小程序都帶有明顯的廠家屬性,每個廠家各不相同。比如,阿里內部有多套小程序(支付寶、淘寶、釘釘等),幸好阿里內部目前已基本統一。但騰訊體系下,微信和 QQ 小程序依然是兩隊人馬,兩套規範。

小程序之前是手機端的,2019年360出了 PC 端小程序。

接下來,會不會還有其它廠家推出自己的小程序?會不會有新的端的出現?比如,面向電視的小程序、面向車載的小程序?

一切皆有可能。

逐水草而居是人類的本能,追求流量依然是互聯網的製勝法寶。當前的小程序宿主,都是億級流量入口,且各家流量政策不同。比如,微信的流量雖然很大,但有各種限制;百度和頭條是支持廣告投放的,通過廣告投放,可以快速獲得大量較為精準的用戶;百度小程序還有個Web 化的功能,可以將Web 的搜索流量,轉化成小程序的流量。

面對眾多小程序平台及各自巨大的入口流量,開發者如何應對?

等待 W3C 的小程序標準統一,短期不太現實。當下,若想將業務快速觸達多家小程序,借助跨端框架應該是唯一可行的方案。

(2)跨雲開發

開發商借助“uni-app”或其它跨端框架,雖然已可以開發所有前端應用。但仍然需要雇傭 PHP 或 Java 等後台開發人員,既有後端人員成本,又有前/後端溝通成本。

騰訊、阿里、百度小程序雖陸續上線了雲開發,但它們均只支持自己的小程序,無法跨端,分散的服務器對開發商更不可取。

故我們認為跨廠商的Serverless 是接下來的一個重點需求,開發者在一個雲端存儲所有業務數據及後端邏輯,然後將前端小程序發行到各家小程序平台,也就是“一云多端”模式。

五、小結

基於小程序的現狀,我們也許可以總結一下小程序技術上的可能方向:

  1. 其它小程序拉齊與微信的差距,讓開發者可以做出足夠高性能的應用服務;
  2. 所有小程序應拉齊和 App 的體驗差距,雖然功能 API 方面仍有不足,但操作性能和交互體驗,不應該弱於 App;
  3. 跨端框架 + Serverless,讓開發者更輕鬆,讓企業更高效。

作者介紹:

崔紅保,DCloud CTO,Uni-App 團隊負責人,開發了2個 Github Star 上萬的流行項目。有10年以上研發管理經驗,在跨平台引擎、前端 UI、小程序性能優化等方面有豐富的實踐經驗。

原文鏈接:
https://gmtc.infoq.cn/2019/shenzhen/