国产粉嫩无码不卡在线观看,酒店大战丝袜高跟鞋人妻,特级精品毛片免费观看,欧美亚洲日本国产综合在线

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

隨著各大平臺(tái)小程序的快速放量,開(kāi)發(fā)者遇到越來(lái)越多的平臺(tái)適配問(wèn)題。各平臺(tái)小程序的性能優(yōu)化方法也各不相同,我們?cè)撊绾螒?yīng)對(duì)?DCloud CTO 崔紅保在 GMTC 深圳 2019(全球大前端技術(shù)大會(huì))分享了《小程序的未來(lái)方向》,介紹了小程序技術(shù)架構(gòu)、性能卡點(diǎn)以及各平臺(tái)優(yōu)化方案,對(duì)于小程序未來(lái)的技術(shù)更迭,提出了小程序在未來(lái)可能的發(fā)展方向。本文根據(jù)演講內(nèi)容整理而成。

簡(jiǎn)單介紹一下我自己,中年碼農(nóng),跨平臺(tái)開(kāi)發(fā)領(lǐng)域的老兵。在那個(gè)翻蓋摩托羅拉手機(jī)代表著先進(jìn)和時(shí)髦的年代,我就開(kāi)始參與 “window mobile/j2me/symbain” 等系統(tǒng)的跨平臺(tái)研發(fā)管理工作,可能很多同學(xué)都沒(méi)見(jiàn)過(guò)那些手機(jī)。到后來(lái)的移動(dòng)互聯(lián)網(wǎng)時(shí)代及當(dāng)下的小程序時(shí)代,我也一直在深度參與其中,持續(xù)輸出“Hybrid App”引擎、前端 UI 庫(kù)(mui)及小程序跨端開(kāi)發(fā)框架(uni-app)。目前在 DCloud 任職 CTO,同時(shí)兼 “uni-app” 產(chǎn)品負(fù)責(zé)人。

羅馬不是一天建成的,小程序也不是一天發(fā)明的。小程序這種介于 H5 和 Native App 之間的特殊應(yīng)用形態(tài),從探索到成熟,經(jīng)歷了哪些過(guò)程?我們首先帶大家回顧梳理一下。然后,從現(xiàn)有技術(shù)架構(gòu)出發(fā),分析小程序當(dāng)下幾個(gè)主要性能坑點(diǎn)。各家小程序引擎為解決這些坑點(diǎn),做了哪些完善工作。比如,大家知道小程序是以 Web 渲染為主、原生渲染為輔,那引入原生渲染后,引發(fā)了哪些新的問(wèn)題?為解決這些問(wèn)題,微信提出了同層渲染的方案,同層渲染在技術(shù)層面上又是如何實(shí)現(xiàn)的?最后從當(dāng)前已知問(wèn)題出發(fā),對(duì)于小程序未來(lái)的技術(shù)更迭,拋出一些我們認(rèn)為的可能方向,供大家參考。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

一、小程序歷史

HTML5 于 2007 年在 W3C 立項(xiàng),與 iPhone 發(fā)布同年。

喬布斯曾期待 HTML5 能幫助 iPhone 打造起應(yīng)用生態(tài)系統(tǒng)。但 HTML5 的發(fā)展速度并不如預(yù)期,雖然它成功地打破了 IE Flash 壟斷的局面,卻沒(méi)有達(dá)到承載優(yōu)秀的移動(dòng)互聯(lián)網(wǎng)體驗(yàn)的地步。

蘋果公司在 iPhone 站穩(wěn)腳跟后,緊接著發(fā)布了自己的 App Store,開(kāi)啟了移動(dòng)互聯(lián)網(wǎng)的原生應(yīng)用時(shí)代。

大家知道現(xiàn)在手機(jī)端主要是 iOS、Android 兩大系統(tǒng),實(shí)際上在早期有 3 大系統(tǒng)競(jìng)爭(zhēng),還有一個(gè)就是諾基亞的 MeeGo 系統(tǒng),MeeGo 采用 C HTML5 的雙模應(yīng)用生態(tài)策略。然而,C 的開(kāi)發(fā)難度太大,HTML5 體驗(yàn)又不行,所以后來(lái) MeeGo 就掉隊(duì)了;與之對(duì)應(yīng),Android 依靠 Java 技術(shù)生態(tài),在競(jìng)爭(zhēng)中脫穎而出。

于是在移動(dòng)互聯(lián)網(wǎng)初期,應(yīng)用生態(tài)被定了基調(diào) —— 原生開(kāi)發(fā)。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

