# 小程序 視圖
[TOC]
# WXML
WXML(WeiXin Markup Language)是框架設計的一套標簽語言,結合[基礎組件](https://developers.weixin.qq.com/miniprogram/dev/component/index.html)、[事件系統](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html),可以構建出頁面的結構。
用以下一些簡單的例子來看看 WXML 具有什么能力:
### [數據綁定](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/data.html)
```
<!--wxml-->
<view> {{message}} </view>
```
```
// page.js
Page({
data: {
message: 'Hello MINA!'
}
})
```
### [列表渲染](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/list.html)
```
<!--wxml-->
<view wx:for="{{array}}"> {{item}} </view>
```
```
// page.js
Page({
data: {
array: [1, 2, 3, 4, 5]
}
})
```
### [條件渲染](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/conditional.html)
```
<!--wxml-->
<view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view wx:elif="{{view == 'APP'}}"> APP </view>
<view wx:else="{{view == 'MINA'}}"> MINA </view>
```
```
// page.js
Page({
data: {
view: 'MINA'
}
})
```
### [模板](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/template.html)
```
<!--wxml-->
<template name="staffName">
<view>
FirstName: {{firstName}}, LastName: {{lastName}}
</view>
</template>
<template is="staffName" data="{{...staffA}}"></template>
<template is="staffName" data="{{...staffB}}"></template>
<template is="staffName" data="{{...staffC}}"></template>
```
```
// page.js
Page({
data: {
staffA: {firstName: 'Hulk', lastName: 'Hu'},
staffB: {firstName: 'Shang', lastName: 'You'},
staffC: {firstName: 'Gideon', lastName: 'Lin'}
}
})
```
### [事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html)
```
<view bindtap="add"> {{count}} </view>
```
```
Page({
data: {
count: 1
},
add: function(e) {
this.setData({
count: this.data.count + 1
})
}
})
```
具體的能力以及使用方式在以下章節查看:
[數據綁定](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/data.html)、[列表渲染](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/list.html)、[條件渲染](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/conditional.html)、[模板](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/template.html)、[事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html)、[引用](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/import.html)
# 數據綁定
WXML 中的動態數據均來自對應 Page 的 data。
## 簡單綁定
數據綁定使用 Mustache 語法(雙大括號)將變量包起來,可以作用于:
### 內容
```
<view> {{ message }} </view>
```
```
Page({
data: {
message: 'Hello MINA!'
}
})
```
### 組件屬性(需要在雙引號之內)
```
<view id="item-{{id}}"> </view>
```
```
Page({
data: {
id: 0
}
})
```
### 控制屬性(需要在雙引號之內)
```
<view wx:if="{{condition}}"> </view>
```
```
Page({
data: {
condition: true
}
})
```
### 關鍵字(需要在雙引號之內)
`true`:boolean 類型的 true,代表真值。
`false`: boolean 類型的 false,代表假值。
```
<checkbox checked="{{false}}"> </checkbox>
```
*特別注意:不要直接寫 `checked="false"`,其計算結果是一個字符串,轉成 boolean 類型后代表真值。*
## 運算
可以在 `{{}}` 內進行簡單的運算,支持的有如下幾種方式:
### 三元運算
```
<view hidden="{{flag ? true : false}}"> Hidden </view>
```
### 算數運算
```
<view> {{a + b}} + {{c}} + d </view>
```
```
Page({
data: {
a: 1,
b: 2,
c: 3
}
})
```
view中的內容為 `3 + 3 + d`。
### 邏輯判斷
```
<view wx:if="{{length > 5}}"> </view>
```
### 字符串運算
```
<view>{{"hello" + name}}</view>
```
```
Page({
data:{
name: 'MINA'
}
})
```
### 數據路徑運算
```
<view>{{object.key}} {{array[0]}}</view>
```
```
Page({
data: {
object: {
key: 'Hello '
},
array: ['MINA']
}
})
```
## 組合
也可以在 Mustache 內直接進行組合,構成新的對象或者數組。
### 數組
```
<view wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </view>
```
```
Page({
data: {
zero: 0
}
})
```
最終組合成數組`[0, 1, 2, 3, 4]`。
### 對象
```
<template is="objectCombine" data="{{for: a, bar: b}}"></template>
```
```
Page({
data: {
a: 1,
b: 2
}
})
```
最終組合成的對象是 `{for: 1, bar: 2}`
也可以用擴展運算符 `...` 來將一個對象展開
```
<template is="objectCombine" data="{{...obj1, ...obj2, e: 5}}"></template>
```
```
Page({
data: {
obj1: {
a: 1,
b: 2
},
obj2: {
c: 3,
d: 4
}
}
})
```
最終組合成的對象是 `{a: 1, b: 2, c: 3, d: 4, e: 5}`。
如果對象的 key 和 value 相同,也可以間接地表達。
```
<template is="objectCombine" data="{{foo, bar}}"></template>
```
```
Page({
data: {
foo: 'my-foo',
bar: 'my-bar'
}
})
```
最終組合成的對象是 `{foo: 'my-foo', bar:'my-bar'}`。
注意:上述方式可以隨意組合,但是如有存在變量名相同的情況,后邊的會覆蓋前面,如:
```
<template is="objectCombine" data="{{...obj1, ...obj2, a, c: 6}}"></template>
```
```
Page({
data: {
obj1: {
a: 1,
b: 2
},
obj2: {
b: 3,
c: 4
},
a: 5
}
})
```
最終組合成的對象是 `{a: 5, b: 3, c: 6}`。
注意: 花括號和引號之間如果有空格,將最終被解析成為字符串
```
<view wx:for="{{[1,2,3]}} ">
{{item}}
</view>
```
等同于
```
<view wx:for="{{[1,2,3] + ' '}}">
{{item}}
</view>
```
# 列表渲染
### wx:for
在組件上使用?`wx:for`?控制屬性綁定一個數組,即可使用數組中各項的數據重復渲染該組件。
默認數組的當前項的下標變量名默認為?`index`,數組當前項的變量名默認為?`item`
```
<view wx:for="{{array}}">
{{index}}: {{item.message}}
</view>
```
```
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
```
使用?`wx:for-item`?可以指定數組當前元素的變量名,
使用?`wx:for-index`?可以指定數組當前下標的變量名:
```
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}}: {{itemName.message}}
</view>
```
`wx:for`?也可以嵌套,下邊是一個九九乘法表
```
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
<view wx:if="{{i <= j}}">
{{i}} * {{j}} = {{i * j}}
</view>
</view>
</view>
```
### block wx:for
類似?`block wx:if`,也可以將?`wx:for`?用在`<block/>`標簽上,以渲染一個包含多節點的結構塊。例如:
```
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: </view>
<view> {{item}} </view>
</block>
```
### wx:key
如果列表中項目的位置會動態改變或者有新的項目添加到列表中,并且希望列表中的項目保持自己的特征和狀態(如?`<input/>`?中的輸入內容,`<switch/>`?的選中狀態),需要使用?`wx:key`?來指定列表中項目的唯一的標識符。
`wx:key`?的值以兩種形式提供
1. 字符串,代表在 for 循環的 array 中 item 的某個 property,該 property 的值需要是列表中唯一的字符串或數字,且不能動態改變。
2. 保留關鍵字?`*this`?代表在 for 循環中的 item 本身,這種表示需要 item 本身是一個唯一的字符串或者數字,如:
當數據改變觸發渲染層重新渲染的時候,會校正帶有 key 的組件,框架會確保他們被重新排序,而不是重新創建,以確保使組件保持自身的狀態,并且提高列表渲染時的效率。
如不提供?`wx:key`,會報一個?`warning`, 如果明確知道該列表是靜態,或者不必關注其順序,可以選擇忽略。
示例代碼:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/tpg5tKmv6kZt "在開發者工具中預覽效果")
```
<switch wx:for="{{objectArray}}" wx:key="unique" style="display: block;"> {{item.id}} </switch>
<button bindtap="switch"> Switch </button>
<button bindtap="addToFront"> Add to the front </button>
<switch wx:for="{{numberArray}}" wx:key="*this" style="display: block;"> {{item}} </switch>
<button bindtap="addNumberToFront"> Add to the front </button>
```
```
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
})
}
})
```
注意:
當?`wx:for`?的值為字符串時,會將字符串解析成字符串數組
```
<view wx:for="array">
{{item}}
</view>
```
等同于
```
<view wx:for="{{['a','r','r','a','y']}}">
{{item}}
</view>
```
注意:?花括號和引號之間如果有空格,將最終被解析成為字符串
```
<view wx:for="{{[1,2,3]}} ">
{{item}}
</view>
```
等同于
```
<view wx:for="{{[1,2,3] + ' '}}" >
{{item}}
</view>
```
條件渲染
wx:if
在框架中,使用 wx:if="{{condition}}" 來判斷是否需要渲染該代碼塊:
<view wx:if="{{condition}}"> True </view>
也可以用 wx:elif 和 wx:else 來添加一個 else 塊:
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
block wx:if
因為 wx:if 是一個控制屬性,需要將它添加到一個標簽上。如果要一次性判斷多個組件標簽,可以使用一個 <block/> 標簽將多個組件包裝起來,并在上邊使用 wx:if 控制屬性。
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
注意: <block/> 并不是一個組件,它僅僅是一個包裝元素,不會在頁面中做任何渲染,只接受控制屬性。
wx:if vs hidden
因為 wx:if 之中的模板也可能包含數據綁定,所以當 wx:if 的條件值切換時,框架有一個局部渲染的過程,因為它會確保條件塊在切換時銷毀或重新渲染。
同時 wx:if 也是惰性的,如果在初始渲染條件為 false,框架什么也不做,在條件第一次變成真的時候才開始局部渲染。
相比之下,hidden 就簡單的多,組件始終會被渲染,只是簡單的控制顯示與隱藏。
一般來說,wx:if 有更高的切換消耗而 hidden 有更高的初始渲染消耗。因此,如果需要頻繁切換的情景下,用 hidden 更好,如果在運行時條件不大可能改變則 wx:if 較好。
# 模板
WXML提供模板(template),可以在模板中定義代碼片段,然后在不同的地方調用。
### 定義模板
使用 name 屬性,作為模板的名字。然后在`<template/>`內定義代碼片段,如:
```
<!--
index: int
msg: string
time: string
-->
<template name="msgItem">
<view>
<text> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>
```
### 使用模板
使用 is 屬性,聲明需要的使用的模板,然后將模板所需要的 data 傳入,如:
```
<template is="msgItem" data="{{...item}}"/>
```
```
Page({
data: {
item: {
index: 0,
msg: 'this is a template',
time: '2016-09-15'
}
}
})
```
is 屬性可以使用 Mustache 語法,來動態決定具體需要渲染哪個模板:
```
<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>
```
### 模板的作用域
模板擁有自己的作用域,只能使用 data 傳入的數據以及模板定義文件中定義的?`<wxs />`?模塊。
# 事件
## 什么是事件
* 事件是視圖層到邏輯層的通訊方式。
* 事件可以將用戶的行為反饋到邏輯層進行處理。
* 事件可以綁定在組件上,當達到觸發事件,就會執行邏輯層中對應的事件處理函數。
* 事件對象可以攜帶額外信息,如 id, dataset, touches。
## 事件的使用方式
* 在組件中綁定一個事件處理函數。
如`bindtap`,當用戶點擊該組件的時候會在該頁面對應的Page中找到相應的事件處理函數。
```
<view id="tapTest" data-hi="WeChat" bindtap="tapName"> Click me! </view>
```
* 在相應的Page定義中寫上相應的事件處理函數,參數是event。
```
Page({
tapName: function(event) {
console.log(event)
}
})
```
* 可以看到log出來的信息大致如下:
```
{
"type":"tap",
"timeStamp":895,
"target": {
"id": "tapTest",
"dataset": {
"hi":"WeChat"
}
},
"currentTarget": {
"id": "tapTest",
"dataset": {
"hi":"WeChat"
}
},
"detail": {
"x":53,
"y":14
},
"touches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}],
"changedTouches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}]
}
```
## 事件詳解
### 事件分類
事件分為冒泡事件和非冒泡事件:
1. 冒泡事件:當一個組件上的事件被觸發后,該事件會向父節點傳遞。
2. 非冒泡事件:當一個組件上的事件被觸發后,該事件不會向父節點傳遞。
WXML的冒泡事件列表:
| 類型 | 觸發條件 | 最低版本 |
| --- | --- | --- |
| touchstart | 手指觸摸動作開始 | |
| touchmove | 手指觸摸后移動 | |
| touchcancel | 手指觸摸動作被打斷,如來電提醒,彈窗 | |
| touchend | 手指觸摸動作結束 | |
| tap | 手指觸摸后馬上離開 | |
| longpress | 手指觸摸后,超過350ms再離開,如果指定了事件回調函數并觸發了這個事件,tap事件將不被觸發 | [1.5.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 1.5.0 開始支持,低版本需做兼容處理。") |
| longtap | 手指觸摸后,超過350ms再離開(推薦使用longpress事件代替) | |
| transitionend | 會在 WXSS transition 或 wx.createAnimation 動畫結束后觸發 | |
| animationstart | 會在一個 WXSS animation 動畫開始時觸發 | |
| animationiteration | 會在一個 WXSS animation 一次迭代結束時觸發 | |
| animationend | 會在一個 WXSS animation 動畫完成時觸發 | |
| touchforcechange | 在支持 3D Touch 的 iPhone 設備,重按時會觸發 | [1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 1.9.90 開始支持,低版本需做兼容處理。") |
注:除上表之外的其他組件自定義事件如無特殊聲明都是非冒泡事件,如[`<form/>`](https://developers.weixin.qq.com/miniprogram/dev/component/form.html)的`submit`事件,[`<input/>`](https://developers.weixin.qq.com/miniprogram/dev/component/input.html)的`input`事件,[`<scroll-view/>`](https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html)的`scroll`事件,(詳見各個[組件](https://developers.weixin.qq.com/miniprogram/dev/component/index.html))
### 事件綁定和冒泡
事件綁定的寫法同組件的屬性,以 key、value 的形式。
* key 以`bind`或`catch`開頭,然后跟上事件的類型,如`bindtap`、`catchtouchstart`。自基礎庫版本?[1.5.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 1.5.0 開始支持,低版本需做兼容處理。")?起,在非[原生組件](https://developers.weixin.qq.com/miniprogram/dev/component/native-component.html)中,`bind`和`catch`后可以緊跟一個冒號,其含義不變,如`bind:tap`、`catch:touchstart`。
* value 是一個字符串,需要在對應的 Page 中定義同名的函數。不然當觸發事件的時候會報錯。
`bind`事件綁定不會阻止冒泡事件向上冒泡,`catch`事件綁定可以阻止冒泡事件向上冒泡。
如在下邊這個例子中,點擊 inner view 會先后調用`handleTap3`和`handleTap2`(因為tap事件會冒泡到 middle view,而 middle view 阻止了 tap 事件冒泡,不再向父節點傳遞),點擊 middle view 會觸發`handleTap2`,點擊 outer view 會觸發`handleTap1`。
```
<view id="outer" bindtap="handleTap1">
outer view
<view id="middle" catchtap="handleTap2">
middle view
<view id="inner" bindtap="handleTap3">
inner view
</view>
</view>
</view>
```
### 事件的捕獲階段
自基礎庫版本?[1.5.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 1.5.0 開始支持,低版本需做兼容處理。")?起,觸摸類事件支持捕獲階段。捕獲階段位于冒泡階段之前,且在捕獲階段中,事件到達節點的順序與冒泡階段恰好相反。需要在捕獲階段監聽事件時,可以采用`capture-bind`、`capture-catch`關鍵字,后者將中斷捕獲階段和取消冒泡階段。
在下面的代碼中,點擊 inner view 會先后調用`handleTap2`、`handleTap4`、`handleTap3`、`handleTap1`。
```
<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
outer view
<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
inner view
</view>
</view>
```
如果將上面代碼中的第一個`capture-bind`改為`capture-catch`,將只觸發`handleTap2`。
```
<view id="outer" bind:touchstart="handleTap1" capture-catch:touchstart="handleTap2">
outer view
<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
inner view
</view>
</view>
```
### 事件對象
如無特殊說明,當組件觸發事件時,邏輯層綁定該事件的處理函數會收到一個事件對象。
BaseEvent 基礎事件對象屬性列表:
| 屬性 | 類型 | 說明 |
| --- | --- | --- |
| [type](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#type) | String | 事件類型 |
| [timeStamp](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#timestamp) | Integer | 事件生成時的時間戳 |
| [target](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#target) | Object | 觸發事件的組件的一些屬性值集合 |
| [currentTarget](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#currenttarget) | Object | 當前組件的一些屬性值集合 |
CustomEvent 自定義事件對象屬性列表(繼承 BaseEvent):
| 屬性 | 類型 | 說明 |
| --- | --- | --- |
| [detail](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#detail) | Object | 額外的信息 |
TouchEvent 觸摸事件對象屬性列表(繼承 BaseEvent):
| 屬性 | 類型 | 說明 |
| --- | --- | --- |
| [touches](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#touches) | Array | 觸摸事件,當前停留在屏幕中的觸摸點信息的數組 |
| [changedTouches](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#changedtouches) | Array | 觸摸事件,當前變化的觸摸點信息的數組 |
特殊事件:?`<canvas/>`?中的觸摸事件不可冒泡,所以沒有 currentTarget。
### type
代表事件的類型。
### timeStamp
頁面打開到觸發事件所經過的毫秒數。
### target
觸發事件的源組件。
| 屬性 | 類型 | 說明 |
| --- | --- | --- |
| id | String | 事件源組件的id |
| tagName | String | 當前組件的類型 |
| [dataset](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#dataset) | Object | 事件源組件上由`data-`開頭的自定義屬性組成的集合 |
### currentTarget
事件綁定的當前組件。
| 屬性 | 類型 | 說明 |
| --- | --- | --- |
| id | String | 當前組件的id |
| tagName | String | 當前組件的類型 |
| [dataset](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#dataset) | Object | 當前組件上由`data-`開頭的自定義屬性組成的集合 |
說明: target 和 currentTarget 可以參考上例中,點擊 inner view 時,`handleTap3`?收到的事件對象 target 和 currentTarget 都是 inner,而?`handleTap2`?收到的事件對象 target 就是 inner,currentTarget 就是 middle。
#### dataset
在組件中可以定義數據,這些數據將會通過事件傳遞給 SERVICE。 書寫方式: 以`data-`開頭,多個單詞由連字符`-`鏈接,不能有大寫(大寫會自動轉成小寫)如`data-element-type`,最終在 event.currentTarget.dataset 中會將連字符轉成駝峰`elementType`。
示例:
```
<view data-alpha-beta="1" data-alphaBeta="2" bindtap="bindViewTap"> DataSet Test </view>
```
```
Page({
bindViewTap:function(event){
event.currentTarget.dataset.alphaBeta === 1 // - 會轉為駝峰寫法
event.currentTarget.dataset.alphabeta === 2 // 大寫會轉為小寫
}
})
```
### touches
touches 是一個數組,每個元素為一個 Touch 對象(canvas 觸摸事件中攜帶的 touches 是 CanvasTouch 數組)。 表示當前停留在屏幕上的觸摸點。
#### Touch 對象
| 屬性 | 類型 | 說明 |
| --- | --- | --- |
| identifier | Number | 觸摸點的標識符 |
| pageX, pageY | Number | 距離文檔左上角的距離,文檔的左上角為原點 ,橫向為X軸,縱向為Y軸 |
| clientX, clientY | Number | 距離頁面可顯示區域(屏幕除去導航條)左上角距離,橫向為X軸,縱向為Y軸 |
#### CanvasTouch 對象
| 屬性 | 類型 | 說明 | 特殊說明 |
| --- | --- | --- | --- |
| identifier | Number | 觸摸點的標識符 | |
| x, y | Number | 距離 Canvas 左上角的距離,Canvas 的左上角為原點 ,橫向為X軸,縱向為Y軸 | |
### changedTouches
changedTouches 數據格式同 touches。 表示有變化的觸摸點,如從無變有(touchstart),位置變化(touchmove),從有變無(touchend、touchcancel)。
### detail
自定義事件所攜帶的數據,如表單組件的提交事件會攜帶用戶的輸入,媒體的錯誤事件會攜帶錯誤信息,詳見[組件](https://developers.weixin.qq.com/miniprogram/dev/component)定義中各個事件的定義。
點擊事件的`detail`?帶有的 x, y 同 pageX, pageY 代表距離文檔左上角的距離。
# 引用
WXML 提供兩種文件引用方式`import`和`include`。
### import
`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 有作用域的概念,即只會 import 目標文件中定義的 template,而不會 import 目標文件 import 的 template。
如:C import B,B import A,在C中可以使用B定義的`template`,在B中可以使用A定義的`template`,但是C不能使用A定義的`template`。
```
<!-- A.wxml -->
<template name="A">
<text> A template </text>
</template>
```
```
<!-- B.wxml -->
<import src="a.wxml"/>
<template name="B">
<text> B template </text>
</template>
```
```
<!-- C.wxml -->
<import src="b.wxml"/>
<template is="A"/> <!-- Error! Can not use tempalte when not import A. -->
<template is="B"/>
```
### include
`include`?可以將目標文件除了?`<template/>`?`<wxs/>`?外的整個代碼引入,相當于是拷貝到?`include`?位置,如:
```
<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>
```
```
<!-- header.wxml -->
<view> header </view>
```
```
<!-- footer.wxml -->
<view> footer </view>
```
# WXSS
WXSS(WeiXin Style Sheets)是一套樣式語言,用于描述 WXML 的組件樣式。
WXSS 用來決定 WXML 的組件應該怎么顯示。
為了適應廣大的前端開發者,WXSS 具有 CSS 大部分特性。同時為了更適合開發微信小程序,WXSS 對 CSS 進行了擴充以及修改。
與 CSS 相比,WXSS 擴展的特性有:
* 尺寸單位
* 樣式導入
### 尺寸單位
* rpx(responsive pixel): 可以根據屏幕寬度進行自適應。規定屏幕寬為750rpx。如在 iPhone6 上,屏幕寬度為375px,共有750個物理像素,則750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
| 設備 | rpx換算px (屏幕寬度/750) | px換算rpx (750/屏幕寬度) |
| --- | --- | --- |
| iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx |
| iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
| iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
建議:?開發微信小程序時設計師可以用 iPhone6 作為視覺稿的標準。
注意:?在較小的屏幕上不可避免的會有一些毛刺,請在開發時盡量避免這種情況。
### 樣式導入
使用`@import`語句可以導入外聯樣式表,`@import`后跟需要導入的外聯樣式表的相對路徑,用`;`表示語句結束。
示例代碼:
```
/** common.wxss **/
.small-p {
padding:5px;
}
```
```
/** app.wxss **/
@import "common.wxss";
.middle-p {
padding:15px;
}
```
### 內聯樣式
框架組件上支持使用 style、class 屬性來控制組件的樣式。
* style:靜態的樣式統一寫到 class 中。style 接收動態的樣式,在運行時會進行解析,請盡量避免將靜態的樣式寫進 style 中,以免影響渲染速度。
```
<view style="color:{{color}};" />
```
* class:用于指定樣式規則,其屬性值是樣式規則中類選擇器名(樣式類名)的集合,樣式類名不需要帶上`.`,樣式類名之間用空格分隔。
```
<view class="normal_view" />
```
### 選擇器
目前支持的選擇器有:
| 選擇器 | 樣例 | 樣例描述 |
| --- | --- | --- |
| .class | `.intro` | 選擇所有擁有 class="intro" 的組件 |
| #id | `#firstname` | 選擇擁有 id="firstname" 的組件 |
| element | `view` | 選擇所有 view 組件 |
| element, element | `view, checkbox` | 選擇所有文檔的 view 組件和所有的 checkbox 組件 |
| ::after | `view::after` | 在 view 組件后邊插入內容 |
| ::before | `view::before` | 在 view 組件前邊插入內容 |
### 全局樣式與局部樣式
定義在 app.wxss 中的樣式為全局樣式,作用于每一個頁面。在 page 的 wxss 文件中定義的樣式為局部樣式,只作用在對應的頁面,并會覆蓋 app.wxss 中相同的選擇器。
# 基礎組件
框架為開發者提供了一系列基礎組件,開發者可以通過組合這些基礎組件進行快速開發。詳細介紹請參考[組件文檔](https://developers.weixin.qq.com/miniprogram/dev/component/index.html)。
什么是組件:
* 組件是視圖層的基本組成單元。
* 組件自帶一些功能與微信風格一致的樣式。
* 一個組件通常包括?`開始標簽`?和?`結束標簽`,`屬性`?用來修飾這個組件,`內容`?在兩個標簽之內。
```
<tagname property="value">
Content goes here ...
</tagname>
```
注意:所有組件與屬性都是小寫,以連字符`-`連接
### 屬性類型
| 類型 | 描述 | 注解 |
| --- | --- | --- |
| Boolean | 布爾值 | 組件寫上該屬性,不管是什么值都被當作?`true`;只有組件上沒有該屬性時,屬性值才為`false`。
如果屬性值為變量,變量的值會被轉換為Boolean類型 |
| Number | 數字 | `1`,?`2.5` |
| String | 字符串 | `"string"` |
| Array | 數組 | `[ 1, "string" ]` |
| Object | 對象 | `{ key: value }` |
| EventHandler | 事件處理函數名 | `"handlerName"`?是?[Page](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/page.html)?中定義的事件處理函數名 |
| Any | 任意屬性 | |
### 公共屬性
所有組件都有以下屬性:
| 屬性名 | 類型 | 描述 | 注解 |
| --- | --- | --- | --- |
| id | String | 組件的唯一標示 | 保持整個頁面唯一 |
| class | String | 組件的樣式類 | 在對應的 WXSS 中定義的樣式類 |
| style | String | 組件的內聯樣式 | 可以動態設置的內聯樣式 |
| hidden | Boolean | 組件是否顯示 | 所有組件默認顯示 |
| data-* | Any | 自定義屬性 | 組件上觸發的事件時,會發送給事件處理函數 |
| bind* / catch* | EventHandler | 組件的事件 | 詳見[事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html) |
### 特殊屬性
幾乎所有組件都有各自定義的屬性,可以對該組件的功能或樣式進行修飾,請參考各個[組件](https://developers.weixin.qq.com/miniprogram/dev/component/index.html)的定義。
# WXS
WXS(WeiXin Script)是小程序的一套腳本語言,結合?`WXML`,可以構建出頁面的結構。
### 注意
1. wxs 不依賴于運行時的基礎庫版本,可以在所有版本的小程序中運行。
2. wxs 與 javascript 是不同的語言,有自己的語法,并不和 javascript 一致。
3. wxs 的運行環境和其他 javascript 代碼是隔離的,wxs 中不能調用其他 javascript 文件中定義的函數,也不能調用小程序提供的API。
4. wxs 函數不能作為組件的事件回調。
5. 由于運行環境的差異,在 iOS 設備上小程序內的 wxs 會比 javascript 代碼快 2 ~ 20 倍。在 android 設備上二者運行效率無差異。
以下是一些使用 WXS 的簡單示例:
### 頁面渲染
```
<!--wxml-->
<wxs module="m1">
var msg = "hello world";
module.exports.message = msg;
</wxs>
<view> {{m1.message}} </view>
```
頁面輸出:
```
hello world
```
### 數據處理
```
// page.js
Page({
data: {
array: [1, 2, 3, 4, 5, 1, 2, 3, 4]
}
})
```
```
<!--wxml-->
<!-- 下面的 getMax 函數,接受一個數組,且返回數組中最大的元素的值 -->
<wxs module="m1">
var getMax = function(array) {
var max = undefined;
for (var i = 0; i < array.length; ++i) {
max = max === undefined ?
array[i] :
(max >= array[i] ? max : array[i]);
}
return max;
}
module.exports.getMax = getMax;
</wxs>
<!-- 調用 wxs 里面的 getMax 函數,參數為 page.js 里面的 array -->
<view> {{m1.getMax(array)}} </view>
```
頁面輸出:
```
5
```
# WXS 模塊
WXS 代碼可以編寫在 wxml 文件中的?`<wxs>`?標簽內,或以?`.wxs`?為后綴名的文件內。
## 模塊
每一個?`.wxs`?文件和?`<wxs>`?標簽都是一個單獨的模塊。
每個模塊都有自己獨立的作用域。即在一個模塊里面定義的變量與函數,默認為私有的,對其他模塊不可見。
一個模塊要想對外暴露其內部的私有變量與函數,只能通過?`module.exports`?實現。
## .wxs 文件
在微信開發者工具里面,右鍵可以直接創建?`.wxs`?文件,在其中直接編寫 WXS 腳本。
示例代碼:
```
// /pages/comm.wxs
var foo = "'hello world' from comm.wxs";
var bar = function(d) {
return d;
}
module.exports = {
foo: foo,
bar: bar
};
```
上述例子在?`/pages/comm.wxs`?的文件里面編寫了 WXS 代碼。該?`.wxs`?文件可以被其他的?`.wxs`?文件 或 WXML 中的?`<wxs>`?標簽引用。
### module 對象
每個?`wxs`?模塊均有一個內置的?`module`?對象。
#### 屬性
* `exports`: 通過該屬性,可以對外共享本模塊的私有變量與函數。
示例代碼:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/KYgu1Km36pZP "在開發者工具中預覽效果")
```
// /pages/tools.wxs
var foo = "'hello world' from tools.wxs";
var bar = function (d) {
return d;
}
module.exports = {
FOO: foo,
bar: bar,
};
module.exports.msg = "some msg";
```
```
<!-- page/index/index.wxml -->
<wxs src="./../tools.wxs" module="tools" />
<view> {{tools.msg}} </view>
<view> {{tools.bar(tools.FOO)}} </view>
```
頁面輸出:
```
some msg
'hello world' from tools.wxs
```
### require函數
在`.wxs`模塊中引用其他?`wxs`?文件模塊,可以使用?`require`?函數。
引用的時候,要注意如下幾點:
* 只能引用?`.wxs`?文件模塊,且必須使用相對路徑。
* `wxs`?模塊均為單例,`wxs`?模塊在第一次被引用時,會自動初始化為單例對象。多個頁面,多個地方,多次引用,使用的都是同一個?`wxs`?模塊對象。
* 如果一個?`wxs`?模塊在定義之后,一直沒有被引用,則該模塊不會被解析與運行。
示例代碼:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/E4g94Kme6rZ6 "在開發者工具中預覽效果")
```
// /pages/tools.wxs
var foo = "'hello world' from tools.wxs";
var bar = function (d) {
return d;
}
module.exports = {
FOO: foo,
bar: bar,
};
module.exports.msg = "some msg";
```
```
// /pages/logic.wxs
var tools = require("./tools.wxs");
console.log(tools.FOO);
console.log(tools.bar("logic.wxs"));
console.log(tools.msg);
```
```
<!-- /page/index/index.wxml -->
<wxs src="./../logic.wxs" module="logic" />
```
控制臺輸出:
```
'hello world' from tools.wxs
logic.wxs
some msg
```
## `<wxs>`?標簽
| 屬性名 | 類型 | 默認值 | 說明 |
| --- | --- | --- | --- |
| module | String | | 當前?`<wxs>`?標簽的模塊名。必填字段。 |
| src | String | | 引用 .wxs 文件的相對路徑。僅當本標簽為單閉合標簽或標簽的內容為空時有效。 |
### module 屬性
module 屬性是當前?`<wxs>`?標簽的模塊名。在單個 wxml 文件內,建議其值唯一。有重復模塊名則按照先后順序覆蓋(后者覆蓋前者)。不同文件之間的 wxs 模塊名不會相互覆蓋。
module 屬性值的命名必須符合下面兩個規則:
* 首字符必須是:字母(a-zA-Z),下劃線(_)
* 剩余字符可以是:字母(a-zA-Z),下劃線(_), 數字(0-9)
示例代碼:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/IzgmKKmn6tZ6 "在開發者工具中預覽效果")
```
<!--wxml-->
<wxs module="foo">
var some_msg = "hello world";
module.exports = {
msg : some_msg,
}
</wxs>
<view> {{foo.msg}} </view>
```
頁面輸出:
```
hello world
```
上面例子聲明了一個名字為?`foo`?的模塊,將?`some_msg`?變量暴露出來,供當前頁面使用。
### src 屬性
src 屬性可以用來引用其他的?`wxs`?文件模塊。
引用的時候,要注意如下幾點:
* 只能引用?`.wxs`?文件模塊,且必須使用相對路徑。
* `wxs`?模塊均為單例,`wxs`?模塊在第一次被引用時,會自動初始化為單例對象。多個頁面,多個地方,多次引用,使用的都是同一個?`wxs`?模塊對象。
* 如果一個?`wxs`?模塊在定義之后,一直沒有被引用,則該模塊不會被解析與運行。
示例代碼:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/C7gYFKmh6vZI "在開發者工具中預覽效果")
```
// /pages/index/index.js
Page({
data: {
msg: "'hello wrold' from js",
}
})
```
```
<!-- /pages/index/index.wxml -->
<wxs src="./../comm.wxs" module="some_comms"></wxs>
<!-- 也可以直接使用單標簽閉合的寫法
<wxs src="./../comm.wxs" module="some_comms" />
-->
<!-- 調用 some_comms 模塊里面的 bar 函數,且參數為 some_comms 模塊里面的 foo -->
<view> {{some_comms.bar(some_comms.foo)}} </view>
<!-- 調用 some_comms 模塊里面的 bar 函數,且參數為 page/index/index.js 里面的 msg -->
<view> {{some_comms.bar(msg)}} </view>
```
頁面輸出:
```
'hello world' from comm.wxs
'hello wrold' from js
```
上述例子在文件?`/page/index/index.wxml`?中通過?`<wxs>`?標簽引用了?`/page/comm.wxs`?模塊。
### 注意
* `<wxs>`?模塊只能在定義模塊的 WXML 文件中被訪問到。使用?`<include>`?或?`<import>`?時,`<wxs>`?模塊不會被引入到對應的 WXML 文件中。
* `<template>`?標簽中,只能使用定義該?`<template>`?的 WXML 文件中定義的?`<wxs>`?模塊。
# 變量
## 概念
* WXS 中的變量均為值的引用。
* 沒有聲明的變量直接賦值使用,會被定義為全局變量。
* 如果只聲明變量而不賦值,則默認值為?`undefined`。
* var表現與javascript一致,會有變量提升。
```
var foo = 1;
var bar = "hello world";
var i; // i === undefined
```
上面代碼,分別聲明了?`foo`、?`bar`、?`i`?三個變量。然后,`foo`?賦值為數值?`1`?,`bar`?賦值為字符串?`"hello world"`。
## 變量名
變量命名必須符合下面兩個規則:
* 首字符必須是:字母(a-zA-Z),下劃線(_)
* 剩余字符可以是:字母(a-zA-Z),下劃線(_), 數字(0-9)
## 保留標識符
以下標識符不能作為變量名:
```
delete
void
typeof
null
undefined
NaN
Infinity
var
if
else
true
false
require
this
function
arguments
return
for
while
do
break
continue
switch
case
default
```
# 注釋
WXS 主要有 3 種注釋的方法。
示例代碼:
```
<!-- wxml -->
<wxs module="sample">
// 方法一:單行注釋
/*
方法二:多行注釋
*/
/*
方法三:結尾注釋。即從 /* 開始往后的所有 WXS 代碼均被注釋
var a = 1;
var b = 2;
var c = "fake";
</wxs>
```
上述例子中,所有 WXS 代碼均被注釋掉了。
> 方法三 和 方法二 的唯一區別是,沒有?`*/`?結束符。
>
# 運算符
## 基本運算符
示例代碼:
```
var a = 10, b = 20;
// 加法運算
console.log(30 === a + b);
// 減法運算
console.log(-10 === a - b);
// 乘法運算
console.log(200 === a * b);
// 除法運算
console.log(0.5 === a / b);
// 取余運算
console.log(10 === a % b);
```
* 加法運算(`+`)也可以用作字符串的拼接。
```
var a = '.w' , b = 'xs';
// 字符串拼接
console.log('.wxs' === a + b);
```
## 一元運算符
示例代碼:
```
var a = 10, b = 20;
// 自增運算
console.log(10 === a++);
console.log(12 === ++a);
// 自減運算
console.log(12 === a--);
console.log(10 === --a);
// 正值運算
console.log(10 === +a);
// 負值運算
console.log(0-10 === -a);
// 否運算
console.log(-11 === ~a);
// 取反運算
console.log(false === !a);
// delete 運算
console.log(true === delete a.fake);
// void 運算
console.log(undefined === void a);
// typeof 運算
console.log("number" === typeof a);
```
## 位運算符
示例代碼:
```
var a = 10, b = 20;
// 左移運算
console.log(80 === (a << 3));
// 無符號右移運算
console.log(2 === (a >> 2));
// 帶符號右移運算
console.log(2 === (a >>> 2));
// 與運算
console.log(2 === (a & 3));
// 異或運算
console.log(9 === (a ^ 3));
// 或運算
console.log(11 === (a | 3));
```
## 比較運算符
示例代碼:
```
var a = 10, b = 20;
// 小于
console.log(true === (a < b));
// 大于
console.log(false === (a > b));
// 小于等于
console.log(true === (a <= b));
// 大于等于
console.log(false === (a >= b));
```
## 等值運算符
示例代碼:
```
var a = 10, b = 20;
// 等號
console.log(false === (a == b));
// 非等號
console.log(true === (a != b));
// 全等號
console.log(false === (a === b));
// 非全等號
console.log(true === (a !== b));
```
## 賦值運算符
示例代碼:
```
var a = 10;
a = 10; a *= 10;
console.log(100 === a);
a = 10; a /= 5;
console.log(2 === a);
a = 10; a %= 7;
console.log(3 === a);
a = 10; a += 5;
console.log(15 === a);
a = 10; a -= 11;
console.log(-1 === a);
a = 10; a <<= 10;
console.log(10240 === a);
a = 10; a >>= 2;
console.log(2 === a);
a = 10; a >>>= 2;
console.log(2 === a);
a = 10; a &= 3;
console.log(2 === a);
a = 10; a ^= 3;
console.log(9 === a);
a = 10; a |= 3;
console.log(11 === a);
```
## 二元邏輯運算符
示例代碼:
```
var a = 10, b = 20;
// 邏輯與
console.log(20 === (a && b));
// 邏輯或
console.log(10 === (a || b));
```
## 其他運算符
示例代碼:
```
var a = 10, b = 20;
//條件運算符
console.log(20 === (a >= 10 ? a + 10 : b + 10));
//逗號運算符
console.log(20 === (a, b));
```
## 運算符優先級
| 優先級 | 運算符 | 說明 | 結合性 |
| --- | --- | --- | --- |
| 20 | `(`?...?`)` | 括號 | n/a |
| 19 | ...?`.`?... | 成員訪問 | 從左到右 |
| | ...?`[`?...?`]` | 成員訪問 | 從左到右 |
| | ...?`(`?...?`)` | 函數調用 | 從左到右 |
| 17 | ...?`++` | 后置遞增 | n/a |
| | ...?`--` | 后置遞減 | n/a |
| 16 | `!`?... | 邏輯非 | 從右到左 |
| | `~`?... | 按位非 | 從右到左 |
| | `+`?... | 一元加法 | 從右到左 |
| | `-`?... | 一元減法 | 從右到左 |
| | `++`?... | 前置遞增 | 從右到左 |
| | `--`?... | 前置遞減 | 從右到左 |
| | `typeof`?... | typeof | 從右到左 |
| | `void`?... | void | 從右到左 |
| | `delete`?... | delete | 從右到左 |
| 14 | ...?`*`?... | 乘法 | 從左到右 |
| | ...?`/`?... | 除法 | 從左到右 |
| | ...?`%`?... | 取模 | 從左到右 |
| 13 | ...?`+`?... | 加法 | 從左到右 |
| | ...?`-`?... | 減法 | 從左到右 |
| 12 | ...?`<<`?... | 按位左移 | 從左到右 |
| | ...?`>>`?... | 按位右移 | 從左到右 |
| | ...?`>>>`?... | 無符號右移 | 從左到右 |
| 11 | ...?`<`?... | 小于 | 從左到右 |
| | ...?`<=`?... | 小于等于 | 從左到右 |
| | ...?`>`?... | 大于 | 從左到右 |
| | ...?`>=`?... | 大于等于 | 從左到右 |
| 10 | ...?`==`?... | 等號 | 從左到右 |
| | ...?`!=`?... | 非等號 | 從左到右 |
| | ...?`===`?... | 全等號 | 從左到右 |
| | ...?`!==`?... | 非全等號 | 從左到右 |
| 9 | ...?`&`?... | 按位與 | 從左到右 |
| 8 | ...?`^`?... | 按位異或 | 從左到右 |
| 7 | ...?`|`?... | 按位或 | 從左到右 |
| 6 | ...?`&&`?... | 邏輯與 | 從左到右 |
| 5 | ...?`||`?... | 邏輯或 | 從左到右 |
| 4 | ...?`?`?...?`:`?... | 條件運算符 | 從右到左 |
| 3 | ...?`=`?... | 賦值 | 從右到左 |
| | ...?`+=`?... | 賦值 | 從右到左 |
| | ...?`-=`?... | 賦值 | 從右到左 |
| | ...?`*=`?... | 賦值 | 從右到左 |
| | ...?`/=`?... | 賦值 | 從右到左 |
| | ...?`%=`?... | 賦值 | 從右到左 |
| | ...?`<<=`?... | 賦值 | 從右到左 |
| | ...?`>>=`?... | 賦值 | 從右到左 |
| | ...?`>>>=`?... | 賦值 | 從右到左 |
| | ...?`&=`?... | 賦值 | 從右到左 |
| | ...?`^=`?... | 賦值 | 從右到左 |
| | ...?`|=`?... | 賦值 | 從右到左 |
| 0 | ...?`,`?... | 逗號 | 從左到右 |# 語句
## if 語句
在 WXS 中,可以使用以下格式的?`if`?語句 :
* `if (expression) statement`?: 當?`expression`?為 truthy 時,執行?`statement`。
* `if (expression) statement1 else statement2`?: 當?`expression`?為 truthy 時,執行?`statement1`。 否則,執行?`statement2`
* `if ... else if ... else statementN`?通過該句型,可以在?`statement1`?~?`statementN`?之間選其中一個執行。
示例語法:
```
// if ...
if (表達式) 語句;
if (表達式)
語句;
if (表達式) {
代碼塊;
}
// if ... else
if (表達式) 語句;
else 語句;
if (表達式)
語句;
else
語句;
if (表達式) {
代碼塊;
} else {
代碼塊;
}
// if ... else if ... else ...
if (表達式) {
代碼塊;
} else if (表達式) {
代碼塊;
} else if (表達式) {
代碼塊;
} else {
代碼塊;
}
```
## switch 語句
示例語法:
```
switch (表達式) {
case 變量:
語句;
case 數字:
語句;
break;
case 字符串:
語句;
default:
語句;
}
```
* `default`?分支可以省略不寫。
* `case`?關鍵詞后面只能使用:`變量`,`數字`,`字符串`。
示例代碼:
```
var exp = 10;
switch ( exp ) {
case "10":
console.log("string 10");
break;
case 10:
console.log("number 10");
break;
case exp:
console.log("var exp");
break;
default:
console.log("default");
}
```
輸出:
```
number 10
```
## for 語句
示例語法:
```
for (語句; 語句; 語句)
語句;
for (語句; 語句; 語句) {
代碼塊;
}
```
* 支持使用?`break`,`continue`?關鍵詞。
示例代碼:
```
for (var i = 0; i < 3; ++i) {
console.log(i);
if( i >= 1) break;
}
```
輸出:
```
0
1
```
## while 語句
示例語法:
```
while (表達式)
語句;
while (表達式){
代碼塊;
}
do {
代碼塊;
} while (表達式)
```
* 當`表達式`為 true 時,循環執行`語句`或`代碼塊`。
* 支持使用?`break`,`continue`?關鍵詞。
# 數據類型
WXS 語言目前共有以下幾種數據類型:
* `number`?: 數值
* `string`?:字符串
* `boolean`:布爾值
* `object`:對象
* `function`:函數
* `array`?: 數組
* `date`:日期
* `regexp`:正則
## number
### 語法
number 包括兩種數值:整數,小數。
```
var a = 10;
var PI = 3.141592653589793;
```
### 屬性
* `constructor`:返回字符串?`"Number"`。
### 方法
* `toString`
* `toLocaleString`
* `valueOf`
* `toFixed`
* `toExponential`
* `toPrecision`
> 以上方法的具體使用請參考?`ES5`?標準。
## string
### 語法
string 有兩種寫法:
```
'hello world';
"hello world";
```
### 屬性
* `constructor`:返回字符串?`"String"`。
* `length`
> 除constructor外屬性的具體含義請參考?`ES5`?標準。
### 方法
* `toString`
* `valueOf`
* `charAt`
* `charCodeAt`
* `concat`
* `indexOf`
* `lastIndexOf`
* `localeCompare`
* `match`
* `replace`
* `search`
* `slice`
* `split`
* `substring`
* `toLowerCase`
* `toLocaleLowerCase`
* `toUpperCase`
* `toLocaleUpperCase`
* `trim`
> 以上方法的具體使用請參考?`ES5`?標準。
## boolean
### 語法
布爾值只有兩個特定的值:`true`?和?`false`。
### 屬性
* `constructor`:返回字符串?`"Boolean"`。
### 方法
* `toString`
* `valueOf`
> 以上方法的具體使用請參考?`ES5`?標準。
## object
### 語法
object 是一種無序的鍵值對。使用方法如下所示:
```
var o = {} //生成一個新的空對象
//生成一個新的非空對象
o = {
'string' : 1, //object 的 key 可以是字符串
const_var : 2, //object 的 key 也可以是符合變量定義規則的標識符
func : {}, //object 的 value 可以是任何類型
};
//對象屬性的讀操作
console.log(1 === o['string']);
console.log(2 === o.const_var);
//對象屬性的寫操作
o['string']++;
o['string'] += 10;
o.const_var++;
o.const_var += 10;
//對象屬性的讀操作
console.log(12 === o['string']);
console.log(13 === o.const_var);
```
### 屬性
* `constructor`:返回字符串?`"Object"`。
```
console.log("Object" === {k:"1",v:"2"}.constructor)
```
### 方法
* `toString`:返回字符串?`"[object Object]"`。
## function
### 語法
function 支持以下的定義方式:
```
//方法 1
function a (x) {
return x;
}
//方法 2
var b = function (x) {
return x;
}
```
function 同時也支持以下的語法(匿名函數,閉包等):
```
var a = function (x) {
return function () { return x;}
}
var b = a(100);
console.log( 100 === b() );
```
### arguments
function 里面可以使用?`arguments`?關鍵詞。該關鍵詞目前只支持以下的屬性:
* `length`: 傳遞給函數的參數個數。
* `[index]`: 通過?`index`?下標可以遍歷傳遞給函數的每個參數。
示例代碼:
```
var a = function(){
console.log(3 === arguments.length);
console.log(100 === arguments[0]);
console.log(200 === arguments[1]);
console.log(300 === arguments[2]);
};
a(100,200,300);
```
### 屬性
* `constructor`:返回字符串?`"Function"`。
* `length`:返回函數的形參個數。
### 方法
* `toString`:返回字符串?`"[function Function]"`。
示例代碼:
```
var func = function (a,b,c) { }
console.log("Function" === func.constructor);
console.log(3 === func.length);
console.log("[function Function]" === func.toString());
```
## array
### 語法
array 支持以下的定義方式:
```
var a = []; //生成一個新的空數組
a = [1,"2",{},function(){}]; //生成一個新的非空數組,數組元素可以是任何類型
```
### 屬性
* `constructor`:返回字符串?`"Array"`。
* `length`
> 除constructor外屬性的具體含義請參考?`ES5`?標準。
### 方法
* `toString`
* `concat`
* `join`
* `pop`
* `push`
* `reverse`
* `shift`
* `slice`
* `sort`
* `splice`
* `unshift`
* `indexOf`
* `lastIndexOf`
* `every`
* `some`
* `forEach`
* `map`
* `filter`
* `reduce`
* `reduceRight`
> 以上方法的具體使用請參考?`ES5`?標準。
## date
### 語法
生成 date 對象需要使用?`getDate`函數, 返回一個當前時間的對象。
```
getDate()
getDate(milliseconds)
getDate(datestring)
getDate(year, month[, date[, hours[, minutes[, seconds[, milliseconds]]]]])
```
* 參數
* `milliseconds`: 從1970年1月1日00:00:00 UTC開始計算的毫秒數
* `datestring`: 日期字符串,其格式為:"month day, year hours:minutes:seconds"
示例代碼:
```
var date = getDate(); //返回當前時間對象
date = getDate(1500000000000);
// Fri Jul 14 2017 10:40:00 GMT+0800 (中國標準時間)
date = getDate('2017-7-14');
// Fri Jul 14 2017 00:00:00 GMT+0800 (中國標準時間)
date = getDate(2017, 6, 14, 10, 40, 0, 0);
// Fri Jul 14 2017 10:40:00 GMT+0800 (中國標準時間)
```
### 屬性
* `constructor`:返回字符串 “Date”。
### 方法
* `toString`
* `toDateString`
* `toTimeString`
* `toLocaleString`
* `toLocaleDateString`
* `toLocaleTimeString`
* `valueOf`
* `getTime`
* `getFullYear`
* `getUTCFullYear`
* `getMonth`
* `getUTCMonth`
* `getDate`
* `getUTCDate`
* `getDay`
* `getUTCDay`
* `getHours`
* `getUTCHours`
* `getMinutes`
* `getUTCMinutes`
* `getSeconds`
* `getUTCSeconds`
* `getMilliseconds`
* `getUTCMilliseconds`
* `getTimezoneOffset`
* `setTime`
* `setMilliseconds`
* `setUTCMilliseconds`
* `setSeconds`
* `setUTCSeconds`
* `setMinutes`
* `setUTCMinutes`
* `setHours`
* `setUTCHours`
* `setDate`
* `setUTCDate`
* `setMonth`
* `setUTCMonth`
* `setFullYear`
* `setUTCFullYear`
* `toUTCString`
* `toISOString`
* `toJSON`
> 以上方法的具體使用請參考?`ES5`?標準。
## regexp
### 語法
生成 regexp 對象需要使用?`getRegExp`函數。
```
getRegExp(pattern[, flags])
```
* 參數:
* `pattern`: 正則表達式的內容。
* `flags`:修飾符。該字段只能包含以下字符:
* `g`: global
* `i`: ignoreCase
* `m`: multiline。
示例代碼:
```
var a = getRegExp("x", "img");
console.log("x" === a.source);
console.log(true === a.global);
console.log(true === a.ignoreCase);
console.log(true === a.multiline);
```
### 屬性
* `constructor`:返回字符串?`"RegExp"`。
* `source`
* `global`
* `ignoreCase`
* `multiline`
* `lastIndex`
> 除constructor外屬性的具體含義請參考?`ES5`?標準。
### 方法
* `exec`
* `test`
* `toString`
> 以上方法的具體使用請參考?`ES5`?標準。
## 數據類型判斷
### `constructor`?屬性
數據類型的判斷可以使用?`constructor`?屬性。
示例代碼:
```
var number = 10;
console.log( "Number" === number.constructor );
var string = "str";
console.log( "String" === string.constructor );
var boolean = true;
console.log( "Boolean" === boolean.constructor );
var object = {};
console.log( "Object" === object.constructor );
var func = function(){};
console.log( "Function" === func.constructor );
var array = [];
console.log( "Array" === array.constructor );
var date = getDate();
console.log( "Date" === date.constructor );
var regexp = getRegExp();
console.log( "RegExp" === regexp.constructor );
```
### `typeof`
使用?`typeof`?也可以區分部分數據類型。
示例代碼:
```
var number = 10;
var boolean = true;
var object = {};
var func = function(){};
var array = [];
var date = getDate();
var regexp = getRegExp();
console.log( 'number' === typeof number );
console.log( 'boolean' === typeof boolean );
console.log( 'object' === typeof object );
console.log( 'function' === typeof func );
console.log( 'object' === typeof array );
console.log( 'object' === typeof date );
console.log( 'object' === typeof regexp );
console.log( 'undefined' === typeof undefined );
console.log( 'object' === typeof null );
```
# 基礎類庫
## console
`console.log`?方法用于在 console 窗口輸出信息。它可以接受多個參數,將它們的結果連接起來輸出。
## Math
### 屬性
* `E`
* `LN10`
* `LN2`
* `LOG2E`
* `LOG10E`
* `PI`
* `SQRT1_2`
* `SQRT2`
> 以上屬性的具體使用請參考?`ES5`?標準。
### 方法
* `abs`
* `acos`
* `asin`
* `atan`
* `atan2`
* `ceil`
* `cos`
* `exp`
* `floor`
* `log`
* `max`
* `min`
* `pow`
* `random`
* `round`
* `sin`
* `sqrt`
* `tan`
> 以上方法的具體使用請參考?`ES5`?標準。
## JSON
### 方法
* `stringify(object)`: 將?`object`?對象轉換為?`JSON`?字符串,并返回該字符串。
* `parse(string)`: 將?`JSON`?字符串轉化成對象,并返回該對象。
示例代碼:
```
console.log(undefined === JSON.stringify());
console.log(undefined === JSON.stringify(undefined));
console.log("null"===JSON.stringify(null));
console.log("111"===JSON.stringify(111));
console.log('"111"'===JSON.stringify("111"));
console.log("true"===JSON.stringify(true));
console.log(undefined===JSON.stringify(function(){}));
console.log(undefined===JSON.parse(JSON.stringify()));
console.log(undefined===JSON.parse(JSON.stringify(undefined)));
console.log(null===JSON.parse(JSON.stringify(null)));
console.log(111===JSON.parse(JSON.stringify(111)));
console.log("111"===JSON.parse(JSON.stringify("111")));
console.log(true===JSON.parse(JSON.stringify(true)));
console.log(undefined===JSON.parse(JSON.stringify(function(){})));
```
## Number
### 屬性
* `MAX_VALUE`
* `MIN_VALUE`
* `NEGATIVE_INFINITY`
* `POSITIVE_INFINITY`
> 以上屬性的具體使用請參考?`ES5`?標準。
## Date
### 屬性
* `parse`
* `UTC`
* `now`
> 以上屬性的具體使用請參考?`ES5`?標準。
## Global
### 屬性
* `NaN`
* `Infinity`
* `undefined`
> 以上屬性的具體使用請參考?`ES5`?標準。
### 方法
* `parseInt`
* `parseFloat`
* `isNaN`
* `isFinite`
* `decodeURI`
* `decodeURIComponent`
* `encodeURI`
* `encodeURIComponent`
> 以上方法的具體使用請參考?`ES5`?標準。
>
# 獲取界面上的節點信息
## WXML節點信息
[節點信息查詢 API](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createSelectorQuery.html)?可以用于獲取節點屬性、樣式、在界面上的位置等信息。
最常見的用法是使用這個接口來查詢某個節點的當前位置,以及界面的滾動位置。
示例代碼:
```
const query = wx.createSelectorQuery()
query.select('#the-id').boundingClientRect(function(res){
res.top // #the-id 節點的上邊界坐標(相對于顯示區域)
})
query.selectViewport().scrollOffset(function(res){
res.scrollTop // 顯示區域的豎直滾動位置
})
query.exec()
```
上述示例中,?`#the-id`?是一個節點選擇器,與 CSS 的選擇器相近但略有區別,請參見?[SelectorQuery.select](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.select.html)?的相關說明。
在自定義組件中,推薦使用?`this.createSelectorQuery`?來代替?`wx.createSelectorQuery`?,這樣會將選擇器選取范圍定在這個自定義組件內。
## WXML節點布局相交狀態
[節點布局相交狀態 API](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createIntersectionObserver.html)?可用于監聽兩個或多個組件節點在布局位置上的相交狀態。這一組API常常可以用于推斷某些節點是否可以被用戶看見、有多大比例可以被用戶看見。
這一組API涉及的主要概念如下。
* 參照節點:監聽的參照節點,取它的布局區域作為參照區域。如果有多個參照節點,則會取它們布局區域的?交集?作為參照區域。頁面顯示區域也可作為參照區域之一。
* 目標節點:監聽的目標,默認只能是一個節點(使用?`selectAll`?選項時,可以同時監聽多個節點)。
* 相交區域:目標節點的布局區域與參照區域的相交區域。
* 相交比例:相交區域占參照區域的比例。
* 閾值:相交比例如果達到閾值,則會觸發監聽器的回調函數。閾值可以有多個。
以下示例代碼可以在目標節點(用選擇器?`.target-class`?指定)每次進入或離開頁面顯示區域時,觸發回調函數。
示例代碼:
```
Page({
onLoad: function(){
wx.createIntersectionObserver().relativeToViewport().observe('.target-class', (res) => {
res.id // 目標節點 id
res.dataset // 目標節點 dataset
res.intersectionRatio // 相交區域占目標節點的布局區域的比例
res.intersectionRect // 相交區域
res.intersectionRect.left // 相交區域的左邊界坐標
res.intersectionRect.top // 相交區域的上邊界坐標
res.intersectionRect.width // 相交區域的寬度
res.intersectionRect.height // 相交區域的高度
})
}
})
```
以下示例代碼可以在目標節點(用選擇器?`.target-class`?指定)與參照節點(用選擇器?`.relative-class`?指定)在頁面顯示區域內相交或相離,且相交或相離程度達到目標節點布局區域的20%和50%時,觸發回調函數。
示例代碼:
```
Page({
onLoad: function(){
wx.createIntersectionObserver(this, {
thresholds: [0.2, 0.5]
}).relativeTo('.relative-class').relativeToViewport().observe('.target-class', (res) => {
res.intersectionRatio // 相交區域占目標節點的布局區域的比例
res.intersectionRect // 相交區域
res.intersectionRect.left // 相交區域的左邊界坐標
res.intersectionRect.top // 相交區域的上邊界坐標
res.intersectionRect.width // 相交區域的寬度
res.intersectionRect.height // 相交區域的高度
})
}
})
```
在自定義組件中,推薦使用?`this.createIntersectionObserver`?來代替?`wx.createIntersectionObserver`?,這樣會將選擇器選取范圍定在這個自定義組件內。
# 響應顯示區域變化
## 顯示區域尺寸
顯示區域指小程序界面中可以自由布局展示的區域。在默認情況下,小程序顯示區域的尺寸自頁面初始化起就不會發生變化。但以下兩種方式都可以改變這一默認行為。
### 在手機上啟用屏幕旋轉支持
從小程序基礎庫版本?[2.4.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.4.0 開始支持,低版本需做兼容處理。")?開始,小程序在手機上支持屏幕旋轉。使小程序中的頁面支持屏幕旋轉的方法是:在?`app.json`?的?`window`?段中設置?`"pageOrientation": "auto"`?,或在頁面 json 文件中配置?`"pageOrientation": "auto"`?。
以下是在單個頁面 json 文件中啟用屏幕旋轉的示例。
代碼示例:
```
{
"pageOrientation": "auto"
}
```
如果頁面添加了上述聲明,則在屏幕旋轉時,這個頁面將隨之旋轉,顯示區域尺寸也會隨著屏幕旋轉而變化。
### 在 iPad 上啟用屏幕旋轉支持
從小程序基礎庫版本?[2.3.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.3.0 開始支持,低版本需做兼容處理。")?開始,在 iPad 上運行的小程序可以支持屏幕旋轉。使小程序支持 iPad 屏幕旋轉的方法是:在?`app.json`?中添加?`"resizable": true`?。
代碼示例:
```
{
"resizable": true
}
```
如果小程序添加了上述聲明,則在屏幕旋轉時,小程序將隨之旋轉,顯示區域尺寸也會隨著屏幕旋轉而變化。注意:在 iPad 上不能單獨配置某個頁面是否支持屏幕旋轉。
## Media Query
有時,對于不同尺寸的顯示區域,頁面的布局會有所差異。此時可以使用 media query 來解決大多數問題。
代碼示例:
```
.my-class {
width: 40px;
}
@media (min-width: 480px) {
/* 僅在 480px 或更寬的屏幕上生效的樣式規則 */
.my-class {
width: 200px;
}
}
```
## 屏幕旋轉事件
有時,僅僅使用 media query 無法控制一些精細的布局變化。此時可以使用 js 作為輔助。
在 js 中讀取頁面的顯示區域尺寸,可以使用?[selectorQuery.selectViewport](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.selectViewport.html)?。
頁面尺寸發生改變的事件,可以使用?`wx.onWindowResize`?頁面的?`onResize`?來監聽。對于自定義組件,可以使用 resize 生命周期來監聽。回調函數中將返回顯示區域的尺寸信息。
代碼示例:
```
wx.onWindowResize(function(res) {
res.size.windowWidth // 新的顯示區域寬度
res.size.windowHeight // 新的顯示區域高度
})
```
```
Page({
onResize(res) {
res.size.windowWidth // 新的顯示區域寬度
res.size.windowHeight // 新的顯示區域高度
}
})
```
```
Component({
pageLifetimes: {
resize(res) {
res.size.windowWidth // 新的顯示區域寬度
res.size.windowHeight // 新的顯示區域高度
}
}
})
```
Bug & tips:
* Bug: Android 微信版本 6.7.3 中,?`live-pusher`?組件在屏幕旋轉時方向異常。
# 組件模板和樣式
類似于頁面,自定義組件擁有自己的?`wxml`?模板和?`wxss`?樣式。
### 組件模板
組件模板的寫法與頁面模板相同。組件模板與組件數據結合后生成的節點樹,將被插入到組件的引用位置上。
在組件模板中可以提供一個?`<slot>`?節點,用于承載組件引用時提供的子節點。
代碼示例:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/1udXLnmi6KY2 "在開發者工具中預覽效果")
```
<!-- 組件模板 -->
<view class="wrapper">
<view>這里是組件的內部節點</view>
<slot></slot>
</view>
```
```
<!-- 引用組件的頁面模板 -->
<view>
<component-tag-name>
<!-- 這部分內容將被放置在組件 <slot> 的位置上 -->
<view>這里是插入到組件slot中的內容</view>
</component-tag-name>
</view>
```
注意,在模板中引用到的自定義組件及其對應的節點名需要在?`json`?文件中顯式定義,否則會被當作一個無意義的節點。除此以外,節點名也可以被聲明為[抽象節點](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/generics.html)。
### 模板數據綁定
與普通的 WXML 模板類似,可以使用數據綁定,這樣就可以向子組件的屬性傳遞動態數據。
代碼示例:
```
<!-- 引用組件的頁面模板 -->
<view>
<component-tag-name prop-a="{{dataFieldA}}" prop-b="{{dataFieldB}}">
<!-- 這部分內容將被放置在組件 <slot> 的位置上 -->
<view>這里是插入到組件slot中的內容</view>
</component-tag-name>
</view>
```
在以上例子中,組件的屬性?`propA`?和?`propB`?將收到頁面傳遞的數據。頁面可以通過?`setData`?來改變綁定的數據字段。
注意:這樣的數據綁定只能傳遞 JSON 兼容數據。自基礎庫版本?[2.0.9](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.0.9 開始支持,低版本需做兼容處理。")?開始,還可以在數據中包含函數(但這些函數不能在 WXML 中直接調用,只能傳遞給子組件)。
### 組件wxml的slot
在組件的wxml中可以包含?`slot`?節點,用于承載組件使用者提供的wxml結構。
默認情況下,一個組件的wxml中只能有一個slot。需要使用多slot時,可以在組件js中聲明啟用。
```
Component({
options: {
multipleSlots: true // 在組件定義時的選項中啟用多slot支持
},
properties: { /* ... */ },
methods: { /* ... */ }
})
```
此時,可以在這個組件的wxml中使用多個slot,以不同的?`name`?來區分。
```
<!-- 組件模板 -->
<view class="wrapper">
<slot name="before"></slot>
<view>這里是組件的內部細節</view>
<slot name="after"></slot>
</view>
```
使用時,用?`slot`?屬性來將節點插入到不同的slot上。
```
<!-- 引用組件的頁面模板 -->
<view>
<component-tag-name>
<!-- 這部分內容將被放置在組件 <slot name="before"> 的位置上 -->
<view slot="before">這里是插入到組件slot name="before"中的內容</view>
<!-- 這部分內容將被放置在組件 <slot name="after"> 的位置上 -->
<view slot="after">這里是插入到組件slot name="after"中的內容</view>
</component-tag-name>
</view>
```
### 組件樣式
組件對應?`wxss`?文件的樣式,只對組件wxml內的節點生效。編寫組件樣式時,需要注意以下幾點:
* 組件和引用組件的頁面不能使用id選擇器(`#a`)、屬性選擇器(`[a]`)和標簽名選擇器,請改用class選擇器。
* 組件和引用組件的頁面中使用后代選擇器(`.a .b`)在一些極端情況下會有非預期的表現,如遇,請避免使用。
* 子元素選擇器(`.a>.b`)只能用于?`view`?組件與其子節點之間,用于其他組件可能導致非預期的情況。
* 繼承樣式,如?`font`?、?`color`?,會從組件外繼承到組件內。
* 除繼承樣式外,?`app.wxss`?中的樣式、組件所在頁面的的樣式對自定義組件無效。
```
#a { } /* 在組件中不能使用 */
[a] { } /* 在組件中不能使用 */
button { } /* 在組件中不能使用 */
.a > .b { } /* 除非 .a 是 view 組件節點,否則不一定會生效 */
```
除此以外,組件可以指定它所在節點的默認樣式,使用?`:host`?選擇器(需要包含基礎庫?[1.7.2](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 1.7.2 開始支持,低版本需做兼容處理。")?或更高版本的開發者工具支持)。
代碼示例:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/jAgvwKm16bZD "在開發者工具中預覽效果")
```
/* 組件 custom-component.wxss */
:host {
color: yellow;
}
```
```
<!-- 頁面的 WXML -->
<custom-component>這段文本是黃色的</custom-component>
```
### 外部樣式類
有時,組件希望接受外部傳入的樣式類(類似于?`view`?組件的?`hover-class`?屬性)。此時可以在?`Component`?中用?`externalClasses`?定義段定義若干個外部樣式類。這個特性從小程序基礎庫版本?[1.9.90](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 1.9.90 開始支持,低版本需做兼容處理。")?開始支持。
注意:在同一個節點上使用普通樣式類和外部樣式類時,兩個類的優先級是未定義的,因此最好避免這種情況。
代碼示例:
```
/* 組件 custom-component.js */
Component({
externalClasses: ['my-class']
})
```
```
<!-- 組件 custom-component.wxml -->
<custom-component class="my-class">這段文本的顏色由組件外的 class 決定</custom-component>
```
這樣,組件的使用者可以指定這個樣式類對應的 class ,就像使用普通屬性一樣。
代碼示例:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/rbgNNKmE6bZK "在開發者工具中預覽效果")
```
<!-- 頁面的 WXML -->
<custom-component my-class="red-text" />
```
```
.red-text {
color: red;
}
```
### 全局樣式類
使用外部樣式類可以讓組件使用指定的組件外樣式類,如果希望組件外樣式類能夠完全影響組件內部,可以將組件構造器中的`options.addGlobalClass`字段置為`true`。這個特性從小程序基礎庫版本?[2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.2.3 開始支持,低版本需做兼容處理。")?開始支持。
> 當開放了全局樣式類,存在外部樣式污染組件樣式的風險,請謹慎選擇。
代碼示例:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/yNPLQvm87b1F "在開發者工具中預覽效果")
```
/* 組件 custom-component.js */
Component({
options: {
addGlobalClass: true,
}
})
```
```
<!-- 組件 custom-component.wxml -->
<text class="red-text">這段文本的顏色由組件外的 class 決定</text>
```
```
/* 組件外的樣式定義 */
.red-text {
color: red;
}
```
# Component構造器
## 定義段與示例方法
Component構造器可用于定義組件,調用Component構造器時可以指定組件的屬性、數據、方法等。
| 定義段 | 類型 | 是否必填 | 描述 | 最低版本 |
| --- | --- | --- | --- | --- |
| properties | Object Map | 否 | 組件的對外屬性,是屬性名到屬性設置的映射表,屬性設置中可包含三個字段,?`type`?表示屬性類型、?`value`?表示屬性初始值、?`observer`?表示屬性值被更改時的響應函數 | |
| data | Object | 否 | 組件的內部數據,和?`properties`?一同用于組件的模板渲染 | |
| methods | Object | 否 | 組件的方法,包括事件響應函數和任意的自定義方法,關于事件響應函數的使用,參見?[組件事件](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html) | |
| behaviors | String Array | 否 | 類似于mixins和traits的組件間代碼復用機制,參見?[behaviors](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html) | |
| created | Function | 否 | 組件生命周期函數,在組件實例進入頁面節點樹時執行,注意此時不能調用?`setData` | |
| attached | Function | 否 | 組件生命周期函數,在組件實例進入頁面節點樹時執行 | |
| ready | Function | 否 | 組件生命周期函數,在組件布局完成后執行,此時可以獲取節點信息(使用?[SelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html)?) | |
| moved | Function | 否 | 組件生命周期函數,在組件實例被移動到節點樹另一個位置時執行 | |
| detached | Function | 否 | 組件生命周期函數,在組件實例被從頁面節點樹移除時執行 | |
| relations | Object | 否 | 組件間關系定義,參見?[組件間關系](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/relations.html) | |
| externalClasses | String Array | 否 | 組件接受的外部樣式類,參見?[外部樣式類](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html) | |
| options | Object Map | 否 | 一些選項(文檔中介紹相關特性時會涉及具體的選項設置,這里暫不列舉) | |
| lifetimes | Object | 否 | 組件生命周期聲明對象,組件的生命周期:`created`、`attached`、`ready`、`moved`、`detached`將收歸到`lifetimes`字段內進行聲明,原有聲明方式仍舊有效,如同時存在兩種聲明方式,則`lifetimes`字段內聲明方式優先級最高 | [2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.2.3 開始支持,低版本需做兼容處理。") |
| pageLifetimes | Object | 否 | 組件所在頁面的生命周期聲明對象,支持頁面的?`show`?、?`hide`?、?`resize`?生命周期 | [2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.2.3 開始支持,低版本需做兼容處理。")?;?`resize`為?[2.4.0](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.4.0 開始支持,低版本需做兼容處理。") |
| definitionFilter | Function | 否 | 定義段過濾器,用于自定義組件擴展,參見?[自定義組件擴展](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/extend.html) | [2.2.3](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.2.3 開始支持,低版本需做兼容處理。") |
生成的組件實例可以在組件的方法、生命周期函數和屬性?`observer`?中通過?`this`?訪問。組件包含一些通用屬性和方法。
| 屬性名 | 類型 | 描述 |
| --- | --- | --- |
| is | String | 組件的文件路徑 |
| id | String | 節點id |
| dataset | String | 節點dataset |
| data | Object | 組件數據,包括內部數據和屬性值 |
| properties | Object | 組件數據,包括內部數據和屬性值(與?`data`?一致) |
| 方法名 | 參數 | 描述 |
| --- | --- | --- |
| setData | Object?`newData` | 設置data并執行視圖層渲染 |
| hasBehavior | Object?`behavior` | 檢查組件是否具有?`behavior`?(檢查時會遞歸檢查被直接或間接引入的所有behavior) |
| triggerEvent | String?`name`, Object?`detail`, Object?`options` | 觸發事件,參見?[組件事件](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html) |
| createSelectorQuery | | 創建一個?[SelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.html)?對象,選擇器選取范圍為這個組件實例內 |
| createIntersectionObserver | | 創建一個?[IntersectionObserver](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.html)?對象,選擇器選取范圍為這個組件實例內 |
| selectComponent | String?`selector` | 使用選擇器選擇組件實例節點,返回匹配到的第一個組件實例對象(會被?`wx://component-export`?影響) |
| selectAllComponents | String?`selector` | 使用選擇器選擇組件實例節點,返回匹配到的全部組件實例對象組成的數組 |
| getRelationNodes | String?`relationKey` | 獲取這個關系所對應的所有關聯節點,參見?[組件間關系](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/relations.html) |
| groupSetData | Function?`callback` | 立刻執行?`callback`?,其中的多個?`setData`?之間不會觸發界面繪制(只有某些特殊場景中需要,如用于在不同組件同時 setData 時進行界面繪制同步) |
代碼示例:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/GqVTnvmq7g1m "在開發者工具中預覽效果")
```
Component({
behaviors: [],
properties: {
myProperty: { // 屬性名
type: String, // 類型(必填),目前接受的類型包括:String, Number, Boolean, Object, Array, null(表示任意類型)
value: '', // 屬性初始值(可選),如果未指定則會根據類型選擇一個
observer: function(newVal, oldVal, changedPath) {
// 屬性被改變時執行的函數(可選),也可以寫成在methods段中定義的方法名字符串, 如:'_propertyChange'
// 通常 newVal 就是新設置的數據, oldVal 是舊數據
}
},
myProperty2: String // 簡化的定義方式
},
data: {}, // 私有數據,可用于模板渲染
lifetimes: {
// 生命周期函數,可以為函數,或一個在methods段中定義的方法名
attached: function () { },
moved: function () { },
detached: function () { },
},
// 生命周期函數,可以為函數,或一個在methods段中定義的方法名
attached: function () { }, // 此處attached的聲明會被lifetimes字段中的聲明覆蓋
ready: function() { },
pageLifetimes: {
// 組件所在頁面的生命周期函數
show: function () { },
hide: function () { },
resize: function () { },
},
methods: {
onMyButtonTap: function(){
this.setData({
// 更新屬性和數據的方法與更新頁面數據的方法類似
})
},
// 內部方法建議以下劃線開頭
_myPrivateMethod: function(){
// 這里將 data.A[0].B 設為 'myPrivateData'
this.setData({
'A[0].B': 'myPrivateData'
})
},
_propertyChange: function(newVal, oldVal) {
}
}
})
```
注意:在?`properties`?定義段中,屬性名采用駝峰寫法(`propertyName`);在?`wxml`?中,指定屬性值時則對應使用連字符寫法(`component-tag-name property-name="attr value"`),應用于數據綁定時采用駝峰寫法(`attr="{{propertyName}}"`)。
## 使用 Component 構造器構造頁面
事實上,小程序的頁面也可以視為自定義組件。因而,頁面也可以使用?`Component`?構造器構造,擁有與普通組件一樣的定義段與實例方法。但此時要求對應 json 文件中包含?`usingComponents`?定義段。
此時,組件的屬性可以用于接收頁面的參數,如訪問頁面?`/pages/index/index?paramA=123¶mB=xyz`?,如果聲明有屬性?`paramA`?或?`paramB`?,則它們會被賦值為?`123`?或?`xyz`?。
頁面的生命周期方法(即?`on`?開頭的方法),應寫在?`methods`?定義段中。
代碼示例:
```
{
"usingComponents": {}
}
```
```
Component({
properties: {
paramA: Number,
paramB: String,
},
methods: {
onLoad: function() {
this.data.paramA // 頁面參數 paramA 的值
this.data.paramB // 頁面參數 paramB 的值
}
}
})
```
#### Bug & Tips:
* 使用?`this.data`?可以獲取內部數據和屬性值,但不要直接修改它們,應使用?`setData`?修改。
* 生命周期函數無法在組件方法中通過?`this`?訪問到。
* 屬性名應避免以 data 開頭,即不要命名成?`dataXyz`?這樣的形式,因為在 WXML 中,?`data-xyz=""`?會被作為節點 dataset 來處理,而不是組件屬性。
* 在一個組件的定義和使用時,組件的屬性名和 data 字段相互間都不能沖突(盡管它們位于不同的定義段中)。
* 從基礎庫?[2.0.9](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.0.9 開始支持,低版本需做兼容處理。")?開始,對象類型的屬性和 data 字段中可以包含函數類型的子字段,即可以通過對象類型的屬性字段來傳遞函數。低于這一版本的基礎庫不支持這一特性。
* `bug`?: 對于 type 為 Object 或 Array 的屬性,如果通過該組件自身的 this.setData 來改變屬性值的一個子字段,則依舊會觸發屬性 observer ,且 observer 接收到的?`newVal`?是變化的那個子字段的值,?`oldVal`?為空,?`changedPath`?包含子字段的字段名相關信息。
# 組件間通信與事件
## 組件間通信
組件間的基本通信方式有以下幾種。
* WXML 數據綁定:用于父組件向子組件的指定屬性設置數據,僅能設置 JSON 兼容數據(自基礎庫版本?[2.0.9](https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html "基礎庫 2.0.9 開始支持,低版本需做兼容處理。")?開始,還可以在數據中包含函數)。具體在?[組件模板和樣式](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html)?章節中介紹。
* 事件:用于子組件向父組件傳遞數據,可以傳遞任意數據。
* 如果以上兩種方式不足以滿足需要,父組件還可以通過?`this.selectComponent`?方法獲取子組件實例對象,這樣就可以直接訪問組件的任意數據和方法。
## 監聽事件
事件系統是組件間通信的主要方式之一。自定義組件可以觸發任意的事件,引用組件的頁面可以監聽這些事件。關于事件的基本概念和用法,參見?[事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html)。
監聽自定義組件事件的方法與監聽基礎組件事件的方法完全一致:
代碼示例:
```
<!-- 當自定義組件觸發“myevent”事件時,調用“onMyEvent”方法 -->
<component-tag-name bindmyevent="onMyEvent" />
<!-- 或者可以寫成 -->
<component-tag-name bind:myevent="onMyEvent" />
```
```
Page({
onMyEvent: function(e){
e.detail // 自定義組件觸發事件時提供的detail對象
}
})
```
## 觸發事件
自定義組件觸發事件時,需要使用?`triggerEvent`?方法,指定事件名、detail對象和事件選項:
代碼示例:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/DFfYSKmI6vZD "在開發者工具中預覽效果")
```
<!-- 在自定義組件中 -->
<button bindtap="onTap">點擊這個按鈕將觸發“myevent”事件</button>
```
```
Component({
properties: {}
methods: {
onTap: function(){
var myEventDetail = {} // detail對象,提供給事件監聽函數
var myEventOption = {} // 觸發事件的選項
this.triggerEvent('myevent', myEventDetail, myEventOption)
}
}
})
```
觸發事件的選項包括:
| 選項名 | 類型 | 是否必填 | 默認值 | 描述 |
| --- | --- | --- | --- | --- |
| bubbles | Boolean | 否 | false | 事件是否冒泡 |
| composed | Boolean | 否 | false | 事件是否可以穿越組件邊界,為false時,事件將只能在引用組件的節點樹上觸發,不進入其他任何組件內部 |
| capturePhase | Boolean | 否 | false | 事件是否擁有捕獲階段 |
關于冒泡和捕獲階段的概念,請閱讀?[事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html)?章節中的相關說明。
代碼示例:
[在開發者工具中預覽效果](https://developers.weixin.qq.com/s/UGfljKm66zZ1 "在開發者工具中預覽效果")
```
// 頁面 page.wxml
<another-component bindcustomevent="pageEventListener1">
<my-component bindcustomevent="pageEventListener2"></my-component>
</another-component>
```
```
// 組件 another-component.wxml
<view bindcustomevent="anotherEventListener">
<slot />
</view>
```
```
// 組件 my-component.wxml
<view bindcustomevent="myEventListener">
<slot />
</view>
```
```
// 組件 my-component.js
Component({
methods: {
onTap: function(){
this.triggerEvent('customevent', {}) // 只會觸發 pageEventListener2
this.triggerEvent('customevent', {}, { bubbles: true }) // 會依次觸發 pageEventListener2 、 pageEventListener1
this.triggerEvent('customevent', {}, { bubbles: true, composed: true }) // 會依次觸發 pageEventListener2 、 anotherEventListener 、 pageEventListener1
}
}
})
```