首頁 >頭條 >五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

青灯夜游
青灯夜游轉載
2022-08-18 11:27:454876瀏覽

任何可以用 JavaScript 來寫的應用,最終都會用 JavaScript 來寫。那麼同樣的,任何可以用小程式來實現的產品,最終都會用小程式來實現。那麼如何開發小程式?以下這篇文章為大家總結分享五年電商前端團隊小程式開發經驗(萬字原理 優化乾貨),希望對大家有幫助!

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

一、探討雙執行緒架構

對微信小程式底層設計的疑問

  • 雙執行緒模型到底是什麼?
  • 如何將視圖和邏輯分開?
  • 為什麼要用 WXML、WXSS 這種奇怪的語法?
  • 如何屏蔽掉危險操作 API?
  • 如何進行通信,資料驅動視圖更新如何實現?
  • 雙執行緒模型有何缺點?

1、模型組成

1)、傳統瀏覽器進程、執行緒模型存在的問題

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

#效能問題

渲染執行緒與JS 引擎執行緒是互斥的,存在阻塞問題,長時間的JS 腳本執行直接會影響到UI 視圖的渲染,導致頁面卡頓

安全性問題

#危險的HTML 標籤(a、script)和JS API(Function、eval、DOM API) ,攻擊者很容易對頁面內容進行篡改,非法獲取到用戶資訊等

解決方案

小程式採用了雙線程模型,將視圖展示和邏輯處理分開在兩個執行緒中執行

模型中對HTML 標籤進行了封裝,開發者無法直接呼叫原生標籤,JS 中原有的危險邏輯操作API 能力也被屏蔽,涉及到的通訊動作只能透過中間層提供的API 去觸發虛擬DOM 更新(資料驅動視圖)

2)、小程式雙執行緒模型

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

其中視圖層及邏輯層的能力都是透過微信用戶端內建的小程式基礎庫JS 檔案來提供的

WAWebview.js 提供視圖層基礎的API 能力,包含一整套元件系統及Virtual Dom(虛擬DOM)的相關實作

WAService.js 提供邏輯層基礎的API 能力,向開發者揭露各類別操作的API

3)、客戶端引擎底層實作

不同運行環境下,腳本執行環境以及用於元件渲染的環境是不同的,效能表現也存在差異:

  • 在iOS、iPadOS 和Mac OS 上,小程式邏輯層的JavaScript 程式碼運行在JavaScriptCore 中,視圖層是由WKWebView 來渲染的,環境有iOS 14 、iPad OS 14、Mac OS 11.4 等

  • 在Android 上,小程式邏輯層的JavaScript 程式碼運作在 V8 中,視圖層是由基於Mobile Chromium核心的微信自研XWeb 引擎來渲染的(從過去的X5 換成了自研的XWeb 引擎,視圖層是基於Mobile Chrome 內核來渲染的,而且根據官方披露,將來邏輯層也會脫離V8,使用定制化的XWeb Worker 線程,即自訂一個Web Worker 線程,作為小程式的邏輯層。 參考連結

  • 在Windows 上,小程式邏輯層JavaScript 和檢視層都是用Chromium 核心

  • 在開發工具上,小程式邏輯層的JavaScript 程式碼是執行在 NW.js 中,視圖層是由Chromium WebView 來渲染的

2.視圖層

1)、小程式元件系統

#小程式中會涉及的元件共有三種類型:內建元件、自訂元件、原生元件

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

2)、小程式元件組織框架

1、Web Component

Web Component   是瀏覽器中建立封裝功能和自訂元素的能力,由三個主要技術組成

  • Custom elements(自訂元素)一組JavaScript API,允許定義custom elements 及其行為,然後可以在使用者介面中按照需要使用它們

  • Shadow DOM(影子DOM)一組JavaScript API,用於將封裝的「影子」 DOM 樹附加到元素(與主文檔DOM 分開呈現)並控制其關聯的功能。透過這種方式保持元素的功能私有,這樣它們就可以被腳本化和樣式化,而不用擔心與文件的其他部分發生衝突

  • ##HTML templates(HTML模板)

template 和slot 元素可以編寫不在呈現頁面中顯示的標記模板。然後它們可以作為自訂元素結構的基礎被多次重複使用

2、Exparser 框架
Exparser 是微信小程式的元件組織框架,內建在小程式基礎庫中,為小程式的各種組件提供基礎的支持 小程式內的所有元件,包括內建元件和自訂元件,都由Exparser 組織管理

小程式中,所有節點樹相關的操作都依賴Exparser,包括WXML 到頁面最終節點樹的構建、 createSelectorQuery 呼叫和自訂元件特性等

Exparser 會維護整個頁面的節點樹相關信息,包括節點的屬性、事件綁定等,相當於一個簡化版的Shadow DOM 實作

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

