用戶
 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

掃一掃,登錄網站

小程序社區 首頁 教程 實戰教程 查看內容

謎之wxs,uni-app如何用它大幅提升性能

Rolan 2020-8-4 11:42

uni-app支持將WXS、SJS、Filter編譯到這3家小程序平臺,同時還在App和H5實現了WXS的解析。為什么做這些事?也是為了性能。

小程序技術領域,有幾個謎一樣的存在:微信的WXS、支付寶的SJS、百度的Filter。

很多開發者都不明白為什么要造這種語言腳本的輪子出來,甚至很多開發者根本不知道它們的存在。

其實幾大小程序平臺創造它們,都是為了解決性能問題,但不得不吐槽下,設計的實在是很難用,文檔也語焉不詳。

uni-app支持將WXS、SJS、Filter編譯到這3家小程序平臺,同時還在App和H5實現了WXS的解析。為什么做這些事?也是為了性能。

比如uni-ui組件庫中的swiperaction組件,就是列表項向左滑動時拉出幾個擠壓式聯動的菜單按鈕,這種流暢的跟手動畫,正是借助于WXS機制實現的。

微信為何要創造WXS

WXS(WeiXin Script)是微信創造的一套腳本語言,它的官方說法是:“WXS 與 JavaScript 是不同的語言,有自己的語法,并不和 JavaScript 一致”。

那微信為何要脫離 JavaScript ,單獨創造一套語言呢?這要從微信小程序的底層邏輯(運行環境)講起。

小程序的運行環境分為邏輯層和視圖層,分別由2個線程管理,其中:

  • WXML 模板和 WXSS 樣式工作在視圖層,界面使用 WebView 進行渲染
  • JavaScript代碼工作在邏輯層,運行在JsCore或v8里

小程序在視圖層與邏輯層兩個線程間提供了數據傳輸和事件系統。這樣的分離設計,帶來了顯而易見的好處: - 邏輯和視圖分離,即使業務邏輯計算非常繁忙,也不會阻塞渲染和用戶在視圖層上的交互

但同時也帶來了明顯的壞處: - 視圖層(webview)中不能運行JS,而邏輯層JS又無法直接修改頁面DOM,數據更新及事件系統只能靠線程間通訊,但跨線程通信的成本極高,特別是需要頻繁通信的場景

什么是需要頻繁通訊的場景?最典型的例子就是用戶持續交互的情況,比如觸摸、滾動等。我們以側滑菜單為例,假設在頁面上滑動A元素,要求B元素跟隨移動,一次滑動操作(touchmove)的響應過程如下:

touchmove 事件從視圖層(Webview)傳遞到邏輯層,中間會由微信客戶端(Native)做中轉

邏輯層處理 touchmove 事件,計算需移動的位置,然后再通過 setData 傳遞到視圖層,中間同樣會由微信客戶端(Native)做中轉

一次 touchmove 的響應需要經過 視圖層、Native、邏輯層三者之間2個完整來回的通信,通信的耗時開銷較大,用戶的交互就會出現延時卡頓的情況。

除了滾動、拖動交互外,在for循環里對數據做格式修改,也會造成邏輯層和視圖層頻繁通訊。

其實這類通信損耗問題,在業內由來已久,react native和weex都有類似問題,weex提供了bindingx來解決。

但對于小程序來講,這類問題解決起來更容易。因為其實視圖層的webview,是有js環境的,只不過過去不給開發者開放。

如果在視圖層的js直接處理滾動或拖動交互、直接處理數據格式,就能避免大量通信損耗。

但對于小程序平臺而言,大量開放webview里的js編寫,違反了它的初衷,比如開發者會直接操作dom,影響性能體驗。所以小程序平臺提出一種新規范,限制webview里可運行的js的能力。這就是wxs、sjs、filter的由來。

從本質來講,wxs、sjs、filter是一種被限制過的、運行在視圖層webview里的js。它并不是真的發明了一種新語言。

WXS特征及適用場景

WXS具備如下特征: - WXS是可以在視圖層(webview)中運行的JS - WXS無法直接修改業務數據,僅能設置當前組件的class和style,或者對數據進行格式化。要修改邏輯層的數據,需要通過 callMethod,傳遞參數給邏輯層 - WXS是被限制過的JavaScript,可以進行一些簡單的邏輯運算 - WXS可以監聽touch事件,處理滾動、拖動交互

故可以得出WXS的適用場景,主要包括: - 用戶交互頻繁、僅需改動組件樣式(比如布局位置),無需改動數據內容的場景,比如側滑菜單、索引列表、滾動漸變等 - 數據格式處理,比如文本、日期格式化,或者國際化。通過WXS可以模擬實現Vue框架的過濾器,如下是一個通過wxs實現首字母大寫的示例:

<wxs module="m1">
 //首字母大寫
 var capitalize = function(value) {
 if (!value) return ''
 value = value.toString()
 return value.charAt(0).toUpperCase() + value.slice(1)
 }
 module.exports = {
 capitalize: capitalize
 }
 </wxs>
 <view class="content">
 <view class="text-area">
 <!-- title 為當前頁面 data 中定義的初始數據 -->
 <text class="title">{{m1.capitalize(title)}}</text>
 </view>
 </view>

uni-app如何支持WXS

uni-app遵循Vue單文件組件(SFC)規范,組件/樣式/腳本是寫在一個.vue文件中的,但微信小程序是多文件分離(wxml/wxss/js/json)的,所以在微信端的主要工作是擴展vue-template-compiler,解析template/style/script節點,并正確生成到對應的wxml/wxss/js文件中,具體編譯工作如下圖:

Tips-1:關于<wxs>標簽重構為<script lang="wxs">的說明:

因.vue文件中的<wxs>標簽及內嵌WXS代碼,在主流前端開發工具(vscode/HBuilderX等)中,均無法實現語法提示、代碼高亮及格式化,故uni-app將<wxs module="m1">重構為<script module="m1" lang="wxs">,便捷實現了語法提示、代碼高亮等,如下為vscode/HBuilderX中對于<wxs>標簽重構前后的代碼高亮對比,明顯重構為<script lang="wxs">后,開發體驗更佳:

Tips-2:鑒于Vue的自定義標簽規范,我們建議將<wxs>(<script lang="wxs">)和template平級編寫

編譯器的具體解析擴展工作,這里不詳述,僅給出wxs生成的示例代碼,讓大家有個直觀理解:

createFilterTag (filterTag, {
 content,
 attrs
 }) {
 content = content.trim()
 if (content) { //<wxs>標簽內直接編寫 wxs 代碼
 return `<${filterTag} module="${attrs.module}">
 ${content}
 </${filterTag}>`
 } else if (attrs.src) { //外聯 .wxs 文件
 return `<${filterTag} src="${attrs.src}" module="${attrs.module}"></${filterTag}>`
 }
 }

在保證編譯正確的情況下,微信小程序運行時會正確解析并執行WXS腳本,框架runtime無需干預。

基于 WXS 提升性能體驗的實現示例

下面的gif顯示的內容,是借助 WXS 實現的一個swipeaction組件示例,列表項向左滑動時拉出幾個擠壓式聯動的菜單按鈕,跟手動畫、回彈動畫都很自然流暢。

這里簡單給出主要實現思路:

在 vue 中引用 wxs 文件,并綁定 touch 事件

<template>
 <view class="uni-swipe_content">
 <!-- 可滑動的菜單項容器,綁定touch事件 -->
 <view :data-position="pos" class="move-hock"
 @touchstart="swipe.touchstart" @touchmove="swipe.touchmove" @touchend="swipe.touchend" @change="change">
 <view class="uni-swipe_box">
 <slot />
 </view>
 <view class="uni-swipe_button-group move-hock">
 <!-- 滑動后,右側擠壓式的聯動菜單按鈕-->
 <view v-for="(item,index) in options" :data-button="btn" :key="index" class="button-hock">
 {{ item.text }}
 </view>
 </view>
 </view>
 </view>
 </template>
 <script module="swipe" lang="wxs" src="./index.wxs"></script>

在 wxs 文件中,處理 touch 事件邏輯,通過 translateX 移動元素位置

function touchstart(e, ins) {
 //記錄開始位置及動畫狀態
 var pageX = e.touches[0].pageX;
 ....
 }
function touchmove(e, ownerInstance) {
 var instance = e.instance;
 var pageX = e.touches[0].pageX;//獲取當前移動位置
 //計算偏移位置
 var x = Math.max(-instance.getState().position[1].width, Math.min((value), 0));
 //設置左側元素移動位置
 instance.setStyle({transform: 'translateX(' + x + 'px)'}) 
//循環右側擠壓式聯動菜單
 var btnIns = ownerInstance.selectAllComponents('.button-hock');
 for (var i = 0; i < btnIns.length; i++) {
 ...
 //設置每個聯動菜單的移動位置
 btnIns[i].setStyle({transform: 'translateX(' + (arr[i - 1] + value * (arr[i - 1] / position[1].width)) + 'px)'})
 ...
 }
 }
function touchend(e, ownerInstance) {
 var instance = e.instance;
 var state = instance.getState()
 //根據當前移動位置,實現菜單項的自動展開或回彈
 move(state.left, -40, instance, ownerInstance)
 }

該示例的完整源碼參考github

在這段代碼中,響應手勢并移動菜單,是在視圖層直接完成的。而不用wxs的傳統寫法,實現這個功能就會很卡。首先是視圖層接收到touch事件,然后傳遞給邏輯層,邏輯層的js響應touch事件,判斷移動距離,再通知視圖層更新界面元素的位置。在持續的拖動過程中,視圖層和邏輯層不停交互通信,無法做到跟手的順滑。

雖然我們了解了wxs的原理,但老實講,wxs挺難用的,直到現在,大多數開發者仍然不會用它。比較合適的做法,還是一些框架的作者對它進行封裝。uni-app提供的uni-ui組件庫,就是這樣做的,開發者只需要按標準vue組件的方式去引用uni ui的swiperaction組件,就能得到流暢的滑動跟手菜單。

更多平臺的兼容性

uni-app的App端也是一個小程序引擎,為了在App端實現流暢的跟手拖動,也實現和兼容了wxs。

其實H5平臺倒不存在邏輯層和視圖層通訊折損的問題,但為了平臺兼容性拉齊,uni-app在H5端也實現了wxs機制。

這樣編寫wxs代碼,在uni-app中可同時運行在App端、H5端、微信小程序端。

百度小程序的Filter過濾器和支付寶小程序的SJS,成熟度還比較低,目前只能處理基本的數據格式過濾,還不能響應touch等交互事件。

至于頭條和QQ小程序,還不支持類WXS機制。

期待其他小程序平臺盡快補齊這個重要功能,實現體驗的提升。

uni-app目前也支持單獨編寫百度小程序的Filter過濾器和支付寶小程序的SJS,這兩種腳本無法跨多端,僅支持自有平臺。開發者若需使用,可分別編寫wxs/filter/sjs腳本,然后依次通過script引用,uni-app編譯器會根據目標平臺,分別編譯發行,如下為示例代碼: 示例代碼要有條件編譯

<!-- App/H5/微信小程序平臺調用wxs腳本 -->
 <script module="utils" lang="wxs" src="./utils.wxs"></script>
 <!-- 百度小程序平臺調用filter.js腳本 -->
 <script module="utils" lang="filter" src="./utils.filter.js"></script>
 <!-- 支付寶小程序平臺調用sjs腳本 -->
 <script module="utils" lang="sjs" src="./utils.sjs"></script>

后續

用運行在視圖層的js解決通訊阻塞,可能很多人都沒意識到。希望本文能給大家解惑,解開WXS之謎。

其實小程序的性能體驗優化,仍然有大量空間

鮮花
鮮花
雞蛋
雞蛋
分享至 : QQ空間
收藏
原作者: 崔紅保 來自: segmentfault
梦幻单人赚钱方法 山西快乐十分前三组遗漏数据 喜乐彩中奖规则 广东11选5开奖结果 赚钱一天200元游戏 体彩七星彩500期走势图 极速十一选五助手 大乐透计算公式及秘诀 内蒙体育彩票11选5 安徽快338期开奖号码 排列三试机号和三句话