國(guó)內(nèi)有一批做瀏覽器的廠商,嘗試去改進(jìn) HTML5。比如,百度在 2013 年的百度世界大會(huì)上發(fā)布了輕應(yīng)用,通過(guò)給 WebView 擴(kuò)展原生能力,補(bǔ)充 JS API,讓 HTML5 應(yīng)用可以實(shí)現(xiàn)更多功能。

這類業(yè)務(wù)發(fā)展的頂峰,是微信在 2015 年初發(fā)布的微信 JS SDK,作為國(guó)內(nèi)事實(shí)上最大的手機(jī)瀏覽器,微信為它的瀏覽器內(nèi)核擴(kuò)充了大量 JS API,讓開(kāi)發(fā)者可以用 JS 調(diào)用微信支付、掃碼等眾多 HTML5 做不到的功能。

不過(guò)這類業(yè)務(wù)沒(méi)有取得成功,HTML5 的問(wèn)題不止是功能不足,性能體驗(yàn)是更嚴(yán)重的問(wèn)題。而體驗(yàn)問(wèn)題,不是簡(jiǎn)單地?cái)U(kuò)展 JS 能力能搞定的。

與瀏覽器不同,Hybrid 應(yīng)用是另一個(gè)細(xì)分領(lǐng)域,開(kāi)發(fā)者使用 JS 編寫應(yīng)用,為了讓 JS 應(yīng)用更接近原生應(yīng)用的功能體驗(yàn),這個(gè)行業(yè)的從業(yè)者做出了很多嘗試。我們 DCloud 公司是業(yè)內(nèi)主流 Hybrid App 引擎提供方之一,我們提出了改進(jìn) HTML5 的“性能功能”障礙的解決方案 —— 通過(guò)工具、引擎優(yōu)化、開(kāi)發(fā)模式調(diào)整,讓開(kāi)發(fā)者可以通過(guò) JS 寫出更接近原生 App 體驗(yàn)的應(yīng)用。

多 WebView 模式,原生接管轉(zhuǎn)場(chǎng)動(dòng)畫、下拉刷新、Tab 分頁(yè),預(yù)載 WebView……各種優(yōu)化技術(shù)不停迭代,終于讓 Hybrid 應(yīng)用取得了性能體驗(yàn)的突破。

Hybrid 應(yīng)用和輕應(yīng)用、微信 JS SDK 等基于瀏覽器增加方案相比,還有一個(gè)巨大的差別:一個(gè)是 Client/Server,一個(gè)是 Browser/Server。簡(jiǎn)單來(lái)說(shuō),Hybrid 應(yīng)用是 JS 編寫的需要安裝的 App,而輕應(yīng)用是在線網(wǎng)頁(yè)。

C/S 的應(yīng)用在每次頁(yè)面加載時(shí),僅需要聯(lián)網(wǎng)獲取 JSON 數(shù)據(jù);而 B/S 應(yīng)用除了 JSON 數(shù)據(jù)外,還需要每次從服務(wù)器加載頁(yè)面 DOM、樣式、邏輯代碼,所以 B/S 應(yīng)用的頁(yè)面加載很慢,體驗(yàn)很差。

可是這樣的 C/S 應(yīng)用雖然體驗(yàn)好,卻失去了 HTML5 的動(dòng)態(tài)性,仍然需要安裝、更新,無(wú)法即點(diǎn)即用、直達(dá)二級(jí)頁(yè)面。

那么 C/S 應(yīng)用的動(dòng)態(tài)性是否可以解決呢?對(duì)此, DCloud 率先提出了“流應(yīng)用”概念,把之前 Hybrid 應(yīng)用里的運(yùn)行于客戶端的 JS 代碼,先打包發(fā)布到服務(wù)器,制定流式加載協(xié)議,手機(jī)端引擎動(dòng)態(tài)下載這些 JS 代碼到本地,并且為了第一次加載速度更快,實(shí)現(xiàn)了應(yīng)用的邊下載邊運(yùn)行。

就像流媒體的邊下邊播一樣,應(yīng)用也可以實(shí)現(xiàn)邊用邊下。

在這套方案的保障下,終于解決了之前的各種難題:讓 JS 應(yīng)用功能體驗(yàn)達(dá)到原生,并且可即點(diǎn)即用、直達(dá)二級(jí)頁(yè)面。

接著就是微信小程序,最初的名字實(shí)際上是微信應(yīng)用號(hào),之后改名為小程序,2016 年 9 月份內(nèi)測(cè),2017 年 1 月正式發(fā)行,再之后阿里巴巴、手機(jī)廠商聯(lián)盟、百度、今日頭條,陸續(xù)推出了自己的小程序平臺(tái),小程序時(shí)代滾滾而來(lái)。