3、Exparser 特點
  • 基於Shadow DOM 模型:模型上與Web Component 的Shadow DOM 高度相似,但不依賴瀏覽器的原生支持,也沒有其他依賴函式庫;實作時,也針對性地增加了其他API 以支援小程式元件程式設計

  • 可在純JS 環境中運作:這表示邏輯層也具有一定的元件樹組織能力(在元件的渲染中就會用到)

  • 高效輕量:效能表現好,在元件實例極多的環境下表現尤其優異,同時程式碼尺寸也較小

4、Exparser 核心方法

#registerBehavior 註冊元件的一些基礎行為,供元件繼承

#registerElement 註冊元件,跟我們互動介面主要是屬性和事件

#元件建立

小程式基礎庫對外提供有Page 和Component 兩個建構器,小程式啟動將properties、data、methods 等定義字段,寫入Exparser 的元件註冊表,在初始化頁面時, Exparser 會建立出頁面根元件的一個實例,用到的其他元件也會相應創建組件實例(這是一個遞歸的過程)

5、Page 和Component 的創建過程

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

從上圖中我們能夠比較明顯的看到小程式在Page 渲染和Component 渲染時通訊方式上有差異。其中,Page 渲染中 VDOM 的產生和 diff 都是在視圖層完成的,邏輯層只負責對 data 資料的傳送,來觸發渲染層的更新邏輯。而在 Component 的渲染中,邏輯層和視圖層需要共同維護一套 VDOM,方式則是在元件初始化時在邏輯層建構元件的 VDOM,然後將其同步到視圖層。後續的更新操作則會先在邏輯層進行新舊 VDOM 的 diff,然後僅將 diff 之後的結果進行通信,傳遞到視圖層之後直接進行 VDOM 的更新和渲染。這樣做最大的好處就是將視圖更新通訊的粒度控製成DOM 級別,只有最終發生改變的DOM 才會被更新過去(因為有時候data 的改變並不一定會帶來視圖的更新),相對於之前data等級的更新會更精準,避免非必要的通訊成本,效能更好。

3)、小程式原生元件

1、原生元件的優點
  • 擴充Web 的能力。例如像輸入框元件(input, textarea)有更好地控制鍵盤的能力。

  • 體驗更好,同時也減輕 WebView 的渲染工作。例如像地圖元件(map)這類較複雜的元件,其渲染工作不佔用 WebView 線程,而交給更有效率的客戶端原生處理。

  • 渲染性能更好,繞過setData、資料通訊和重渲染流程,例如像畫布元件(canvas)可直接用一套豐富的繪圖接口進行繪製。

2、原生元件建立過程
  • 元件建立後插入到DOM 樹裡,渲染一個佔位元素,瀏覽器核心會立即計算佈局,此時我們可以讀取出元件相對頁面的位置(x, y 座標)、寬高

  • 元件通知客戶端,客戶端在相同的位置上,根據寬高插入一塊原生區域,之後客戶端就在這塊區域渲染介面

  • #當位置或寬高改變時,元件會通知客戶端做對應的調整

#3、原生元件渲染限制
  • 有些CSS 樣式無法套用於原生元件

  • 層級問題(原生元件層級最高,會覆寫WebView 層的元件)

#解決方式

  • 使用cover-view 和cover-image 元件覆蓋原生元件(能力有限,不靈活)

  • 同層渲染(透過一定的技術手段把原生元件直接渲染到WebView 同一層級上)

#4)、原生元件同層渲染

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

## 1、iOS 系統實作原理
  • 小程式iOS 端的同層渲染是基於WKChildScrollView 實作的,原生元件在attached 之後會直接掛載到預先建立好的WKChildScrollView 容器下

  • 原理分析
  • WKWebView 在內部採用的是分層的方式進行渲染,WKWebView 會將WebKit 核心產生的Compositing Layer(合成層)渲染成iOS 上的一個WKCompositingView 元件,但合成層與DOM 節點之間不存在一對一的映射關係(核心一般會將多個DOM 節點合併在一個合成層)。
  • WKChildScrollView

    :當把一個DOM 節點的CSS 屬性設為overflow: scroll (低版本需同時設定-webkit-overflow-scrolling: touch)之後,WKWebView 會為其產生一個WKChildScrollView(為了可以讓iOS 上的WebView 滾動有更流暢的體驗,WebView 裡的滾動實際上是由真正的原生滾動元件來承載的,​​WebKit 核心已經處理了它與其他DOM 節點之間的層級關係),且WKChildScrollView 與DOM 節點存在一對一映射的關係。
建立流程

#建立一個DOM 節點並設定其CSS 屬性為overflow: scroll 且- webkit-overflow-scrolling: touch

通知客戶端查找到該DOM 節點對應的原生WKChildScrollView 元件

將原生元件掛載到該WKChildScrollView 節點上作為其子View

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

