视图层

WXML

WXML 是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。

数据绑定

WXML 中的动态数据均来自对应 Pagedata

数据绑定使用 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']
  }
})

数组

<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'}

特别注意

花括号和引号之间如果有空格,将最终被解析成为字符串

<view wx:for="{{[1,2,3]}}    ">
  {{item}}
</view>

等同于

<view wx:for="{{[1,2,3] + ' '}}">
  {{item}}
</view>

列表渲染

wx:for

在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件

<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

如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态,需要使用 wx:key 来指定列表中项目的唯一的标识符

wx:key 的值以两种形式提供:

  • 字符串,代表在 for 循环的 arrayitem 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变

  • 保留关键字*this,代表在 for 循环中的 item 本身,这种表示需要 item本身是一个唯一的字符串或者数字

提示

如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略

注意

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="" 来判断是否需要渲染该代码块:

<view wx:if="{{condition}}"> True </view>

也可以用 wx:elifwx: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/>并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。

事件

什么是事件

  • 事件是视图层到逻辑层的通讯方式
  • 事件可以将用户的行为反馈逻辑层进行处理。
  • 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
  • 事件对象可以携带额外信息,如 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
}]
}

事件详解

事件分为冒泡事件和非冒泡事件:

  • 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递
  • 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递

WXML的冒泡事件列表:

非冒泡事件

注:除上表之外的其他组件自定义事件如无特殊声明都是非冒泡事件,如<form/>submit事件,<input/>input事件,<scroll-view/>scroll事件

事件绑定和冒泡

  • keybindcatch开头,然后跟上事件的类型,如bindtapcatchtouchstart。自基础库版本 1.5.0 起,bindcatch后可以紧跟一个冒号其含义不变,如bind:tapcatch:touchstart

  • value 是一个字符串,需要在对应的 Page 中定义同名的函数。不然当触发事件的时候会报错。

注意

bind事件绑定不会阻止冒泡事件向上冒泡catch事件绑定可以阻止冒泡事件向上冒泡

<view id="outer" bindtap="handleTap1">
  outer view
  <view id="middle" catchtap="handleTap2">
    middle view
    <view id="inner" bindtap="handleTap3">
      inner view
    </view>
  </view>
</view>

事件对象

如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。

BaseEvent 基础事件对象属性列表:

CustomEvent 自定义事件对象属性列表(继承 BaseEvent):

TouchEvent 触摸事件对象属性列表(继承 BaseEvent):

type

代表事件的类型。

timeStamp

页面打开到触发事件所经过的毫秒数。

target

触发事件的源组件。

currentTarget

事件绑定的当前组件。

说明

targetcurrentTarget 可以参考上例中,点击 inner view 时,handleTap3 收到的事件对象 targetcurrentTarget 都是 inner,而 handleTap2 收到的事件对象 target 就是 innercurrentTarget 就是 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 // 大写会转为小写
  }
})

Touch 对象

detail

自定义事件所携带的数据,如表单组件的提交事件会携带用户的输入,媒体的错误事件会携带错误信息,详见组件定义中各个事件的定义。

点击事件的detail 带有的 x, ypageX, pageY 代表距离文档左上角的距离。

引用

WXML 提供两种文件引用方式importinclude

import

import可以在该文件中使用目标文件定义的template,如:

item.wxml 中定义了一个叫itemtemplate

<!-- 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 目标文件 importtemplate

提示

如:C import BB 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/> 外的整个代码引入,相当于是拷贝到 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 是一套样式语言,用于描述 WXML 的组件样式。

WXSS 用来决定 WXML 的组件应该怎么显示。

为了适应广大的前端开发者,WXSS 具有 CSS大部分特性。同时为了更适合开发微信小程序,WXSSCSS 进行了扩充以及修改

CSS 相比,WXSS 扩展的特性有:

  • 尺寸单位
  • 样式导入

尺寸单位

  • rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有 750 个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

建议

开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。

样式导入

使用@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。

/** common.wxss **/
.small-p {
  padding:5px;
}
/** app.wxss **/
@import "common.wxss";
.middle-p {
  padding:15px;
}

内联样式

框架组件上支持使用 styleclass 属性来控制组件的样式。

  • style: 静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。
<view style="color:{{color}};" />
  • class: 用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔
<view class="normal_view" />

选择器

目前支持的选择器有:

全局样式与局部样式

定义在 app.wxss 中的样式为全局样式,作用于每一个页面。在 pagewxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。