2018 年 9 月,微信推出云開(kāi)發(fā),這個(gè)功能我們認(rèn)為是小程序發(fā)展歷史上的一個(gè)重要節(jié)點(diǎn),它可以讓前端工程師從前到后將所有業(yè)務(wù)閉環(huán)實(shí)現(xiàn),減少前后端的溝通成本、人力成本、運(yùn)維成本,屬于開(kāi)發(fā)模式的重大升級(jí)。與之前的前端同學(xué)既可通過(guò) JS/CSS 編寫前端 UI,又可通過(guò) “Node.js” 寫后端業(yè)務(wù),這種所謂全棧開(kāi)發(fā)模式相比,云開(kāi)發(fā)有更好的優(yōu)勢(shì),因?yàn)榍岸送瑢W(xué)對(duì)于 DB 優(yōu)化、彈性擴(kuò)容、攻擊防護(hù)、災(zāi)備處理等方面還是有經(jīng)驗(yàn)欠缺的,但云開(kāi)發(fā)將這些都封裝好了,真正做到僅專注業(yè)務(wù)實(shí)現(xiàn),其它都委托云廠商服務(wù)。

二、小程序架構(gòu)

這是一個(gè)比較通用的小程序架構(gòu),目前幾家小程序架構(gòu)設(shè)計(jì)大致都是這樣的(快應(yīng)用的區(qū)別是視圖層只有原生渲染)。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

大家知道小程序是一個(gè)邏輯、視圖層分離的架構(gòu)。

邏輯層就是上圖左上角這塊,小程序中開(kāi)發(fā)的所有頁(yè)面 JS 代碼,最后都會(huì)打包合并到邏輯層,邏輯層除了執(zhí)行開(kāi)發(fā)者的業(yè)務(wù) JS 代碼外,還需處理小程序框架的內(nèi)置邏輯,比如 App 生命周期管理。

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

邏輯層最后運(yùn)行在 JS CORE 或 V8 環(huán)境中;JS CORE 既不是 DOM 環(huán)境,也不是 Node 環(huán)境,你是無(wú)法使用 JS 中的 DOM 或 BOM 對(duì)象的,你能調(diào)用的僅僅是 ECMAScript 標(biāo)準(zhǔn)規(guī)范中所給出的方法。

那如果你要發(fā)送網(wǎng)絡(luò)請(qǐng)求怎么辦?window.XMLHttpRequest 是無(wú)法使用的(當(dāng)然即使可以調(diào)用,在 iOS 的 WKWebView 中也存在更嚴(yán)格的跨域限制,會(huì)有問(wèn)題)。這時(shí)候,網(wǎng)絡(luò)請(qǐng)求就需要通過(guò)原生的網(wǎng)絡(luò)模塊來(lái)發(fā)送,JS CORE 和原生之間呢,就需要這個(gè) JS Bridge 來(lái)通訊。

三、架構(gòu)引發(fā)的性能坑點(diǎn)

小程序這種架構(gòu),最大的好處是新頁(yè)面加載可以并行,讓頁(yè)面加載更快,且不卡轉(zhuǎn)場(chǎng)動(dòng)畫;但同時(shí)也引發(fā)了部分性能坑點(diǎn),今天主要介紹 3 點(diǎn):

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

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

我們從“swipeaction”這個(gè)例子講起,需求是用戶在列表項(xiàng)上向左滑動(dòng),右側(cè)隱藏的菜單跟隨用戶手勢(shì)平滑移動(dòng)。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

若想在小程序架構(gòu)上實(shí)現(xiàn)流暢的跟手滑動(dòng),是很困難的,為什么?

回顧一下小程序架構(gòu),小程序的運(yùn)行環(huán)境分為邏輯層和視圖層,分別由 2 個(gè)線程管理,小程序在視圖層與邏輯層兩個(gè)線程間提供了數(shù)據(jù)傳輸和事件系統(tǒng)。這樣的分離設(shè)計(jì),帶來(lái)了顯而易見(jiàn)的好處:

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

但同時(shí)也帶來(lái)了明顯的壞處:

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

基于這樣的架構(gòu)設(shè)計(jì),我們回到“swipeaction”,分析一次 touchmove 的操作,小程序內(nèi)部的響應(yīng)過(guò)程:

(1)用戶拖動(dòng)列表項(xiàng),視圖層觸發(fā) touchmove 事件,經(jīng) Native 層中轉(zhuǎn)通知邏輯層(邏輯層、視圖層不是直接通訊的,需 Native 中轉(zhuǎn)),即下圖中的?、?兩步;

(2)邏輯層計(jì)算需移動(dòng)的位置,然后再通過(guò) setData 傳遞位置數(shù)據(jù)到視圖層,中間同樣會(huì)由微信客戶端(Native)做中轉(zhuǎn),即下圖中的?、?兩步。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