透過上述流程,小程式的原生元件就被插入到WKChildScrollView 了

#2、Android系統實作原則
  • WebPlugin 是瀏覽器核心的一個插件機制,小程式Android 端的同層渲染是基於

    embed

    標籤和WebPlugin 實作的
  • #原則分析

  • embed 標籤是HTML5 新增的標籤,是使用來定義嵌入的內容(如外掛程式、媒體等),格式可以是PDF、Midi、Wav、MP3 等等,例如Chrome 瀏覽器上的PDF 預覽,就是基於embed 標籤來實現的。

    基於embed 標籤結合Chromium 核心擴充功能來實現,Chromium 支援WebPlugin 機制,WebPlugin 是瀏覽器核心的一個插件機制,主要用來解析和描述embed 標籤,將原生元件渲染到核心提供的Texture 紋理上
  • 建立流程

  • #WebView 側建立一個embed DOM 節點並指定元件類型

Chromium 核心會建立一個WebPlugin 實例,並產生一個RenderLayer

Android 用戶端初始化一個對應的原生元件

#Android 用戶端將原生元件的畫面繪製到步驟2 建立的RenderLayer 所綁定的SurfaceTexture 上

    通知Chromium 核心渲染該RenderLayer
  • #Chromium 渲染該embed 節點並上螢幕
  • #5)、視圖層程式碼編譯

  • 微信開發者工具內建了二進位的WXML 和WXSS 程式碼檔案編譯器,編譯器接受WXML 和WXSS 程式碼檔案列表,處理完成後輸出JavaScript 程式碼
  • 1、WXSS 程式碼檔案

    #######wcsc 指令工具執行過程###############wcsc 編譯WXSS 產生一個JS 檔案產物############JS 檔案包含新增尺寸單位rpx 轉換方法,可根據裝置螢幕寬度自適應計算成px 單位############提供setCssToHead 方法將轉換後的css 內容加入到html 模版的head 中#############eval 方法執行這個JS 檔案字串完成樣式註入###

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

2、WXML 程式碼檔案

#WCC 指令工具執行程序

  • wcc 編譯WXML 產生一個JS 檔案產物

  • JS 檔案包含$gwx 方法,接收WXML 檔案路徑,執行後產生generateFunc 方法,並觸發generateFuncReady 事件

  • generateFunc 方法接受動態資料data,類似一個render 函數,用於產生virtual dom

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

##3、邏輯層

1)、邏輯層包含內容

  • 提供JS 程式碼執行環境

  • 為window 對象新增模組介面require define

  • 提供Page,App,getApp 等介面

  • 提供wx 全域物件下面的api 方法,網路、媒體、檔案、資料快取、位置、裝置、介面、介面節點資訊還有一些特殊的開放介面

2)、JS 程式碼執行環境

JSCore的組成(iOSAndroid

1、JSVirtualMachine

它透過實例化一個VM 環境來執行JS 程式碼,如果你有多個JS 需要執行,就需要實例化多個VM。並且需要注意這幾個VM 之間是不能互相互動的,因為容易出現GC 問題

2、JSContext

JSContext 是JS 程式碼執行的上下文對象,相當於一個Webview 中的window 物件。在同一個VM 中,你可以傳遞不同的Context

3、JSValue

和WASM 類似,JsValue 主要就是為了解決JS 資料型別和Swift 或Java 數據類型之間的相互映射。也就是說任何掛載在JSContext 的內容都是JSValue 類型,Swift 和Java 在內部自動實作了和JS 之間的型別轉換

4、JSExport

#是JSCore 裡面,用來暴露Native 介面的一個protocol。簡單來說,它會直接將Native 的相關屬性和方法,直接轉換成prototype object 上的方法和屬性

3、模組規範的實作

  • 透過define 定義一個模組,限制了模組可使用的其他模組,如window,document 等,define 方法則是透過將所有的JS 程式碼邏輯都以「路徑-模組」的鍵值對的方式存在了全域變數裡實現的

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

1五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

  • #使用require 應用一個模組,使用模組時只會傳入require 和module、exports,程式碼中讀取到的其他變數就會都是undefined,這也是不能在小程式中取得一些瀏覽器環境物件的原因

1五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

這其實就是典型的模組載入思路,與Webpack 打包模組的處理方式十分相似

4、通訊原理

WAWebview.js 和WAService.js 檔案中除了提供視圖層、邏輯層基礎的API 能力之外,還提供了這兩個執行緒之間進行通訊的能力

小程式邏輯層和渲染層的通訊會由Native (微信客戶端)做中轉,邏輯層發送網路請求也經由Native 轉送

1)、底層實作

幾個端的不同實作最終會封裝成WeiXinJSBridge 這樣一個相容層供給開發者呼叫

1、iOS 端

視圖層是透過WKWebView 的window.webkit.messageHandlers.NAME.postMessage 和eval 實作。邏輯層是向JavaScripCore 框架注入一個全域的原生方法

