WXML 用于描述页面的结构。类似于 HTML,支持写成单标签或者双标签,但是必须有严格的闭合,并且大小写敏感。
数据绑定:
WXML 中的动态数据均来自对应 Page 的 data。数据绑定使用 Mustache 语法(双大括号)将变量包起来,可以作用于:
- 内容:
<view> {{ message }} </view> Page({ data: { message: 'Hello MINA!' } })
- 属性:
<view wx:if="{{condition}}"> </view> <view id="item-{{id}}"> </view> <checkbox checked="{{false}}"> </checkbox> Page({ data: { condition: true, id: 0, } })
- 运算:可以在
{{}}
内进行简单的运算,支持的有如下几种方式:- 三元运算:
<view hidden="{{flag ? true : false}}"> Hidden </view>
- 算数运算:
<view> {{a + b}} + {{c}} + d </view>
- 逻辑判断:
<view wx:if="{{length > 5}}"> </view>
- 字符串运算:
<view>{{"hello" + name}}</view>
- 数据路径运算:
<view>{{object.key}} {{array[0]}}</view>
- 三元运算:
- 组合:也可以在 Mustache 内直接进行组合,构成新的数组或对象。
// 数组: <view wx:for="{{[zero, 1, 2, 3, 4]}}"> {{item}} </view> // 对象 <template is="objectCombine" data="{{for: a, bar: b}}"></template>
双向绑定:
在 WXML 中,普通属性的绑定是单向的。如果使用 this.setData({ value: 'Hello'})
来更新 value ,this.data.value
和输入框的中显示的值都会被更新为 Hello ;但如果用户修改了输入框里的值,却不会同时改变 this.data.value
。
<input value="{{value}}" />
如果需要在用户输入的同时改变 this.data.value
,需要借助双向绑定机制。此时,可以在对应项目之前加入 model:
前缀:
双向绑定同样可以使用在自定义组件上。
<input model:value="{{value}}" />
这样,如果输入框的值被改变了,this.data.value
也会同时改变,同时, WXML 中所有绑定了 value 的位置也会被一同更新, 数据监听器也会被正常触发。
双向绑定的限制:
用于双向绑定的表达式只能是一个单一字段的绑定。
// 非法
<input model:value="值为 {{value}}" />
<input model:value="{{ a + b }}" />
<input model:value="{{ a.b }}" />
自定义组件中使用双向绑定:
双向绑定同样可以使用在自定义组件上。
// custom-component.js
Component({
properties: {
transferValue: String
}
})
<!-- custom-component.wxml -->
<input model:value="{{transferValue}}" />
<!-- index.wxml -->
<custom-component model:transfer-value="{{pageValue}}" />
当自定义组件的输入框的值变更时,自定义组件的 transferValue 属性会同时变更,页面的 this.data.pageValue
也会同时变更,页面 WXML 中所有绑定了 pageValue 的位置也会被一同更新。
列表渲染:
可以使用 wx:for
遍历一个数组,即可使用数组中各项的数据重复渲染组件。数组当前项的下标默认为 index,数组当前项的变量名默认为 item。
当
wx:for
遍历的值是字符串时,会将字符串解析成字符串数组。
当wx:for
遍历的值是数字时,会从 1 开始遍历该数字。
当wx:for
遍历的值是对象时,index 对应的就是 key,item 对应的就是 value。
<view wx:for="{{array}}">
{{index}}: {{item.message}}
</view>
使用 wx:for-item
可以指定数组当前元素的变量名,使用 wx:for-index
可以指定数组当前下标的变量名。
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}}: {{itemName.message}}
</view>
block wx:for
:
可以将 wx:for
用在 <block/>
标签上,以渲染一个包含多节点的结构块。
<block/>
就是一个占位符,自己本身并不会被渲染到页面上。
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: </view>
<view> {{item}} </view>
</block>
wx:key
:
如果列表中项目的位置会动态改变或者会有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态,需要使用 wx:key
来指定列表中项目的唯一的标识符。
如果不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
wx:key
的值以两种形式提供:
- 字符串:表示 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
- 保留关键字
*this
: 表示 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,不能是数组或对象。
和 React、Vue 类似,小程序内部也是用了虚拟 DOM,当某一层有很多相同的节点时,希望插入、删除节点时,可以更好地复用节点,因此需要 key 属性。
Page({
data: {
uniques: [
{id: 4, unique: 'unique_4'},
{id: 3, unique: 'unique_3'},
{id: 2, unique: 'unique_2'},
{id: 1, unique: 'unique_1'},
],
numbers: [1, 2, 3, 4]
},
})
<view wx:for="{{uniques}}" wx:key="unique"> {{item.id}} </view>
<view wx:for="{{numbers}}" wx:key="*this"> {{item}} </view>
条件渲染:
使用 wx:if
来判断是否需要渲染该代码块,也可以用 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/>
并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
wx:if
vs hidden:
hidden 是所有组件默认都拥有的属性。
wx:if
:如果条件为 true,则渲染条件块;如果条件为 false,则什么也不做。也就是说,当wx:if
的条件值切换时,条件块会一直销毁或重新渲染,有更高的切换消耗。- hidden:不论条件为 true 或者为 false,组件始终会被渲染,只是通过 display 控制显示与隐藏。有更高的初始渲染消耗。
因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则wx:if
较好。
模板:
WXML 提供模板 template,可以在模板中定义代码片段,然后在不同的地方调用。
模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的
<wxs />
模块。
定义模板:
在<template/>
内定义代码片段,使用 name 属性,作为模板的名字。
<!-- template.wxml -->
<template name="msgItem">
<view>
<text bindtap="handleClick"> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>
使用模板:
- 模版的引用需要用
<import>
标签,该标签的 src 属性为需要引用模版的路径。 - 模版的使用需要用
<template>
标签,使用 is 属性来区别模版文件中定义的模版。 - 在调用页面的 wxml 文件中使用 data 传入模版页面中所需要的数据。
- 在调用页面的 wxml 中引用了
template.wxml
后,模版的样式并不会引用, 需要在调用页面的 wxss 文件中引用template.wxss
文件。 - 在调用页面的 js 文件中定义模板页面中的绑定方法。
<!-- inex.wxml -->
<import src="../tpls/template.wxml" />
<template is="msgItem" data="{{...item}}"/>
// index.js
Page({
data: {
item: {
index: 0,
msg: 'this is a template',
time: '2016-09-15'
}
},
handleClick: function(){}
})
/* index.wxss */
@import "../tpls/template.wxss"
引用:
可以对 WXML 文件进行拆分,通过 import 和 include 这两种方式进行引用。
- include:可以将目标文件中的除了
<template/> <wxs/>
外的整个代码引入,相当于是直接将代码拷贝到 include 所在的位置。// index.js Page({ data: { content: '我是引入的其他 wxml 文件中的 content', }, }) <!-- index.wxml --> <view>header</view> <include src="content.wxml"/> // include 就相当于是直接将代码拷贝过来 <view>footer</view> <!-- content.wxml --> <view>{{content}}</view> // index.js data 中的数据在 content.wxml 中可以直接使用
// 就相当于是: <!-- index.wxml --> <view>header</view> <view>我是引入的其他 wxml 文件中的 content</view> <view>footer</view>
- import:可以在调用文件中使用目标文件中定义的 template 模板。
import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template。
例如:C import B,B import A,在 C 中可以使用 B 定义的 template,在 B 中可以使用 A 定义的 template,但是 C 不能使用 A 定义的 template。// index.js Page({ data: { content: '我是引入的其他 wxml 文件中的 content', }, }) <!-- index.wxml --> <view>header</view> <import src="content.wxml"/> <template is="content" data="{{content: content}}"/> <view>footer</view> <!-- content.wxml --> <template name="content"> <text>{{content}}</text> // 不可以直接使用 index.js data 中的数据,需要通过 data 传递进来 </template>