[TOC]
## 1.2 WXML 模板——`*.wxml`文件
`WXML` 全稱是 WeiXin Markup Language,是小程序框架設計的一套標簽語言,結合小程序的基礎組件、事件系統,可以構建出頁面的結構。
WXML 文件后綴名是 .wxml ,簡單的 WXML語句在語法上同 HTML 非常相似,由標簽、屬性等構成。
不帶有任何邏輯功能的 WXML 基本語法如下:
~~~
<!-- 在此處寫注釋 -->
<標簽名 屬性名1="屬性值1" 屬性名2="屬性值2" ...> ...</標簽名>
~~~
一個完整的 WXML語句由一段開始標簽和一段結束標簽組成,在標簽中可以是內容,也可以是其他的 WXML 語句,這一點上同 HTML 是一致的。有所不同的是,WXML 要求`標簽必須是嚴格閉合`的,沒有閉合將會導致編譯錯誤。
屬性總是定義在開始標簽中,除了一些特殊的屬性外,其余屬性的格式都是key="value" 的方式成對出現。需要注意的是,WXML中的`屬性是大小寫敏感`的,也就是說 class 和 Class 在WXML中是不同的屬性。
在網頁的一般開發流程中,我們通常會通過 `JS` 操作 `DOM` (對應 HTML 的描述產生的樹),以引起界面的一些變化響應用戶的行為。
例如,用戶點擊某個按鈕的時候,`JS` 會記錄一些狀態到 `JS` 變量里邊,同時通過 DOM API 操控 `DOM` 的屬性或者行為,進而引起界面一些變化。
但是,當項目越來越大的時候,JS代碼中會充斥著非常多的界面交互邏輯和程序的各種狀態變量,顯然這不是一個很好的開發模式,因此就有了 MVVM 的開發模式(例如 React, Vue),提倡把渲染和邏輯分離。
簡單來說就是不要再讓 JS 直接操控 DOM,JS 只需要管理狀態即可,然后再通過一種模板語法來描述狀態和界面結構的關系即可。
微信小程序的框架就用到了這個思路,如果需要把一個 `Hello World` 的字符串顯示在界面上。
WXML 是這么寫 :
~~~
<text>{{msg}}</text>
~~~
JS 只需要管理狀態即可:
~~~
this.setData({ msg: "Hello World" })
~~~
通過 `{{ }}` 的語法把一個變量綁定到界面上,我們稱為數據綁定。僅僅通過數據綁定還不夠完整的描述狀態和界面的關系,還需要 `if/else`, `for`等控制能力,在小程序里邊,這些控制能力都用 `wx:` 開頭的屬性來表達。
### 1.2.1 數據綁定
用戶界面呈現會因為當前時刻數據不同而有所不同,或者是因為用戶的操作發生動態改變,程序在運行過程中,要有動態的去改變渲染界面的能力。
在 Web 開發中,開發者使用 JavaScript 通過Dom 接口來完成界面的實時更新。
在小程序中,使用 WXML 語言所提供的數據綁定功能,來完成此項功能。WXML 通過 {{變量名}} 來綁定 WXML 文件和對應的 JavaScript 文件中的 data 對象屬性。
代碼清單2-5 數據綁定示例
- index.wxml文件
~~~
<!--pages/wxml/index.wxml-->
<text>當前時間:{{time}}</text>
~~~
- index.js文件
~~~
// pages/wxml/index.js
Page({
/**
* 頁面的初始數據
*/
data: {
time: (new Date()).toString()
},
})
~~~
屬性值也可以動態的去改變,有所不同的是,屬性值必須被包裹在雙引號中,如下:
代碼清單2-8 屬性值的綁定
~~~
<!-- 正確的寫法 -->
<text data-test="{{test}}"> hello world</text>
<!-- 錯誤的寫法 -->
<text data-test={{test}}> hello world </text >
~~~
注意:
1. `變量名是大小寫敏感`的,也就是說wxml 文件中 {{name}} 和 {{Name}} 是兩個不同的變量。也就意味著進行數據綁定時,同一個變量名要在wxml文件和js文件保持一模一樣。
2. wxml文件中無定義,js文件中有設置值的變量,不會被同步到 wxml 文件中。
3. wxml文件中已定義,js文件中被設置為 undefined 的變量,不會被同步到 wxml 文件中。
### 1.2.2 邏輯語法
通過` {{ 變量名 }}` 語法可以使得 WXML 擁有動態渲染的能力,除此外還可以在 {{ }} 內進行簡單的邏輯運算。
- 三元運算
- 算數運算
- 字符串的拼接
- 放置常量(數字、字符串或者是數組)
~~~
<!-- 三元運算 -->
<text>{{ a === 10? "變量 a 等于10": "變量 a 不等于10"}}</text>
<!-- 算數運算 -->
<!--{ a: 1, b: 2, c: 3 }-->
<view> {{a + b}} + {{c}} + d </view>
<!-- 輸出 3 + 3 + d -->
<!-- 字符串的拼接 -->
<!--{ name: 'world' }-->
<view>{{"hello " + name}}</view>
<!-- 輸出 hello world -->
<!-- 常量 -->
<text>{{[1,2,3]}}</text>
<!-- 輸出 1,2,3 -->
<text>{{"hello world"}}</text>
<!-- 輸出 hello world -->
~~~
### 1.2.3 條件邏輯
WXML 中,使用`if-else`來判斷是否需要渲染該代碼塊:
- 語法:`wx:if="{{condition}}"`,`wx:elseif="{{condition}}"`,`wx:else`
~~~
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
~~~
`wx:if`是一個控制屬性,需要將它添加到一個標簽上。如果要一次性判斷多個組件標簽,可以使用一個\<block\\> 標簽將多個組件包裝起來,并在上邊使用 wx:if 控制屬性。
~~~
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
~~~
### 1.2.4 列表渲染
在組件上使用 `wx:for` 控制屬性綁定一個數組,即可使用數組中各項的數據重復渲染該組件。默認數組的當前項的下標變量名默認為` index`,數組當前項的變量名默認為 `item`。
代碼清單2-13 列表渲染示例
~~~
<!-- array 是一個數組 -->
<view wx:for="{{array}}">
{{index}}: {{item.message}}
</view>
<!-- 對應的腳本文件
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
-->
~~~
使用 `wx:for-index` 指定數組當前下標的變量名,使用 `wx:for-item` 指定數組當前元素的變量名:
~~~
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}}: {{itemName.message}}
</view>
~~~
類似 `block wx:if` ,也可以將 `wx:for` 用在 `<block>` 標簽上,以渲染一個包含多節點的結構塊。例如:
~~~
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: </view>
<view> {{item}} </view>
</block>
~~~
如果列表中項目的位置會動態改變或者有新的項目添加到列表中,并且希望列表中的項目保持自己的特征和狀態(如 `<input/>` 中的輸入內容, `<switch/>` 的選中狀態),需要使用 `wx:key` 來指定列表中項目的唯一的標識符。
`wx:key` 的值以兩種形式提供:
1. 字符串,代表在 for 循環的 array 中 item 的某個 property,該 property 的值需要是列表中唯一的字符串或數字,且不能動態改變。
2. 保留關鍵字 `this` 代表在 for 循環中的 item 本身,這種表示需要 item 本身是一個唯一的字符串或者數字,如:當數據改變觸發渲染層重新渲染的時候,會校正帶有 key 的組件,框架會確保他們被重新排序,而不是重新創建,以確保使組件保持自身的狀態,并且提高列表渲染時的效率。
代碼清單2-14 使用 wx:key 示例(WXML)
~~~
<switch wx:for="{{objectArray}}" wx:key="unique" > {{item.id}} </switch>
<button bindtap="switch"> Switch </button>
<button bindtap="addToFront"> Add to the front </button>
<switch wx:for="{{numberArray}}" wx:key="*this" > {{item}} </switch>
<button bindtap="addNumberToFront"> Add to the front </button>
~~~
代碼清單2-15 使用 wx:key 示例(JavaScript)
~~~
Page({
data: {
objectArray: [
{id: 5, unique: 'unique_5'},
{id: 4, unique: 'unique_4'},
{id: 3, unique: 'unique_3'},
{id: 2, unique: 'unique_2'},
{id: 1, unique: 'unique_1'},
{id: 0, unique: 'unique_0'},
],
numberArray: [1, 2, 3, 4]
},
switch: function(e) {
const length = this.data.objectArray.length
for (let i = 0; i < length; ++i) {
const x = Math.floor(Math.random() * length)
const y = Math.floor(Math.random() * length)
const temp = this.data.objectArray[x]
this.data.objectArray[x] = this.data.objectArray[y]
this.data.objectArray[y] = temp
}
this.setData({
objectArray: this.data.objectArray
})
},
addToFront: function(e) {
const length = this.data.objectArray.length
this.data.objectArray = [{id: length, unique: 'unique_' + length}].concat(this.data.objectArray)
this.setData({
objectArray: this.data.objectArray
})
},
addNumberToFront: function(e){
this.data.numberArray = [ this.data.numberArray.length + 1 ].concat(this.data.numberArray)
this.setData({
numberArray: this.data.numberArray
})
}
})
~~~
### 1.2.5 模板
WXML提供模板(template),可以在模板中定義代碼片段,然后在不同的地方調用。使用 `name` 屬性,作為模板的名字。然后在 `<template/>` 內定義代碼片段,如:
代碼清單2-16 定義模板
~~~
<template name="msgItem">
<view>
<text> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>
~~~
使用 `is` 屬性,聲明需要的使用的模板,然后將模板所需要的 `data` 傳入,如代碼2-17所示。
代碼清單2-17 模板使用示例
~~~
<!--
{
index: 0,
msg: 'this is a template',
time: '2016-06-18'
}
-->
<template name="msgItem">
<view>
<text> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>
<template is="msgItem" data="{{...item}}"/>
<!-- 輸出
0: this is a template Time: 2016-06-18
-->
~~~
`is`還可以動態決定具體需要渲染哪個模板,如代碼2-18所示。
代碼清單2-18 動態使用模板
~~~
<template name="odd">
<view> odd </view>
</template>
<template name="even">
<view> even </view>
</template>
<block wx:for="{{[1, 2, 3, 4, 5]}}">
<template is="{{item % 2 == 0 ? 'even' : 'odd'}}"/>
</block>
<!-- 輸出
odd
even
odd
even
odd
-->
~~~
### 1.2.6 引用
WXML 提供兩種文件引用方式`import`和`include`。
- `import` 可以在該文件中使用目標文件定義的 template,如:
在 item.wxml 中定義了一個叫 item的 template :
~~~
<!-- item.wxml -->
<template name="item">
<text>{{text}}</text>
</template>
~~~
在 index.wxml 中引用了 item.wxml,就可以使用 item模板:
~~~
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>
~~~
需要注意的是 import 有作用域的概念,即只會 import 目標文件中定義的 template,而不會 import 目標文件中 import 的 template,簡言之就是 import 不具有遞歸的特性。
- `include` 可以將目標文件中除了 `<template/> <wxs/>` 外的整個代碼引入,相當于是拷貝到 include 位置,如代碼2-22、代碼2-23、代碼2-24所示。
代碼清單2-22 index.wxml
~~~
<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>
~~~
代碼清單2-23 header.wxml
~~~
<!-- header.wxml -->
<view> header </view>
~~~
代碼清單2-24 footer.wxml
~~~
<!-- footer.wxml -->
<view> footer </view>
~~~
### 1.2.7 共同屬性
所有wxml 標簽都支持的屬性稱之為共同屬性,如表2-1所示。
:-: 表2-1 共同屬性
| 屬性名 |類型| 描述 | 注解 |
| --- | --- | --- | --- |
| id | String | 組件的唯一標識 | 整個頁面唯一 |
| class | String | 組件的樣式類 | 在對應的 WXSS 中定義的樣式類 |
| style | String | 組件的內聯樣式 | 可以動態設置的內聯樣式 |
| hidden | Boolean | 組件是否顯示 | 所有組件默認顯示 |
| data-* | Any | 自定義屬性 | 組件上觸發的事件時,會發送給事件處理函數 |
| bind*/catch* | EventHandler | 組件的事件 |
- 微信
- 小程序
- 1. 代碼組成
- 1.1 JSON配置--'*.json'文件
- 1.2 WXML模板--'*.wxml'文件
- 1.3 WXSS樣式--'*.wxss'文件
- 1.4 JavaScript腳本--'*.js'文件
- 2. 客戶端運行
- 2.1 邏輯層和渲染層
- 2.1.1 邏輯層--App Service
- 2.1.2 渲染層/視圖層--View
- 2.1.3 通信模型
- 2.1.4 數據驅動
- 2.1.5 雙線程下的界面渲染
- 2.2 程序與頁面
- 2.3 組件
- 2.4 API
- 2.5 事件
- 2.6 兼容
- 3. 應用設計
- 3.1 Flex布局
- 3.2 界面常見的交互反饋
- 3.3 發起HTTPS網絡通信--wx.request
- 3.4 微信登錄
- 3.5 本地數據緩存
- 3.6 設備能力
- 4. 小程序的協同工作和發布
- 4.1 協同工作
- 4.2 用戶體驗審視
- 4.3 發布
- 4.4 運營
- 5. 底層框架
- 5.1 雙線程模型
- 5.2 組件系統--Exparser框架
- 5.3 原生組件
- 5.4 小程序與客戶端通信原理
- 6. 運行和性能優化
- 6.1 啟動--代碼加載
- 6.2 頁面準備
- 6.3 數據通信
- 6.4 視圖層渲染
- 6.5 原生組件通信
- 7. 小程序基礎庫的更新迭代
- 8. 微信開發者工具
- 騰訊云支持
- wafer
- Wafer2 快速開發 Demo - PHP
- WXAPI
- api列表