2、Android 端

視圖層和邏輯層的實作原理一致,都是往WebView 的window物件注入一個原生方法WeixinJSCore 實現,這個WeixinJSCore 是微信提供給 JS 呼叫的介面(native實作)

3、微信開發者工具

使用websocket 進行通訊

2)、WeixinJSBridge 模組物件

WeixinJSBridge提供了視圖層JS 與Native、視圖層與邏輯層之間訊息通訊的機制,提供如下幾個方法:

  • invoke:JS 呼叫Native API

  • on:JS 監聽Native 訊息

  • publish:檢視層發佈訊息

  • #subscribe
  • :訂閱邏輯層的訊息
  • #5、雙執行緒架構的缺陷與最佳化

  • 1)、雙執行緒架構的缺陷
  • 無法靈活操作DOM,無法實現較複雜的效果
  • 部分與原生元件相關的視圖有使用限制,如微信的scrollView 內不能有textarea

#頁面大小、開啟頁數都受到限制

1五年電商前端團隊小程式開發經驗總結【乾貨滿滿】##需要單獨開發適配,不能重複使用現有程式碼資源

在JSCore 中JS 體積比較大的情況下,其初始化時間會產生影響

##傳輸資料中,序列化和反序列化耗時需要考慮

2)、雙執行緒架構的最佳化

雙執行緒帶來的效能瓶頸,也是微信自身一直致力於解決的關鍵問題。前面提到小程式將會在新的XWeb 內核裡,將邏輯層的實作替換為透過修改Chromium 內核,實現自訂的XWeb Worker 線程,這樣就不再需要額外的V8 了,使得記憶體佔用方面有顯著減少。另外,由於是基於Chromium 核心的Worker 線程,所以關於資料通訊這塊,很自然的也會擁有PostMessage 的能力,來取代原有的setData 底層通訊方式,獲得更高效能的通訊能力

3)、支付寶小程式的雙執行緒架構

另外,我還了解研究了支付寶小程式底層的相關實作。支付寶小程式也使用了類似的雙執行緒架構模型,透過使用 UC 提供的瀏覽器內核,渲染層是在 Webview 執行緒中運行,邏輯層則是另一個執行緒運行 Service Worker。但是 Service Worker 需要透過 MessageChannel API 來與 渲染線程通信,當資料量較大、物件較複雜時同樣存在效能瓶頸。 因此支付寶小程式重新設計了現有的 JS 虛擬機器 V8,提出了一個最佳化的隔離模型(Optimized isolation model, OIM)。 OIM 的主要想法是共享JS 虛擬機實例中與線程執行環境無關的資料和基礎設施,以及不可變或不易變的JS 對象,使得在保持JS 層邏輯隔離的前提下,節省多實例場景下在內存和功耗上的開銷。儘管有些實例間共享的資料會帶來同步的開銷,但是在隔離模型下,本方案所共享的資料、物件、程式碼和虛擬機器基礎設施都是不可變或不易變的,所以很少發生競爭。

在新的隔離模型下,Webview 裡面的V8 實例就是一個Local Runtime,Worker 線程裡面的V8 實例也是一個Local Runtime,在邏輯層和渲染層交互時,setData 物件的會直接創建在Shared Heap 裡面,因此渲染層的Local Runtime 可以直接讀到該對象,並且用於渲染層的渲染,減少了對象的序列化和網絡傳輸,極大的提升了啟動性能和渲染性能。 除此之外,支付寶小程式實現了首頁離線快取優化,首先渲染上次保存下面的首頁UI 頁面,把首頁展現給用戶,然後在後台繼續加載前端框架和業務的程式碼,載入完成後再和離線快取的首頁UI 進行合併,給用戶展現動態的首頁,這一點和微信小程式的初始渲染快取方案十分類似且更加激進。也使用了 WebAssembly 技術重新實作了虛擬 DOM 這塊的核心程式碼,提升了小程式的頁面渲染。 【相關影片教學推薦:

web前端

1五年電商前端團隊小程式開發經驗總結【乾貨滿滿】6、雙執行緒模型架構設計的思考

技術架構為解決問題而生,一個好的技術方案不僅需要設計者在開發效率、技術成本、性能體驗、系統安全之間做出平衡和取捨,還需要與業務走向、產品形態、用戶訴求緊密結合

#二、執行時間機制一探究竟1五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

######1、程式碼包下載#########在小程式啟動(冷啟動)時,微信會為小程式展示一個固定的啟動介面,介面內包含小程式的圖示、名稱和載入提示圖示################此時,微信會在背後完成幾項工作:## #下載小程式碼包、載入小程式碼包、初始化小程式首頁################

從開發者的角度來看,控製程式碼包大小有助於減少小程式的啟動時間。對低於1MB 的程式碼包,其下載時間可以控制在929ms(iOS)1500ms(Android)