實(shí)際上,用戶滑動(dòng)過(guò)程中,touchmove 的回調(diào)觸發(fā)是非常頻繁的,每次回調(diào)都需要 4 個(gè)步驟的通訊過(guò)程,高頻率回調(diào)導(dǎo)致通訊成本大幅增加,極有可能導(dǎo)致頁(yè)面卡頓或抖動(dòng)。為什么會(huì)卡頓,因?yàn)橥ㄓ嵦^(guò)頻繁,視圖層無(wú)法在 16ms 內(nèi)完成 UI 更新。

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

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

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

其實(shí),通訊阻塞是業(yè)界普遍存在的一個(gè)問(wèn)題,不止小程序,“React Native”“Weex”等同樣存在通訊阻塞的問(wèn)題。只不過(guò)“React Native”“Weex”的視圖層是原生渲染,而小程序是 Web 渲染。我們下面以“Weex”為例來(lái)說(shuō)明。

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

繼續(xù)以上述“swipeaction”為例,要實(shí)現(xiàn)列表項(xiàng)菜單的跟手滑動(dòng),大致需經(jīng)如下流程:

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

(2)當(dāng)手勢(shì)觸發(fā)時(shí), Native UI 層將手勢(shì)事件通過(guò) Bridge 傳遞給 JS 邏輯層 , 這產(chǎn)生了一次 Native UI 到 JS 邏輯的通信,即下圖中的?、?兩步 ;

(3)JS 邏輯在接收到事件后,根據(jù)手指移動(dòng)的偏移量驅(qū)動(dòng)界面變化,這又會(huì)產(chǎn)生一次 JS 到 Native UI 的通信,即下圖中的?、?兩步。

同樣,手勢(shì)回調(diào)事件觸發(fā)的頻率是非常高的,頻繁的的通信帶來(lái)的時(shí)間成本很可能導(dǎo)致界面無(wú)法在 16ms 中完成繪制,卡頓也就產(chǎn)生了。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

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

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

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

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

(4)交互觸發(fā)時(shí),Native UI 根據(jù)其內(nèi)置的表達(dá)式解析引擎,去執(zhí)行表達(dá)式,并根據(jù)表達(dá)式執(zhí)行的結(jié)果驅(qū)動(dòng)視圖變換,這個(gè)過(guò)程無(wú)需和 JS 邏輯通訊。

偽代碼 – 摘錄自 Weex 官網(wǎng)

復(fù)制代碼

