WXML(WeiXin Markup Language)是框架设计的一套标准语言,用于渲染界面,WXML的渲染原理和React Native思路一致,通过一套标记语言,在不同平台被解析为不用端的渲染文件,如图:
使用微信开发者工具时,在WXML中编写一些HTML标签或自定义标签仍然会被正常解析,这会给开发者造成一种小程序能直接支持HTML标签的误解。这是因为微信开发者工具内核是浏览器内核,同时小程序框架并没对WXML中的标签和WXSS中的内容进行强验证,所以HTML和CSS能直接被解析,但这种不合法的WXML在手机端微信中是不能正常显示的。开发过程中我们异地杠幺拿真机进行测试,保证程序能正常运行。WXML具有数据绑定、列表渲染、条件渲染、模板、事件等能力。
1.数据绑定
小程序中页面渲染时,框架会将WXML文件同时对应Page的data进行绑定,在页面中我们可以直接使用data中的属性。小程序的数据绑定使用Mustache语法(双大括号{{}})将变量或简单的运算规则抱起来,主要由以下几种渲染方式:
1)简单绑定
简单绑定是指我们使用Mustache语法(双大括号{{}})将变量包起来,在模板中直接作为字符串输出使用,可作用于内容、组件属性、控制属性、关键字等输出,其中关键字输出是将JavaScript中的关键字按其真值输出。
示例代码如下:
{{content}}
作为属性渲染
作为属性渲染
{{2}}
Page({
data: {
border:'solid 1px #000',
id:1,
content:'内容',
showContent:false
}
})
运行效果如下:
注意:组件属性为boolean类型时,不要直接写checked ="false",这样checked的值时一个false的字符串,转成boolean类型后代表为true,所以这种情况一定要使用关键字输出:checked="{{false}}"
2)运算
在{{}}内可以做一些简单的运算,支持的运算有三元运算、算术运算、逻辑判断、字符串运算,这些运算均符合JavaScript运算规则,示例如下:
Page({
data: {
showContent:false,
num1:1,
num2:2,
num3:3,
name:'weixin',
myObject:{
age:12
},
myArray:['arr1','arr2']
}
});
{{showContent ?'显示文本':'不显示文本'}}
{{num1+num2}}+1+{{num3}}=?
{{"name:"+name}}
{{num3>0}}
{{myObject.age}}{{myArray[1]}}
执行结果如下:
3)组合
data中的数据可以在模板再次组合成新的数据结构,这种组合常常在数组或对象中使用,数组组合比较简单,可以直接将值放置到数组某个下标下:
{{[myValue,2,3,'stringtype']}}
Page(
{
data:{
myValue:0
}
}
)
对象组合有3种组合方式,这里我们以数据注入模板为例
第一种,直接将数据作为value值进行组合:
Page(
{
data: {
myValue1: 'value1',
myValue2: 'value2'
}
}
)
最终组合成的对象为:{name:'vaue1',age:'value2'}
第二种,通过“...”将一个对象展开,把key-value值拷贝到新的结构中:
Page({
data:{
myObj1:{
key1:1,
key2:2
},
myObj2:{
key3:3,
key4:4
}
}
});
最终组合成的对象为:{key1:1,key2:2,key5:5,key3:3,key4:4,key6:6}
第三种,如果对象key和value相同,可以只写key值:
Page({
data:{
key1:1,
key2:2
}
});
最后的组合对象为:{key1:1,key2:2}
如果一个组合中有相同的属性名时,后面的属性将会覆盖前面的属性,如:
Page({
data:{
key1:1,
key2:2
}
});
最后的组合对象为:{key1:3,key2:2}
2.条件渲染
1)wx:if
除了简单的数据绑定,我们常常会使用逻辑分支,这时候可以使用wx:if="{{判断条件}}"来进行条件渲染,当条件成立时渲染该块代码:
内容
Page({
data:{
showContent:true /*若未false则不显示Content*/
}
});
和普通的编程语言一样,WML也支持wx:elif和wx:else,如:
1
2
3
2)block wx:if
wx:if是一个控制属性,可以添置在任何组件标签上,但如果我们需要包装多个组件,又不想影响布局,这时就需要使用
view组件
3)wx:if与hidden
除了wx:if组件,也有了通过hidden属性控制组件是否显示,开发者难免有疑问,这两种方式该怎样取舍,这里我们整理了两种方式的区别:
在频繁切换状态的场景中,会产生更大的消耗,这时尽量使用hidden;
在运行时条件变动不大的场景中我们使用wx:if,这样能保证页面有更高效的渲染,而不用把所有组件渲染出来。
3.列表渲染
1)wx:for
组件的wx:for控制属性用于遍历数组,重复渲染该组件,遍历过程中当前项的下标变量名默认为index,数组当前项变量名默认为item,如:
{{index}}:{{item}}
Page({
data:{
myArray:['value1','value2']
}
});
2)wx:for-index和wx:for-item的变量名修改
index、item变量名可以通过wx:for-index、wx:for-item属性修改,如:
{{myIndex}}:{{myItem.name}}
Page({
data:{
myArray:[
{name: 'value1'},
{name: 'value2'}
]
}
});
普遍遍历中我们没必要修改index、item变量名,当wx:for嵌套使用时,就有必要设置变量名,避免变量名冲突,下面我们遍历一个二维数组:
{{subItem}}
Page({
data:{
myArray:[
[1,2,3],
[4,5,6],
[7,8,9]
]
}
});
在本示例中,我们使用了
4.模板
在项目过程中,常常会遇到某些相同的结构在不同的地方反复出现,这时可以将相同的布局代码片段放置到一个模板中,在不同的地方传入到对应的数据进行渲染,这样能避免重复开发,提升开发效率。
1)定义模板
定义模板非常简单,在内定义代码片段,设置的name属性,指定模板名称即可。如:
内容
{{content}}
2)使用模板
使用模板时,设置is属性指定需要使用的模板,设置data属性,将模板所需的变量传入。模板拥有自己的作用域,只有使用data属性传入数据,而不是直接使用Page中的data数据,渲染时,标签将被模板中的代码块完全替换
示例代码如下:
内容
{{content}}
{{name}}
{{myObj.key1}}
{{key2}}
Page({
data:{
name:'myTemplate',
myObj:{
key1:'value1'
},
myObj2:{
key2:'value2'
}
}
});
执行结果如下:
模板可以嵌套使用:
b tempalte content
a template content
注意:模板is属性支持数据绑定,在项目过程中我们可以通过属性绑定动态决定使用哪个模板,如:
5.事件
WXML中的事件系统和HTML中DOM事件系统极其相似,也是通过在组件上设置“bind(或catch)+事件名”属性进行事件绑定,当触发事件时,框架会调用逻辑层中对应的事件处理函数,并将当前状态通过参数传递给事件处理函数,由于小程序中没有DOM节点概念,所以事件只能通过WXML绑定,不能通过逻辑层动态绑定。官方对WXML事件的定义如下:
1)事件分类
有前端开发经验的开发者应该对事件冒泡都有一定了解,当一个事件被触发后,该事件会沿该组件向其父级对象传播,从里到外依次执行,直到节点最顶层,这个是个非常有用的特性,通常用于实现事件代理,具体实现方案将在下文中具体讨论。
WXML冒泡事件如下:
对于冒泡事件每个组件都是默认支持的,除上述事件之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如:
的submit事件,2)事件绑定
在之前内容中,已经多次实现事件绑定,事件绑定的写法和组件属性一样,以key、value形式组织
绑定时bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定会阻止冒泡事件向上冒泡
冒泡示例如下:
view1
view2
view3
如上述示例中,点击view3时会先触发tap3和tap2事件,有view2他用过catch阻止了tap事件冒泡,这时tap1将不会执行,点击view2只触发tap2,点击view1只触发tap1
3)事件对象
如果没有特殊说明,当组件触发事件时,逻辑层绑定该事件的事件处理函数会收到一个事件对象,如:
view
Page({
myevent:function(e){
console.log(e);
}
});
上述代码中,myevent参数e便是事件对象,这和JavaScript事件绑定特别像,上述代码执行后事件对象输出如下:
事件对象属性基本可以分为三类:BaseEvent、CustomEvent、TouchEvent
BaseEvent为基础事件对象属性,包括:
中的触摸事件不可冒泡,所以没有currentTarget
dataset是组件的自定义数据,通过这种方式可以将组件的自定义属性传递给逻辑层。书写方式为:以data-开头,多个单词由连字符“-”连接,属性名不能有大写(大写最终会被转为小写),最终在dataset中将连字符专程驼峰形式如:
Page({
myevent:function(e){
console.log(e.currentTarget.dataset);
}
});
dataset示例
CustomEvent为自定义事件对象(继承BaseEvent),只有一个属性:
detail没有统一格式,在
的submit方法中它是{"vaue":{},"formid":""},在TouchEvent为触摸事件对象(继承BaseEvent)属性如下所示:
由于支持多点触摸,所有touched和changedTouched都是数组格式,每个元素为一个Touch对象(canvas触摸事件中为CanvasTouch对象)
Touch对象相关属性如下:
CanvasTouch对象相关属性如下:
6.引用
一个WXML可以通过Import或include引入其他WXML文件,两种方式都能引入WXML文件,区别在于import引入WXML文件后只接受模板定义,忽略模板定义之外的所有内容,而且使用过程中有作用域的概念,与improt相反,include则是引入文件中除
1)import
内容
b Template content
import引用有作用域概念,只能直接使用引入的模板,而不能使用间接引入的定义模板,如下例:
b content
b tempalte conteng
c template content
2)include
include引入会将模板定义标签外的内容(含模板使用标签)直接赋值替换
b content
b tempalte conteng{{name}}
c template content
Page({
data:{
name:2 /**将数据注入到b.wxml中 */
}
})
通过对比发现,Import更适合引用模板定义文件,include更适合引入组件文件,在项目中大家可以根据特性灵活使用。