#最佳化方案

  • 減少主套件、分包大小

  • #合理分配,分包預載規則

  • 精細拆分,非同步化分包

2、頁面層級準備

在小程式啟動前,微信會事先準備好一個頁面層級用來展示小程式的首頁。每當一個頁面層級被用於渲染頁面,微信都會提前開始準備一個新的頁面層級,使得每次呼叫 wx.navigateTo 都能夠盡快展示一個新的頁面。視圖層頁面內容都是透過pageframe.html 範本來產生

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

#頁面層級的準備步驟

1五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

  • ##第一階段是啟動一個WebView,在iOS 和Android 系統上,作業系統啟動WebView 都需要一小段時間

  • ##第二階段是在WebView 中初始化基礎函式庫,此時也會進行一些基礎函式庫內部最佳化,以提升頁面渲染效能
  • #第三階段是注入小程式WXML 結構和WXSS 樣式,讓小程式能在接收頁面初始資料之後馬上開始渲染頁面(這階段無法在小程式啟動前執行)
3、頁面程式碼注入和執行

1)、初始化全域變數

視圖層全域變數

__wxConfig:是根據全域配置和頁面配置產生的設定對象,包含page、分包page、tabbar 等資訊
  • __wxAppCode__:非JS類型的開發者程式碼
  • JSON:JSON 設定檔內容
  • WXML:$gwx 函數的執行結果,是一個可執行函數,執行後會傳回VDOM 物件
  • WXSS:eval 執行樣式函數的結果,是可執行函數,執行後會插入style 標籤
邏輯層全域變數

__wxConfig:與視圖層類似,不包含分包page、tabbar 等資訊
  • __wxAppCode__:與視圖層類似,不包含WXSS 類型資料
  • __wxRoute: 用於指向目前正在載入的頁面路徑
  • __wxRouteBegin: 用於標誌Page 的正確註冊
  • __wxAppCurrentFile__:用於指向目前正在載入的JS 檔案
  • __wxAppData: 小程式每個頁面的data 網域物件
  • Component: 自訂元件建構器
  • Behavior: 自訂元件behavior 建構器
  • definePlugin: 自訂外掛程式的建構器
  • global: 全域物件
  • WeixinWorker: 多執行緒建構器
#2)、基礎庫和業務程式碼JS檔案注入

視圖層程式碼注入

#注入並執行wxml.js 檔案(定義$ gwx 方法)
  1. 注入並執行wxss.js 檔案(eval 方法,新增app.wxss 樣式檔案)
  2. 新增__wxAppCode__ 變數
  3. #執行eval 方法,插入目前頁面及引用元件樣式檔
  4. 執行$gwx(目前頁面路徑),傳回對應的產生虛擬DOM 的方法
邏輯層程式碼注入

注入並執行JS 檔案(順序:其他、babel、Component、Page、App)
  1. 注入並執行wxml.js檔案(定義$gwx方法)
#按需注入和用時注入

通常情況下,在小程式啟動時,啟動頁面所在分包和主包(獨立分包除外)的所有JS 程式碼會全部合併注入。自基礎庫版本2.11.1 起,配置了

按需注入和用時注入

,小程式僅注入當前頁面需要的自訂元件和頁面程式碼,在頁面中必然不會用到的自訂元件不會被載入和初始化

3)、邏輯層業務代碼執行

1五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

4、資料通訊和視圖渲染

1)、頁面初始渲染

#資料通訊

在小程式啟動或一個新的頁面被開啟時,頁面的初始資料(data)和路徑等相關資訊會從邏輯層傳送給視圖層,用於視圖層的初始渲染。

Native 層會將這些資料直接傳遞給視圖層,同時向使用者展示一個新的頁面層級,視圖層在這個頁面層級上進行介面繪製。

視圖層接收相關資料後,根據頁面路徑來選擇適當的WXML 結構,WXML 結構與初始資料結合,得到頁面的第一次渲染結果

1五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

在完成視圖層程式碼注入,並收到邏輯層發送的初始資料後,結合從初始資料和視圖層得到的頁面結構和樣式信息,小程式框架會進行小程式首頁的渲染,展示小程式首屏,並觸發首頁的onReady 事件。

如果開啟了 初始渲染快取,首次渲染可以直接使用渲染層的快取數據完成,不依賴邏輯層的初始數據,降低啟動耗時(onReady 事件提前執行)。

五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

頁面初始化的時間大致由頁面初始資料通訊時間和初始渲染時間兩部分構成。 其中,資料通訊的時間指資料從邏輯層開始組織資料到視圖層完全接收完畢的時間,而資料量小於 64KB 時總時長可以控制在 30ms 內。

傳輸時間與資料量大體上呈現正相關關係,傳輸過大的資料將使此時間顯著增加。 因而減少傳輸資料量是降低資料傳輸時間的有效方式。

