大衆點評點餐小程序開發經驗 - 視圖層

做者介紹:何延希,美團點評工程師,4年Web開發經驗,如今是美團點評點餐團隊的一員。javascript

接上一篇大衆點評點餐小程序開發經驗 - 概述,本期想要和你們分享下大衆點評點餐小程序中View視圖層的一些開發經驗。本文部分示例來自於「大衆點評點餐」小程序的菜單頁面。html

頁面代碼結構爲:前端

menu
├── menu.html
├── menu.js
├── menu.json
└── menu.lessvue

咱們將要說的小程序的View視圖層是由WXML(menu.html) 與 WXSS(menu.less) 兩大部分組成,由視圖最小單元 - 組件來進行展現。視圖層將邏輯層的數據(menu.js+menu.json)反應成視圖,同時將視圖層中定義的事件發送給邏輯層,一圖以蔽之。java

WXML

WXML(WeiXin Markup Language)與HTML對應,用於描述頁面的結構,能夠類比React的JSX。項目中menu.html爲WXML語法,一個頁面的頂層是page節點。WXML中獲取邏輯層定義的數據後,經過一系列本身的語法和邏輯展現出這些數據。結構上組件是其最小單元,經過如下方式動態渲染。react

一、數據綁定

數據綁定是最簡單的使用數據方式,語法採用Mustache的變量替換,用雙大括號將變量包起來,若是組件的屬性則需將數據放置於引號之中。git

<view class="dish-item" data-id="{{dishId}}"><text class="name">{{dishName}}</text></view>複製代碼

數據綁定還支持ES6規範的擴展運算符 「...」、解構賦值。github

<template is="dishItem" data="{{...item, count, soldout: true }}"></template>複製代碼

二、邏輯運算

雙大括號中可進行算數運算、三目運算、邏輯判斷、字符串拼接等操做。web

<text class="{{orderBanner.type !== 0 ? 'order-banner arrow' : 'order-banner'}}">{{orderBanner.text}}</text>複製代碼

三、條件渲染

與經常使用模板語言將渲染內容寫在 if/else 判斷條件之中不同的是,小程序的條件渲染將渲染條件直接寫在渲染內容組件的 wx:if/wx:else 屬性中,若是渲染組件爲多個,可將多個組件放在 組件內,渲染條件置於 <block>組件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>複製代碼

四、列表渲染

列表渲染是將遍歷元素做爲渲染組件的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>複製代碼

以上代碼結構上分爲兩層:
一、第一層block循環遍歷testData數組,每一個遍歷值變量名爲mainitem;
二、第二層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錯亂
    以上問題小程序更新版本後均已修復。

注意:

  • 一、循環遍歷時,除官方說明的數組類型能夠循環遍歷外,對象類型也可經過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"
            }]
        }
    }
})複製代碼

結果爲:

  • 二、循環遍歷時,小程序以前還支持wx:forin遍歷,功能和wx:for類似,但官方文檔中未說明,如今嘗試不會報錯,但功能已經失效,估計後期已經合併。

五、模板 & 引用

模板相似於React中的組件component的概念,能夠在模板中定義代碼片斷,而後在不一樣的地方調用,減小重複的代碼。

一、定義:使用name屬性,做爲模板的名字,而後在<template/>內定義模板代碼片斷;
二、使用方式有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>複製代碼

六、綁定事件

事件名稱爲字符串,會默認傳入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。

一、支持的特性

  • 內聯樣式:組件的 style 接收動態的樣式,在運行時會進行解析,請儘可能避免將靜態的樣式寫進style中,以避免影響渲染速度。
  • 選擇器
    對於經常使用的選擇器,目前支持的選擇器有:

注:綠色背景色行表示官方文檔中沒有說明,但經我的親測後肯定也支持的選擇器。

目前不支持的選擇器有:


注意:

  • 如以前提到,頁面的頂層是 節點,因此做用於整個頁面的樣式或修改頂層節點樣式請使用page選擇器。
  • 小程序目前不支持Media Query。

二、擴展的特性

  • 尺寸單位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標籤相似,組件和屬性都須小寫。

一、組件列表

二、原生組件

如上統計,input、textarea、video、map、canvas爲系統原生組件。原生組件相對來講性能和用戶交互方面會有所提高。

以部分機型input元素fixed時喚起鍵盤被遮擋的問題舉例,在某魅族機型上H5頁面中父元素fixed的輸入框會被遮擋:

同一機型小程序中,輸入框不會被遮擋:

三、組件屬性

支持類型

  • 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來渲染的,環境有iOS八、iOS九、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種:

  • 一、將模板編譯成js函數代碼,經過字符串拼接的方式生成渲染的DOM節點,如:Mustache / tpl(點評內部開發使用),數據更改時DOM節點所有更新;
  • 二、字符串parse和compile後拼接渲染外,有本身的DOM節點更新機制, 如:Vue.js / React等,數據更改時經過DOM Diff算法更新DOM節點。

當數據改變觸發渲染層從新渲染的時候,會校訂帶有key的組件,框架會確保他們被從新排序,而不是從新建立,以確保使組件保持自身的狀態,而且提升列表渲染時的效率。

小程序對組件的渲染方式咱們不得而知,只能對開發中碰到的一些問題來推測。結合小程序對列表渲染wx:key的解釋可知其模板渲染屬於第二種,數據更新時會根據key進行渲染優化。但小程序官方未提供相關接口或性能調試工具,因此項目中咱們只能本身嘗試不一樣方案而後對比渲染速度。以菜單頁面爲例,商戶菜品數量多者成百上千,優化後的效果對比仍是比較明顯。

採起方案

  • 一、減小數據嵌套層數/減小組件嵌套層數:菜單頁面將菜品數據扁平化爲一層,併合理利用key值;設計組件結構時採用精簡的組件結構,減小渲染時的數據遍歷和組件嵌套深度帶來的性能消耗。
  • 二、將數據變更的組件與數據不變的組件進行拆分,減小數據更改帶來的組件更新量,如將加減按鈕和菜品信息分離。
  • 三、使用動態加載等方式減少首屏渲染數據量,提高用戶體驗。

本文時間爲2017-02-24,所提小程序暫不支持屬性或碰到的bug以此時間爲準,後續更新或修復請查看官方文檔

參考資料:
微信小程序開發者文檔

相關文章
相關標籤/搜索