{ anchor: foo_view.ref // ----> 這是 " 產(chǎn)生手勢(shì)的視圖 " 的引用 props: [ { element: foo_view.ref, // ----> 這是 " 期望改變的視圖 " 的引用 expression: f(x) = x, // ----> 這是具體的表達(dá)式 property: translateX // ----> 這是期望改變的屬性 }, { element: foo_view.ref, expression: f(y) = y, // ----> y 屬性 property: translateY } ]}

“React Native”同樣存在類似問(wèn)題,為避免頻繁的通信,“React Native”生態(tài)也有對(duì)應(yīng)方案,比如“Animated”組件及 Lottie 動(dòng)畫支持。以 “Animated”組件為例,為實(shí)現(xiàn)流暢的動(dòng)畫效果,該組件采用了聲明式的 API,在 JS 端僅定義了輸入與輸出以及具體的 transform 行為,而真正的動(dòng)畫是通過(guò) Native Driver 在 Native 層執(zhí)行,這樣就避免了頻繁的通信。然而,聲明式的方式能夠定義的行為有限,無(wú)法勝任交互場(chǎng)景。

“uni-app”在 App 端同樣面臨通訊阻塞的問(wèn)題,我們目前的方案是采用類似微信 WXS 的機(jī)制(內(nèi)部叫“renderjs”),但放開(kāi)了 WXS 中無(wú)法獲取頁(yè)面 DOM 元素的限制,比如下圖中多個(gè)小球同時(shí)移動(dòng)的 canvas 動(dòng)畫,“uni-app”在 App 端的實(shí)現(xiàn)方案是:

(1)renderjs 中獲取 canvas 對(duì)象;

(2)基于 web 的 canvas 繪制動(dòng)畫,而非原生 canvas 繪制。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

Tips:大家需要注意,并不是所有場(chǎng)景都是原生性能更好,小程序架構(gòu)下,如上多球同時(shí)移動(dòng)的動(dòng)畫,原生 canvas 并不如在 WXS(renderjs)中直接調(diào)用 Web canvas

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

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

2. 數(shù)據(jù) / 組件差量更新

小程序架構(gòu)存在通訊阻塞問(wèn)題,廠商為解決這個(gè)問(wèn)題,創(chuàng)造了“WXS”腳本語(yǔ)言及關(guān)鍵幀動(dòng)畫等方式,但這些都是廠商維度的優(yōu)化方案。我們作為小程序開(kāi)發(fā)者,在性能優(yōu)化方面,又能做哪些工作呢?

小程序開(kāi)發(fā)性能優(yōu)化,核心就是“setData”的調(diào)用,你能做只有兩件事情:

  • 盡量少調(diào)用“setData”;
  • 每次調(diào)用“setData”,傳遞盡可能少的數(shù)據(jù)量,即數(shù)據(jù)差量更新。

(1)減少 setData 調(diào)用次數(shù)

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

change:function(){? ? this.setData({a:1});? ? ... // 其它業(yè)務(wù)邏輯? ? this.setData({b:2});? ? ... // 其它業(yè)務(wù)邏輯? ? this.setData({c:3});? ? ... // 其它業(yè)務(wù)邏輯? ? this.setData({d:4});}

如上,4 次調(diào)用“setData”,會(huì)引發(fā) 4 次邏輯層、視圖層數(shù)據(jù)通訊。這種場(chǎng)景,開(kāi)發(fā)者需意識(shí)到“setData”有極高的調(diào)用代價(jià),自己需手動(dòng)調(diào)整代碼,合并數(shù)據(jù),減少數(shù)據(jù)通訊次數(shù)。

部分小程序三方框架已內(nèi)置數(shù)據(jù)合并的能力,比如“uni-app”在 Vue runtime 上進(jìn)行了深度定制,開(kāi)發(fā)者無(wú)需關(guān)注“setData”的調(diào)用代價(jià),可放心編寫如下代碼:

change:function(){? ? this.a = 1;? ? ... // 其它業(yè)務(wù)邏輯? ? this.b = 2;? ? ... // 其它業(yè)務(wù)邏輯? ? this.c = 3;? ? ... // 其它業(yè)務(wù)邏輯? ? this.d = 4;}

如上 4 次賦值,uni-app 運(yùn)行時(shí)會(huì)自動(dòng)合并成“{“a”:1,“b”:2,“c”:3,“d”:4}”一條記錄,調(diào)用一次“setData”完成所有數(shù)據(jù)傳遞,大幅降低 setData 的調(diào)用頻次,結(jié)果如下圖:

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

減少“setData”調(diào)用次數(shù),還有個(gè)注意點(diǎn):后臺(tái)頁(yè)面(用戶不可見(jiàn)的頁(yè)面)應(yīng)避免調(diào)用“setData”。

(2)數(shù)據(jù)差量更新

假設(shè)我們有一個(gè) “列表頁(yè) 上拉加載” 的場(chǎng)景,初始化列表項(xiàng)為 “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); // 列表項(xiàng)新增記錄? ? ? ? this.setData({? ? ? ? ? ? list:this.data.list? ? ? ? })? ? }})

如上代碼,change 方法執(zhí)行時(shí),會(huì)將 list 中的 “item1 ~ item8”8 個(gè)列表項(xiàng)通過(guò)“setData”全部傳輸過(guò)去,而實(shí)際上變化的數(shù)據(jù)只有“item5 ~ item8”。

開(kāi)發(fā)者在這種場(chǎng)景下,應(yīng)通過(guò)差量計(jì)算,僅通過(guò)“setData”傳遞變化的數(shù)據(jù),如下是一個(gè)示例代碼:

page({? ? data:{? ? ? ? list:['item1','item2','item3','item4']? ? },? ? change:function(){? ? ? ? // 通過(guò)長(zhǎng)度獲取下一次渲染的索引? ? ? ? let index = this.data.list.length;? ? ? ? let newData = ['item5','item6','item7','item8'];? ? ? ? let newDataObj = {};// 變化的數(shù)據(jù)? ? ? ? newData.forEach((item) => {? ? ? ? ? ? newDataObj['list[' (index ) ']'] = item;// 通過(guò) list 下標(biāo)精確控制變更內(nèi)容? ? ? ? });? ? ? ? this.setData(newDataObj) // 設(shè)置差量數(shù)據(jù)? ? }})

每次都手動(dòng)計(jì)算差量變更數(shù)據(jù)是繁瑣的,新手不理解小程序原理的話,也容易忽略這些性能點(diǎn),給 App 埋下性能坑點(diǎn)。

此處,建議開(kāi)發(fā)者選擇成熟的第三方小程序框架,這些框架已經(jīng)自動(dòng)封裝差量數(shù)據(jù)計(jì)算,對(duì)開(kāi)發(fā)者更友好。比如,“uni-app”借鑒了 “westore JSON Diff”庫(kù),在調(diào)用 setData 之前,會(huì)先比對(duì)歷史數(shù)據(jù),精確高效計(jì)算出有變化的差量數(shù)據(jù),然后再調(diào)用 setData,僅傳輸變化的數(shù)據(jù),這樣可實(shí)現(xiàn)傳遞數(shù)據(jù)量的最小化,提升通訊性能。如下,是一個(gè)示例代碼:

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

Tips:如上 change 方法執(zhí)行時(shí),僅會(huì)將 list 中的 “item5 ~ item8”4 個(gè)新增列表項(xiàng)傳輸過(guò)去,實(shí)現(xiàn)了 setData 傳輸量的極簡(jiǎn)化。

(3)組件差量更新

下圖是一個(gè)微博列表截圖:

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

假設(shè)當(dāng)前有 200 條微博,用戶對(duì)某條微博點(diǎn)贊,需實(shí)時(shí)變更其點(diǎn)贊數(shù)據(jù)(狀態(tài));在傳統(tǒng)模式下,一條微博的點(diǎn)贊狀態(tài)變更,會(huì)將整個(gè)頁(yè)面 (Page) 的數(shù)據(jù)全部通過(guò) setData 傳遞過(guò)去,這個(gè)消耗是非常高的;而即使通過(guò)之前介紹,通過(guò)差量計(jì)算的方式獲取變更數(shù)據(jù),這個(gè) Diff 遍歷范圍也很大,計(jì)算效率極低。

如何實(shí)現(xiàn)更高性能的微博點(diǎn)贊?這其實(shí)就是組件更新的典型場(chǎng)景。

合適的方式應(yīng)該是,將每條微博封裝成一個(gè)組件,用戶點(diǎn)贊后,僅在當(dāng)前組件范圍內(nèi)計(jì)算差量數(shù)據(jù)(可理解為 Diff 范圍縮小為原來(lái)的 1/200),這樣效率才是最高的。

提醒大家注意,并不是所有小程序三方框架都已實(shí)現(xiàn)自定義組件,只有在基于自定義組件模式封裝的框架中,性能才會(huì)大幅提升;如果三方框架是基于老的“template”模板封裝的組件開(kāi)發(fā),則性能并不會(huì)有明顯改善,其 Diff 對(duì)比范圍依然是 Page 頁(yè)面級(jí)的。

3. 混合渲染

大家知道,小程序當(dāng)中有一類特殊的內(nèi)置組件——原生組件,這類組件有別于 WebView 渲染的內(nèi)置組件,他們是由原生客戶端渲染的。

小程序中的原生組件,從使用方式上來(lái)說(shuō),主要分為三類:

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

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

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

接下來(lái)的問(wèn)題,為什么要引入原生渲染?以及為什么僅針對(duì)這幾個(gè)組件提供了原生增強(qiáng)?其他組件為什么沒(méi)有做原生實(shí)現(xiàn)?

這就需要我們針對(duì)每個(gè)組件單獨(dú)進(jìn)行分析思考,這里舉了幾個(gè)例子:

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

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

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

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

在 Android 平臺(tái),還有一種做法是基于 WebKit 改造,定制彈出鍵盤樣式;這種方案,在鍵盤彈出和關(guān)閉時(shí),input 控件都是 Web 實(shí)現(xiàn)的,故不存在“placeholder”閃爍的問(wèn)題。

(2)混合渲染引發(fā)的問(wèn)題

原生組件雖然帶來(lái)了更豐富的特性及更好的性能,但同時(shí)也引入了一些新的問(wèn)題,比如:

  • 層級(jí)問(wèn)題:原生永遠(yuǎn)在最高層,無(wú)法通過(guò)“z-index”設(shè)置不同元素的層級(jí),無(wú)法與 view、image 等內(nèi)置組件相互覆蓋,不支持在“picker-view”“scroll-view”“swiper”等組件中使用;

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

  • 通訊問(wèn)題:比如一個(gè)長(zhǎng)列表中內(nèi)嵌視頻組件,頁(yè)面滾動(dòng)時(shí),需通知原生的視頻組件一起滾動(dòng),通訊阻塞,可能導(dǎo)致組件抖動(dòng)或拖影;
  • 字體問(wèn)題:在 Android 手機(jī)上,調(diào)整系統(tǒng)主題字體,所有原生渲染的控件的字體都會(huì)變化,而 Web 渲染的字體則不會(huì)變化。如下圖,系統(tǒng) rom 字體為一款“你的名字”的三方字體,設(shè)置后,小程序頂部標(biāo)題字體變了,底部選項(xiàng)卡字體也變了,但小程序中間內(nèi)容區(qū)字體不變,這就是比較尷尬的一種情況,一個(gè)頁(yè)面,兩種字體。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

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

(3) 混合渲染改進(jìn)方案

既然混合渲染有這些問(wèn)題,對(duì)應(yīng)就會(huì)有解決方案,目前已有的方案如下。

  • 方案?:創(chuàng)造層級(jí)更高的組件

既然其它組件無(wú)法覆蓋到原生組件上,那就創(chuàng)造出一種新的組件,讓這個(gè)新組件可以覆蓋到 video 或 map 上。“cover-view/cover-image”就是基于這種需求創(chuàng)造出來(lái)的新組件;其實(shí)它們也是原生組件,只不過(guò)層級(jí)略高,可以覆蓋在 map、video、canvas、camera 等原生組件上。

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

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

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

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

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

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

這個(gè)小目標(biāo)說(shuō)起來(lái)簡(jiǎn)單,具體實(shí)現(xiàn)還是很復(fù)雜的。

4. 同層渲染

拋開(kāi)小程序當(dāng)前架構(gòu)實(shí)現(xiàn),解決混合渲染最直接的方案,應(yīng)該更換渲染引擎,全部基于原生渲染,video/map 和 image/view 均為原生控件,層級(jí)相同,層級(jí)遮蓋問(wèn)題自然消失。這正是“uni-app”在 App 端的推薦方案。

當(dāng)前 Web 渲染為主、原生渲染為輔的主流小程序現(xiàn)狀,如何實(shí)現(xiàn)同層渲染?

基于我們的分析研究,這里簡(jiǎn)單講解一下同層渲染實(shí)現(xiàn)的方案,和微信真實(shí)實(shí)現(xiàn)可能會(huì)有出入(目前僅微信一家實(shí)現(xiàn)了同層渲染)。

(1) iOS 平臺(tái)

小程序在 iOS 端使用 WKWebView 進(jìn)行渲染,WKWebView 在內(nèi)部采用的是分層渲染,一般會(huì)將多個(gè) DOM 節(jié)點(diǎn),合并到一個(gè)層上進(jìn)行渲染。因此,DOM 節(jié)點(diǎn)和層之間不存在一一對(duì)應(yīng)關(guān)系。但是,一旦將一個(gè) DOM 節(jié)點(diǎn)的 CSS 屬性設(shè)置為 “overflow: scroll” 后,WKWebView 便會(huì)為其生成一個(gè) WKChildScrollView,且 WebKit 內(nèi)核已經(jīng)處理了 WKChildScrollView 與其他 DOM 節(jié)點(diǎn)之間的層級(jí)關(guān)系,這時(shí) DOM 節(jié)點(diǎn)就和層之間有一一對(duì)應(yīng)關(guān)系了。

小程序 iOS 端的同層渲染可基于 WKChildScrollView 實(shí)現(xiàn),主要流程如下:

  • 創(chuàng)建一個(gè) DOM 節(jié)點(diǎn)并設(shè)置其 CSS 屬性為 overflow: scroll;
  • 通知原生層查找到該 DOM 節(jié)點(diǎn)對(duì)應(yīng)的原生 WKChildScrollView 組件;
  • 將原生組件掛載到該 WKChildScrollView 節(jié)點(diǎn)上作為其子 View。

(2)Android 平臺(tái)

小程序在 Android 端采用 Chromium 作為 WebView 渲染層,和 iOS 的 WKWebView 不同,是統(tǒng)一渲染的,不會(huì)分層渲染。但 Chromium 支持 WebPlugin 機(jī)制,WebPlugin 是瀏覽器內(nèi)核的一個(gè)插件機(jī)制,可用來(lái)解析“< embed >”。Android 端的同層渲染可基于 “< embed >”加 Chromium 內(nèi)核擴(kuò)展來(lái)實(shí)現(xiàn),大致流程如下:

  • 原生層創(chuàng)建一個(gè)原生組件(如 video);
  • WebView 創(chuàng)建一個(gè) “< embed >”節(jié)點(diǎn)并指定其類型為 video;
  • Chromium 內(nèi)核創(chuàng)建一個(gè) WebPlugin 實(shí)例 * 并生成一個(gè) RenderLayer;
  • 原生層將原生組件的畫面繪制到 RenderLayer 所綁定的 SurfaceTexture 上;
  • Chromium 渲染該 RenderLayer。

這個(gè)流程相當(dāng)于給 WebView 添加了一個(gè)外置插件,且“< embed >”節(jié)點(diǎn)是真正的 DOM 節(jié)點(diǎn),可將更多的樣式作用于該節(jié)點(diǎn)上。

四、未來(lái)可能

如果要探討小程序接下來(lái)的技術(shù)升級(jí)方向,我們認(rèn)為應(yīng)該在用戶體驗(yàn)、開(kāi)發(fā)效率兩個(gè)方向上努力。

1. 更優(yōu)秀的用戶體驗(yàn)

先說(shuō)用戶體驗(yàn)的問(wèn)題,主要也是兩個(gè)方面:

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

如果你也想快速搭建的自己的小程序引擎,并更優(yōu)的解決如上體驗(yàn)問(wèn)題,該怎么辦?

uni-app 發(fā)行到 App 端,實(shí)際上就是一個(gè)完整的小程序引擎,DCloud 會(huì)在近期將這個(gè)引擎完整開(kāi)源,歡迎大家基于 uni-app 小程序 SDK 快速打造自己的小程序平臺(tái)。

uni-app 小程序 SDK 具備如下幾個(gè)特征:

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

跨平臺(tái)開(kāi)發(fā)領(lǐng)域老兵:我眼中小程序的當(dāng)下和未來(lái)可能(跨平臺(tái) 小程序)

2. 開(kāi)發(fā)效率

開(kāi)發(fā)效率應(yīng)該從跨端、跨云兩個(gè)維度進(jìn)行分析。

(1)跨端開(kāi)發(fā)

目前的小程序都帶有明顯的廠家屬性,每個(gè)廠家各不相同。比如,阿里內(nèi)部有多套小程序(支付寶、淘寶、釘釘?shù)龋?,幸好阿里?nèi)部目前已基本統(tǒng)一。但騰訊體系下,微信和 QQ 小程序依然是兩隊(duì)人馬,兩套規(guī)范。

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

接下來(lái),會(huì)不會(huì)還有其它廠家推出自己的小程序?會(huì)不會(huì)有新的端的出現(xiàn)?比如,面向電視的小程序、面向車載的小程序?

一切皆有可能。

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

面對(duì)眾多小程序平臺(tái)及各自巨大的入口流量,開(kāi)發(fā)者如何應(yīng)對(duì)?

等待 W3C 的小程序標(biāo)準(zhǔn)統(tǒng)一,短期不太現(xiàn)實(shí)。當(dāng)下,若想將業(yè)務(wù)快速觸達(dá)多家小程序,借助跨端框架應(yīng)該是唯一可行的方案。

(2)跨云開(kāi)發(fā)

開(kāi)發(fā)商借助“uni-app”或其它跨端框架,雖然已可以開(kāi)發(fā)所有前端應(yīng)用。但仍然需要雇傭 PHP 或 Java 等后臺(tái)開(kāi)發(fā)人員,既有后端人員成本,又有前 / 后端溝通成本。

騰訊、阿里、百度小程序雖陸續(xù)上線了云開(kāi)發(fā),但它們均只支持自己的小程序,無(wú)法跨端,分散的服務(wù)器對(duì)開(kāi)發(fā)商更不可取。

故我們認(rèn)為跨廠商的 Serverless 是接下來(lái)的一個(gè)重點(diǎn)需求,開(kāi)發(fā)者在一個(gè)云端存儲(chǔ)所有業(yè)務(wù)數(shù)據(jù)及后端邏輯,然后將前端小程序發(fā)行到各家小程序平臺(tái),也就是“一云多端”模式。

五、小結(jié)

基于小程序的現(xiàn)狀,我們也許可以總結(jié)一下小程序技術(shù)上的可能方向:

  1. 其它小程序拉齊與微信的差距,讓開(kāi)發(fā)者可以做出足夠高性能的應(yīng)用服務(wù);
  2. 所有小程序應(yīng)拉齊和 App 的體驗(yàn)差距,雖然功能 API 方面仍有不足,但操作性能和交互體驗(yàn),不應(yīng)該弱于 App;
  3. 跨端框架 Serverless,讓開(kāi)發(fā)者更輕松,讓企業(yè)更高效。

作者介紹:
崔紅保,DCloud CTO,Uni-App 團(tuán)隊(duì)負(fù)責(zé)人,開(kāi)發(fā)了 2 個(gè) Github Star 上萬(wàn)的流行項(xiàng)目。有 10 年以上研發(fā)管理經(jīng)驗(yàn),在跨平臺(tái)引擎、前端 UI、小程序性能優(yōu)化等方面有豐富的實(shí)踐經(jīng)驗(yàn)。

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn),該文觀點(diǎn)僅代表作者本人。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請(qǐng)發(fā)送郵件至 舉報(bào),一經(jīng)查實(shí),本站將立刻刪除。