初始渲染

初始渲染發生在頁面剛建立時。初始渲染時,將初始資料套用在對應的 WXML 片段上產生節點樹。節點樹也就是在開發者工具 WXML 面板中看到的頁面樹結構,它包含頁面內所有元件節點的名稱、屬性值和事件回呼函數等資訊。最後根據節點樹包含的各個節點,在介面上依序建立出各個元件。初始渲染中得到的 data 和目前節點樹會保留下來用於重渲染。

2五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

在這整個流程中,時間開銷大體上與節點樹中節點的總量成正比例關係。因而 減少 WXML 中節點的數量 可以有效降低初始渲染和重渲染的時間開銷,提升渲染效能。

2)、更新資料渲染

初始渲染完畢後,視圖層可以在開發者呼叫 setData 後執行介面更新。

setData 原理

1、改變對應的this.data 的值(同步運算)

解析屬性名稱(包含. 和[] 等資料路徑符號),傳回對應的層級結構,修改對應的局部data 的值

{abc: 1} 中 abc 属性名 => [abc]
{a.b.c: 1} 中 'a.b.c' 属性 => [a,b,c]
{"array[0].text": 1} => [array, 0, text]

2、將資料從邏輯層傳送到視圖層(非同步操作)

##evaluateJavascript :使用者傳輸的數據,需要將其轉換為字串形式傳遞,同時把轉換後的數據內容拼接成一份JS 腳本,再透過執行JS 腳本的形式傳遞到兩邊獨立環境

2五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

重渲染時,邏輯層將setData 資料合併到data 中,渲染層將data 和setData 資料套用在WXML 片段上,得到一個新節點樹。然後將新節點樹與目前節點樹進行比較,這樣可以得到哪些節點的哪些屬性需要更新、哪些節點需要新增或移除。最後,用新節點樹取代舊節點樹,用於下一次重渲染。

在進行目前節點樹與新節點樹的比較時,會著重比較 setData 資料所影響到的節點屬性。因而,去除不必要設定的資料、減少 setData 的資料量也有助於提升這一步驟的效能。

3)、使用者事件通訊

檢視層會接受使用者事件,如

點擊事件、觸控事件 等。

使用者事件的通訊比較簡單,當一個使用者事件被觸發且有相關的事件監聽器需要被觸發時,視圖層會將資訊回饋給邏輯層。

因為這個通訊過程是異步的,會產生一定的延遲,延遲時間同樣與傳輸的資料量正相關,資料量小於 64KB 時在 30ms 內。

2五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

降低延遲時間的方法主要有兩個

  • #去掉不必要的事件綁定(WXML 中的bind 和catch ),從而減少通訊的資料量和次數

  • 事件綁定時需要傳輸target 和currentTarget 的dataset,因而不要在節點的data前綴屬性中放置過大的資料

三、小程序基建和优化方案实践

1、小程序性能优化

1)、代码分包

1、合理的分包和分包预加载策略

主包内容

Tab页(系统要求)、业务必要页面(错误兜底页、登陆授权页等),其余文件都以业务模块或者页面的维度,拆分成各自的分包

分包预加载

据用户的实际使用场景,预测下一跳的页面,将可能性最高的场景设置为预加载分包(可以参照业务埋点数据),例如:进入电商首页后,需要对会场和商详页的分包进行预加载

2、组件分包分发

实现思路

小程序不支持主包引用分包代码,只能在分包中引用主包代码,所以把公共使用的组件代码放在主包目录中,但这些公共组件未必在主包所属的页面中会被引用,可能只是在分包页面中被多次引用,这样使得主包中代码体积越来越大,用户首次加载速度变慢。

将主包页面不依赖的公共组件分别分发到依赖它们的分包目录中,虽然分包各自的体积会有所增大,但主包体积会有显著下降

实现原理

将所有需要分发的组件放置主包指定目录中,并添加配置文件,说明组建文件分发信息。在开发时用 gulp 任务监听单个文件变化、在构建时递归遍历所有组件,将其复制到配置文件中指定的子包路径目录中。

目标文件在复制之前,都先要将文件内的依赖路径进行更新,使其在子包中运行时也能引用成功。针对不同类型的文件,采取不同的依赖分析手段。

  • JS 文件:使用 babel.transformFile 修改依赖引用地址

  • WXSS 文件:使用 postcss.plugin('transform-wxss') 处理依赖的 @import 导入样式文件地址

  • WXML 文件:使用 require('htmlparser2').Parser 来转换内部 wxs、template(import 和 include 导入)依赖的引用地址

异步分包

小程序基础库版本 2.17.3 及以上开始支持分包异步化,即可以在分包之间互相引用,这个能力可以快速取代我们自己的组件分发方案,而且优化效果更佳,可以将分包中公共依赖的代码部分打成新的分包,在使用时异步按需引入,能力与 Web 端的异步组件类似,但这个方案在生产环境的稳定性有待验证。

2)、setData 优化

实现思路

合并 setData 调用,将其放在下个时间片(事件循环)统一传输,降低通信频率

实现原理

需要先将逻辑层 this.data 进行更新,避免前后数据不一致造成逻辑出错。将需要传送至视图层的 data 进行整合,在 nextTick 中调用原生的 setData 统一进行传送,可以有效降低通信频率,并且在传送前手动做一次与 this.data 的 diff 操作,降低通信体积

1. 降低频率

const nextTick = wx.nextTick ? wx.nextTick : setTimeout; // 小程序里的时间片 API

2. 减少体积

参考京东 Taro 中 diff 的实现,对基本、数组、对象等不同类型进行处理,最终转换为 arr[1]、x.y 这样的属性路径语法,减少传输信息量

3)、Storage API 重封装

实现思路

onLaunch、onLoad 等生命周期函数中存在大量对微信 Storage 的同步调用(使用 Sync 结尾的API),这些操作涉及JS与原生通信,同步等待耗时过久,推迟页面 onReady 触发即用户可交互时间,影响用户体验。直接改为异步操作又存在业务代码改动量较大的问题,存在一定风险,大量的异步回调代码语义不优雅、可读性较差。因此需要对原生 Storage 操作进行重封装,改为对内存中对象的实时存取,提高响应速度,并定期调用原生 API 向真实 Storage 中同步。

实现原理

封裝呼叫方式一致的API,以getStorage 和getStorageSync 為基礎API,首次調用時觸發原生API 獲取原始數據,獲取到之後存儲至內存對象,後面的各種操作(刪改查)便都基於這個對象做操作。其中的 set、remove 操作需要對對應的資料做髒標誌並儲存到一張髒表裡,以便在後面將變動同步到原生端。呼叫方需要定時呼叫變動同步方法來持久化資料(遍歷同步髒表中的資料),以防記憶體運行時資料意外遺失,一般需要定期執行(app 的onShow 執行setInterval)和在app 的onHide 生命週期執行。

我們不僅對Storage API 進行了重封裝,對於其他耗時較長的同步API(例如用來取得裝置和系統資訊的getSystemInfo/getSystemInfoSync API,底層都是同步實作),我們也採用了類似的方式,僅在第一次獲取時調用系統API 並將結果緩存在內存中,之後的調用則直接返回緩存資訊。

4)、Data Prefetch

1、發佈訂閱方式

實作想法

從A頁面跳到B 頁面之前,在A 頁面emit 訊息,觸發B 頁面數據接口請求,並緩存結果數據,當B 頁面打開時,先取緩存,取不到再重新調用接口

2五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

實作原則

B 頁面雖然沒有實例化,但是頁面已經被註冊,Page 外層程式碼已被執行,因此可以事先與A 頁面完成訊息的發佈訂閱

2、全域註冊變數方式

實現想法

當需要預先載入的頁面沒有在主套件或是非同步註冊時,無法透過發布訂閱的模式進行預先請求,我們可以透過全域註冊預載方法的方式來實現

#實作原理

在前一頁路由跳轉時發起預請求,將此請求儲存到一個全域的Promise 變數裡,在需要預先載入的頁面首次渲染時優先去取全域的Promise 變數then 回呼裡的資料結果

#5) 、記憶體最佳化

1、圖片優化

對整體的圖片資源做最佳化(阿里雲函數)

  • 尺寸 :實際寬高的1-2 倍寬度
  • Webp 格式:Android 端全量使用
  • Png 格式:透明底強訴求場景才使用,其餘預設使用Jpg/Jpeg
  • 圖片懶載入:開啟Image 元件的lazy-load 屬性
2、低階機降級

實作想法

根據使用者機型硬體效能層級,前端展示設定不同的呈現策略

實作原理

#低階機場景下對以下功能進行降級

  • gif 動圖進行降級成靜態圖片
  • JS、CSS 動畫降級成靜態樣式
  • 不展示swiper 元件
  • 品質使用60% 的quality
#3、長列表最佳化
  • RecycleView
  • IntersectionObserver

#2、小程式工程化

1)、外掛程式框架

框架工具

BeautyWe 是一個三方小程式業務開發框架,透過生命底層公共業務邏輯的封裝

應用場景

公共業務邏輯,例如每個頁面對使用者登入狀態的校驗、取得和處理url 參數、PV 自動埋點、效能監控等

實作原理

生命週期函數插件化,將原生的onLoad、onShow、onReady 等函數使用Object.defineProperty 的方式進行重寫,使其內部形成一個Promise 任務鏈,透過引入插件,向任務鏈中前後位置自由插入方法,在系統生命週期函數執行時觸發並依序呼叫。

2五年電商前端團隊小程式開發經驗總結【乾貨滿滿】

實現生命週期鉤子的插件化之後,我們便可以將各類別需要處理的底層公共邏輯進行封裝,將其插入到宿主(原生生命週期)的Promise 任務鏈中

2)、自動化部署

基於小程式CI Gitlab CI Puppeteer 實作了一套半自動的小程式建置部署工具,詳見我的另一篇文章 《微信小程式自動化部署方案》,後面微信小程式提供了可以在Linux 環境單獨運作的miniprogram-ci,更簡單易用。

3)、埋點監控

監控能力

  • 業務埋點(頁面PV、模組曝光等業務埋點)

  • 效能回報(小程式啟動、頁面渲染、FMP、記憶體警告)

  • 異常監控(JS錯誤、介面錯誤、業務錯誤)

效能監控回報類型

  • memory_warning:記憶體警告資料上報。收集方法:wx.onMemoryWarning 回呼中收集警告等級

  • #app_launch:app 執行階段資料上報。收集方法:記錄 app 生命週期執行時間(onLaunch、onShow),在 page 的 onLoad 時上報

  • page_render:page 執行時間資料上報。收集方法:記錄page 生命週期執行時間(onLoad、onShow、onReady)、FMP 等,在page 的onHide 或onUnload 時上報

################################################## ####基於外掛程式框架的方案,對生命週期鉤子函數做重寫,即可在頁面程式執行過程中自動記錄和回報效能資料#########3、效能最佳化落地和日常迭代規格#########我們使用了FMP 作為衡量秒開率的關鍵指標,詳細內容參考我的另一篇文章###以使用者體驗為中心的前端效能最佳化###。另外,在日常開發中,我們也沉澱出一些規範,可以提升日常業務迭代中對效能、穩定性的基礎保障,內容詳見###微信小程式開發軍規###。 #########四、總結思考與發展前景############基建能力不斷完善#########官方文件中對框架底層技術的不斷揭露,說明小程式技術建設日益成熟完善。基建生態的不斷豐富,同層渲染、網路環境監測、初始渲染快取、啟動效能優化,官方陸續提供了這些能力的支持,讓小程式與 Web 生態逐步靠攏。 ######我們自己在業務迭代中,為了解決各種問題,自己造了很多輪子,例如:分包異步化、CI 打包、性能 Performance API,這些能力後面微信都原生給予實現。看起來似乎做了無用功,但事實上恰恰說明我們過去所做的事情是正確的,方向也是和整個生態的發展是相吻合的。 #########更多場景賦能業務#########除了技術能力的完善之外,微信小程式生態還在不斷豐富更多業務場景下的能力支撐,例如支援小程式分享朋友圈、支援產生短連結、微信聊天素材開啟小程序,為我們業務提供更多可能性與想像。 ######微信還推出了小程序硬體框架,能讓硬體設備(非通用型計算設備)在缺乏條件運行微信客戶端的情況下運行微信小程序,在各行各業的安卓系統平板電腦、大螢幕設備等硬件,提供低成本螢幕互動解決方案,為物聯網設備使用者提供更標準化、功能豐富的使用體驗。 ######這不禁讓我想起那句流傳了數十年的電腦技術領域 ###Atwood 定律###,任何可以用 JavaScript 來寫的應用,最終都將用 JavaScript 來寫。那麼同樣的,任何可以用小程式來實現的產品,最終都會用小程式來實現。 ###

Prospects for the development of mini programs

Since the birth of WeChat mini programs in 2017, the super App mini program/light application model has been tried and tested in various businesses, such as WeChat and WeChat mini programs, Alipay and Alipay mini programs, Douyin and Douyin mini programs, etc. Mini programs, a product model with low cost, fast iteration and easy promotion, have been used in various fields with the support of huge traffic brought by super apps. All have achieved great success and have gradually become a trend, with more and more companies adding this model to their products. The mini program itself has been unknowingly integrated into every aspect of life. During the epidemic, health code applications in various regions, mini program codes for e-commerce group purchases in the community, and restaurant ordering, if you want to drink milk tea and coffee, you can download them in advance on the mini program. In fact, people's lives are inseparable from small programs. The product technology solution of small programs has indeed created great social value.

When I was chatting with a friend, I jokingly said that mini programs are PWA applications with Chinese characteristics. Facing the future, mini programs still have great potential. In addition to the existing online shopping and life service applications that account for a relatively high proportion, they are also used in more The usefulness of mini program technology can also be seen in various scenarios. For example, in the fields of health care, offline retail, entertainment games, AI intelligence and other industries, it is far from reaching saturation. The market gap has made the development potential of mini program become more and more important. To get bigger, Mini Programs are moving towards the original goal of making Mini Programs within reach and everywhere.

Original address: https://juejin.cn/post/7100752247381819399

(Learning video sharing: Getting started with web front-end)

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除