# 小程序 视图
[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
}
}
})
```