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

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

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

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

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

一、小程序歷史

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

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

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

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

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

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

國內有一批做瀏覽器的廠商,嘗試去改進 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 優化、彈性擴容、攻擊防護、災備處理等方面還是有經驗欠缺的,但云開發將這些都封裝好了,真正做到僅專注業務實現,其它都委託雲廠商服務。

二、小程序架構

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

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

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

邏輯層就是上圖左上角這塊,小程序中開發的所有頁面 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 點:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

另外,微信的“關鍵幀動畫”、百度的“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 中完成繪製,卡頓也就產生了。

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

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

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

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

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

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

偽代碼 - 摘錄自 Weex 官網

複製代碼

<code>{      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                }            ]}/<code>

“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 繪製。

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

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

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

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

2. 數據 / 組件差量更新

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

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

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

(1)減少 setData 調用次數

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

<code>change:function(){
this.setData({a:1});
... // 其它業務邏輯
this.setData({b:2});
... // 其它業務邏輯
this.setData({c:3});
... // 其它業務邏輯
this.setData({d:4});
}/<code>

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

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

<code>change:function(){
this.a = 1;
... // 其它業務邏輯
this.b = 2;
... // 其它業務邏輯
this.c = 3;
... // 其它業務邏輯
this.d = 4;
}
/<code>


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

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

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

(2)數據差量更新

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

<code>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
})
}
})
/<code>

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

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

<code>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) // 設置差量數據
}
})
/<code>

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

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

<code>export default{
data(){
return {
list:['item1','item2','item3','item4']
}
},
methods:{
change:function(){
let newData = ['item5','item6','item7','item8'];
this.list.push(...newData) // 直接賦值,框架會自動計算差量數據

}
}
}
/<code>

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

(3)組件差量更新

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

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

假設當前有 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”控件的原生化,可以稍微發散一下。

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

小程序中原生 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”等組件中使用;
跨平臺開發領域老兵:我眼中小程序的當下和未來可能

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

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

(3) 混合渲染改進方案

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

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

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

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

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

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

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

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

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

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

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 是瀏覽器內核的一個插件機制,可用來解析“< embed >”。Android 端的同層渲染可基於 “< embed >”加 Chromium 內核擴展來實現,大致流程如下:

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

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

四、未來可能

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

1. 更優秀的用戶體驗

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

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

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

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

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

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

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、小程序性能優化等方面有豐富的實踐經驗。


分享到:


相關文章: