作者介紹:何延希,美團點評工程師,4年Web開發經驗,現在是美團點評點餐團隊的一員。
接上一篇大眾點評點餐小程式開發經驗 - 概述,本期想要和大家分享下大眾點評點餐小程式中View檢視層的一些開發經驗。本文部分示例來自於「大眾點評點餐」小程式的選單頁面。
頁面程式碼結構為:
menu
├── menu.html
├── menu.js
├── menu.json
└── menu.less
我們將要說的小程式的View檢視層是由WXML(menu.html) 與 WXSS(menu.less) 兩大部分組成,由檢視最小單元 - 元件來進行展示。檢視層將邏輯層的資料(menu.js+menu.json)反應成檢視,同時將檢視層中定義的事件傳送給邏輯層,一圖以蔽之。
WXML
WXML(WeiXin Markup Language)與HTML對應,用於描述頁面的結構,可以類比React的JSX。專案中menu.html為WXML語法,一個頁面的頂層是page節點。WXML中獲取邏輯層定義的資料後,通過一系列自己的語法和邏輯展示出這些資料。結構上元件是其最小單元,通過以下方式動態渲染。
1、資料繫結
資料繫結是最簡單的使用資料方式,語法採用Mustache的變數替換,用雙大括號將變數包起來,如果元件的屬性則需將資料放置於引號之中。
<view class="dish-item" data-id="{{dishId}}"><text class="name">{{dishName}}</text></view>複製程式碼
資料繫結還支援ES6規範的擴充套件運算子 “...”、解構賦值。
<template is="dishItem" data="{{...item, count, soldout: true }}"></template>複製程式碼
2、邏輯運算
雙大括號中可進行算數運算、三目運算、邏輯判斷、字串拼接等操作。
<text class="{{orderBanner.type !== 0 ? 'order-banner arrow' : 'order-banner'}}">{{orderBanner.text}}</text>複製程式碼
3、條件渲染
與常用模板語言將渲染內容寫在 if/else 判斷條件之中不一樣的是,小程式的條件渲染將渲染條件直接寫在渲染內容元件的 wx:if/wx:else 屬性中,如果渲染元件為多個,可將多個元件放在
if/else
<text wx:if="{{item.soldOut}}" class="status-soldout">已售完</text>
<template wx:else is="numberCount" data="{{count: cartSpuCount[item.spuId]}}"></template>複製程式碼
<block>
<block wx:if="{{serverError}}">
<text>點小評去吃滿漢全席啦~</text>
<button class="menu-btn" bindtap="requestMenu">重試</button>
</block>複製程式碼
4、列表渲染
列表渲染是將遍歷元素作為渲染元件的wx:for屬性值,與此相關的還有以下幾個屬性:
- wx:key:遍歷元素的唯一的識別符號,主要用於資料動態變化時dom的更新機制,資料不變則可無視
- wx:for-item:遍歷元素的變數名,預設item
- wx:for-index:遍歷元素下標的變數名,預設index
注意:以上屬性值雖然是字串,為正確取值單詞間都不要使用-等符號連線(如dish-item在使用時{{dish-item}}會解析成減號而取不到值)。
專案中資料較為複雜,使用測試資料舉例:
<block wx:for="{{testData}}"
wx:for-item="mainitem"
wx:key="{{mainindex}}"
wx:for-index="mainindex">
<view wx:for="{{mainitem}}"
wx:for-item="subitem"
wx:key="{{subitem.id}}"
wx:for-index="subindex">
<view class="dom-item">第一層index: {{mainindex}} id: {{subitem.id}} name: {{subitem.name}}</view>
</view>
</block>複製程式碼
以上程式碼結構上分為兩層:
1、第一層block迴圈遍歷testData陣列,每個遍歷值變數名為mainitem;
2、第二層view迴圈遍歷mainitem陣列,每個遍歷值變數名為subitem,展示第一層index,第二層id和name屬性;
// 建立頁面例項物件
Page({
/**
* 頁面的初始資料
*/
data: {
"testData": [
[ {
"id": "1-1",
"name": "節點1 - 1"
}, {
"id": "1-2",
"name": "節點1 - 2"
}], [{
"id": "2-1",
"name": "節點2 - 1"
}, {
"id": "2-2",
"name": "節點2 - 2"
}]
]
}
})複製程式碼
展示結果:
開發過程中曾碰到
- wx:for第二層中wx:for-item和wx:for-index失效
- wx:for渲染異常
- wx:for中wx:index錯亂
以上問題小程式更新版本後均已修復。
注意:
- 1、迴圈遍歷時,除官方說明的陣列型別可以迴圈遍歷外,物件型別也可通過wx:for進行屬性遍歷,此時for-index為屬性的key值。
如將上面例子中testData換成物件型別:
// 建立頁面例項物件
Page({
/**
* 頁面的初始資料
*/
data: {
"testData": {
"a": [{
"id": "1-1",
"name": "節點1 - 1"
}, {
"id": "1-2",
"name": "節點1 - 2"
}],
"b": [{
"id": "2-1",
"name": "節點2 - 1"
}, {
"id": "2-2",
"name": "節點2 - 2"
}]
}
}
})複製程式碼
結果為:
- 2、迴圈遍歷時,小程式之前還支援wx:forin遍歷,功能和wx:for相似,但官方文件中未說明,現在嘗試不會報錯,但功能已經失效,估計後期已經合併。
5、模板 & 引用
模板類似於React中的元件component的概念,可以在模板中定義程式碼片段,然後在不同的地方呼叫,減少重複的程式碼。
1、定義:使用name屬性,作為模板的名字,然後在<template/>內定義模板程式碼片段;
2、使用方式有2種:
- 使用include方式,將目標檔案除了<template/>的整個程式碼引入,相當於是拷貝到include位置,所以無法傳入引數;
- 通過import的方式引入定義的檔案,然後通過<template/>元件的 is 屬性,宣告需要的使用的模板,然後將模板所需要的 data 傳入,模板擁有自己的作用域,只能使用data傳入的資料。
注意:
- 只會 import 目標檔案中定義的 <template/>,不能引用目標檔案中引用的 <template/>。
- React的父元件通過props將資料傳入子元件中,傳值方式為引用傳值,子元件中可修改自身props影響父元件資料。小程式的模板只能單向使用傳入的資料。
示例(單個菜品元件):
<import src="../../components/common/dish-item.wxml" />
<template is="dishItem" data="{{...item}}"></template>複製程式碼
6、繫結事件
事件名稱為字串,會預設傳入event引數,無法定製其他引數,所以一般將所需引數通過data-屬性繫結至元件後通過e.currentTarget.dataset獲取。
<view class="cart-btn" data-type="1" bindtap="redirectCart">選好了</view>複製程式碼
WXSS
WXSS(WeiXin Style Sheet)與CSS對應,用於描述頁面的樣式。
定義在app.less中的樣式為全域性樣式,作用於每一個頁面;在page的wxss檔案中定義的樣式為區域性樣式,只作用在對應的頁面,並會覆蓋app.less中相同的選擇器,如程式碼結構中menu.less作用於menu.html。
1、支援的特性
- 內聯樣式:元件的 style 接收動態的樣式,在執行時會進行解析,請儘量避免將靜態的樣式寫進style中,以免影響渲染速度。
- 選擇器
對於常用的選擇器,目前支援的選擇器有:
注:綠色背景色行表示官方文件中沒有說明,但經個人親測後確定也支援的選擇器。
目前不支援的選擇器有:
注意:
- 如之前提到,頁面的頂層是
節點,所以作用於整個頁面的樣式或修改頂層節點樣式請使用page選擇器。 - 小程式目前不支援Media Query。
2、擴充套件的特性
- 尺寸單位rpx
rpx為小程式自創的單位,可以根據螢幕寬度進行自適應。規定螢幕寬為750rpx。如在iPhone6上,螢幕寬度為375px,共有750個物理畫素,則750rpx = 375px = 750物理畫素,1rpx = 0.5px = 1物理畫素。
建議:開發微信小程式時設計師可以用 iPhone6 作為視覺稿的標準。
注意: 由於數值較小時渲染時會存在四捨五入的情況,在較小螢幕上差距會很大,所以要求精確而較小的檢視內容需避免使用此單位。
如下圖所示菜品的減號操作圖示,高度iPhone6(750)下是2px,iPhone4s(640)下直接渲染成了1px(實際比例值為1.7px),而加號按鈕圖示高度iPhone6(750)下是11px,iPhone4s(640)下渲染成了9px(實際比例值為9.48px),誤差比例較小沒有出現明顯視覺問題,所以兩者看起來會不協調。
- 樣式匯入
用@import語句可以匯入外聯樣式表,@import後跟需要匯入的外聯樣式表的相對路徑,用;表示語句結束。
元件
如上WXML中所述,元件是檢視層的基本組成單元,與HMTL中標籤作用類似,基於Web Component標準,屬性和內容的使用方法也和HTML標籤類似,元件和屬性都須小寫。
1、元件列表
2、原生元件
如上統計,input、textarea、video、map、canvas為系統原生元件。原生元件相對來說效能和使用者互動方面會有所提升。
以部分機型input元素fixed時喚起鍵盤被遮擋的問題舉例,在某魅族機型上H5頁面中父元素fixed的輸入框會被遮擋:
同一機型小程式中,輸入框不會被遮擋:
3、元件屬性
支援型別
- Boolean:布林值
- Number:數字
- String:字元
- Array:陣列
- Object:物件
- EventHandler:事件處理函式名,事件繫結(如bindtap)屬性
- Any:任意屬性(不是很明白是什麼意思)
共同屬性
- id:元件的唯一標識
- class:元件的樣式類,和wxss中定義的class選擇器對應
- style:內聯樣式
- hidden:元件是否顯示
- data-*:自定義屬性,可傳入自定義資料,邏輯層事件處理函式中通過e.currentTarget.dataset獲取。
- bind / catch:都是事件繫結,bind事件繫結不會阻止冒泡事件向上冒泡,catch事件繫結可以阻止冒泡事件向上冒泡。
特殊屬性
特殊屬性是各個元件自己定義的屬性,如 <icon> 元件的size屬性,具體各參見官方文件各元件具體說明。
相容性
渲染機制
根據官方文件的說明:
- 在iOS 上,小程式的javascript 程式碼是執行在JavaScriptCore 中,是由WKWebView來渲染的,環境有iOS8、iOS9、iOS10;
- 在Android上,小程式的javascript程式碼是通過X5 JSCore來解析,是由 X5 基於Mobile Chrome 37 核心來渲染的;
- 在開發工具上,小程式的javascript程式碼是執行在nwjs中,是由Chrome Webview來渲染的。
由於核心渲染表現不一致,H5開發過程中存在X5瀏覽器和各類機型或系統的相容性的部分問題小程式中依舊存在。
常見問題分類
- X5瀏覽器
X5前端開發問題
X5 Caniuse Tests - iOS/Android版本導致的相容性問題:小程式在iOS/Andriod 系統中只要求微信版本 >= 6.5.3,對機型的系統版本並無限制。
- Android機型碎片化導致的相容性問題
效能優化
前端常用的模板方案一般有2種:
- 1、將模板編譯成js函式程式碼,通過字串拼接的方式生成渲染的DOM節點,如:Mustache / tpl(點評內部開發使用),資料更改時DOM節點全部更新;
- 2、字串parse和compile後拼接渲染外,有自己的DOM節點更新機制, 如:Vue.js / React等,資料更改時通過DOM Diff演算法更新DOM節點。
當資料改變觸發渲染層重新渲染的時候,會校正帶有key的元件,框架會確保他們被重新排序,而不是重新建立,以確保使元件保持自身的狀態,並且提高列表渲染時的效率。
小程式對元件的渲染方式我們不得而知,只能對開發中碰到的一些問題來推測。結合小程式對列表渲染wx:key的解釋可知其模板渲染屬於第二種,資料更新時會根據key進行渲染優化。但小程式官方未提供相關介面或效能除錯工具,所以專案中我們只能自己嘗試不同方案然後對比渲染速度。以選單頁面為例,商戶菜品數量多者成百上千,優化後的效果對比還是比較明顯。
採取方案
- 1、減少資料巢狀層數/減少元件巢狀層數:選單頁面將菜品資料扁平化為一層,併合理利用key值;設計元件結構時採用精簡的元件結構,減少渲染時的資料遍歷和元件巢狀深度帶來的效能消耗。
- 2、將資料變動的元件與資料不變的元件進行拆分,減少資料更改帶來的元件更新量,如將加減按鈕和菜品資訊分離。
- 3、使用動態載入等方式減小首屏渲染資料量,提升使用者體驗。
本文時間為2017-02-24,所提小程式暫不支援屬性或碰到的bug以此時間為準,後續更新或修復請檢視官方文件。
參考資料:
微信小程式開發者文件