小程序踩坑记——页面数据的绑定
前言
之前在用vue做前端开发的时候,已经习惯了vue语法的数据绑定,突然跨越到小程序的时候,前期还是有些不适应,因此即使这方面在小程序的官方文档里已经说得很清楚了,我还是要单独写一篇博客来记录一下我刚开始对于小程序数据绑定方面遇到的疑惑。
介绍
框架
官方文档:框架
小程序开发框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务。
整个小程序框架系统分为两部分:逻辑层(App Service)和 视图层(View)。小程序提供了自己的视图层描述语言
WXML和WXSS,以及基于JavaScript的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑。
小程序的框架主要有两个部分,官方文档也说明了这点。在逻辑层与视图层的这点上,小程序的开发与Vue的相似点很大,不过区别还是在于微信将页面的这些框架单独分开成了独立的文件,而Vue则偏向于单个vue文件组成一个页面。
响应式编程
Vue作为响应式编程,已经也算是众所周知了。小程序依赖的也是响应式的编程,数据之间的绑定也都是响应式的。
响应式编程的好处:
框架的核心是一个响应的数据绑定系统,可以让数据与视图非常简单地保持同步。当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新。
小程序的官方文档给出了一个很清晰的例子:
x12<!-- This is our View -->3<view> Hello {{name}}! </view>4<button bindtap="changeName"> Click me! </button>51812// This is our App Service.3// This is our data.4var helloData = {5 name: 'WeChat'6}78// Register a Page.9Page({10 data: helloData,11 changeName: function(e) {12 // sent data change to view13 this.setData({14 name: 'MINA'15 })16 }17})18
- 开发者通过框架将逻辑层数据中的
name与视图层的name进行了绑定,所以在页面一打开的时候会显示Hello WeChat!;- 当点击按钮的时候,视图层会发送
changeName的事件给逻辑层,逻辑层找到并执行对应的事件处理函数;- 回调函数触发后,逻辑层执行
setData的操作,将data中的name从MINA,因为该数据和视图层已经绑定了,从而视图层会自动改变为Hello MINA!。
如果对Vue很熟悉的话,会发现在这点上,小程序和Vue编程相似性很高,但是区别也是存在的。因此这篇博客主要是介绍一些小程序在数据绑定上面与Vue编程的区别。
正文
官方文档:WXML语法参考
数据绑定
WXML 中的动态数据均来自对应 Page 的 data。
简单绑定
简单绑定这方面,小程序与Vue几乎一样,这里就不详述,官网介绍得也很清楚。我们需要绑定动态数据的时候,使用 Mustache 语法(双大括号)将变量包起来,就可以用于很多场合,例如:组件内容,组件属性(需要在双引号之内),控制属性(需要在双引号之内),关键字(需要在双引号之内)等。可以看到,这里反复在强调,若想要动态绑定组件的属性,则必须要放在双引号之内。示例如下:
31<!-- index.wxml -->2<checkbox checked="{{ifChecked}}"> </checkbox>3812// index.js3Page({4 data: {5 ifChecked: true,6 }7})8这里需要注意的是,在wxml视图层里面,作为变量想绑定数据的时候务必要加上双引号。而其实我在开发的时候,经常忘记的是加双大括号。不能直接写 checked="ifChecked",其计算结果是一个字符串,转成 boolean 类型后代表真值。
运算
运算部分区别也不是很大,实际开发的时候,用多了,自己就会了。官网的例子很清楚,这里也不加详述。
运算方面用得最多的,还是在属性上面的动态绑定。经常有需求会需要动态显示隐藏某一组件,或者动态更改某一组件的样式。尽管好像不属于运算这一类下,但是还是在一起说明好了。
运算——动态显示或隐藏某一组件
熟悉vue的朋友都知道,vue有自己的特有属性v-if,v-show,功能就显而易见了,一个控制是否存在,一个控制是否显示。而微信小程序也有类似的属性wx:if,hidden,使用方法都是一样的。
wx:if hidden用法
详细参见官网文档:条件渲染。其中具体什么时候使用wx:if,什么时候使用hidden,官网给出了如下解释:
因为
wx:if之中的模板也可能包含数据绑定,所以当wx:if的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。同时
wx:if也是惰性的,如果在初始渲染条件为false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。相比之下,
hidden就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。一般来说,
wx:if有更高的切换消耗而hidden有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用hidden更好,如果在运行时条件不大可能改变则wx:if较好。类样式
我们可以通过样式来控制组件的显示与隐藏。
612<!-- index.wxml -->3<view class="modal {{ifHidden ? 'show': ''}}">4content5</view>6812// index.js3Page({4data: {5ifHidden: true,6}7})8812/* index.wxss */3.modal {4opacity: 0;5}6.show {7opacity: 1; /* 通过控制css的opatcity属性来控制组件的显示与隐藏 */8}除了
opacity属性外,也可以通过display属性,控制block或者none来显示和隐藏组件。
其余数据绑定方式
其余数据绑定方式微信小程序写得很清楚了,在这里就不多介绍了。再次贴上官网网址:数据绑定
列表渲染
wx:for
官方文档:列表渲染
列表渲染,在Vue里用的是v-for,小程序用的则是wx:for,使用方法大部分都是相同的,不过小程序有些小改动。
在组件上使用
wx:for控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。默认数组的当前项的下标变量名默认为
index,数组当前项的变量名默认为item
在vue中,使用v-for的时候,需要自己设定当前项的下标名,和当前项的变量名。而在小程序里,这些是由默认值的。因此如果没有特殊需求,我们只需要在wx:for后面写上一个数据绑定的数组即可。
612<!-- index.wxml -->3<view wx:for="{{array}}">4 {{index}}: {{item.message}}5</view>61212// index.js3Page({4 data: {5 array: [{6 message: 'foo',7 }, {8 message: 'bar'9 }]10 }11})12使用 wx:for-item 可以指定数组当前元素的变量名,
使用 wx:for-index 可以指定数组当前下标的变量名:
612<!-- index.wxml -->3<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">4 {{idx}}: {{itemName.message}}5</view>6wx:key
关于wx:key,官网有一个详细的解释:
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。
wx:key的值以两种形式提供
- 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
- 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,如: 当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
如不提供
wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
字符串
wx:for不仅仅支持数组,也是可以支持字符串的,wx:for会将字符串解析成字符串数组
612<!-- index.wxml -->3<view wx:for="array"> <!-- wx:for="array" 等同于 wx:for="{{['a','r','r','a','y']}}"-->4 {{item}}5</view>6注意:如果花括号与引号之间有空格,最终也会被解析成字符串!
612<!-- index.wxml -->3<view wx:for="{{[1,2,3]}} "> <!-- wx:for="{{[1,2,3]}} " 等同于 wx:for="{{[1,2,3] + ' '}}"-->4 {{item}}5</view>6
绑定事件
官网文档:JS-逻辑交互
一个服务仅仅只有界面展示是不够的,还需要和用户做交互:响应用户的点击、获取用户的位置等等。在小程序里边,我们就通过编写
JS脚本文件来处理用户的操作。
事件
官方文档:事件
什么是事件
- 事件是视图层到逻辑层的通讯方式。
- 事件可以将用户的行为反馈到逻辑层进行处理。
- 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
- 事件对象可以携带额外信息,如 id, dataset, touches。
Vue中给组件绑定相应的事件使用的是v-on:click等操作,而小程序则使用bindtap作为触发属性。除了bindtap外,小程序还提供了许多事件,其中大致分为:冒泡事件和非冒泡事件。由于这些事件在一些简单的开发中触及很少,因此这里就不再详述。
- 对于
bindtap事件,绑定到组件内并进行调用,示例如下:
412<!-- index.wxml -->3<view id="tapTest" data-hi="WeChat" bindtap="tapName"> Click me! </view>4- 在组件内绑定好事件后,在Page就可以写上相应的事件处理函数,参数为
event,可带可不带,根据自己需求选择。
712// index.js3Page({4 tapName: function(event) {5 console.log(event);6 }7})其中event为事件对象,当组件触发事件的时候,逻辑层的处理函数就会收到一个事件对象,对象属性列表如下:
| 属性 | 类型 | 说明 | 基础库版本 |
|---|---|---|---|
| type | String | 事件类型 | |
| timeStamp | Integer | 事件生成时的时间戳 | |
| target | Object | 触发事件的组件的一些属性值集合 | |
| currentTarget | Object | 当前组件的一些属性值集合 | |
| mark | Object | 事件标记数据 | 2.7.1 |
获取组件传递的参数
正常开发的情况下,使用到的属性并不是很多,最常用的莫过于currentTarget属性,该属性官网也给出了详细的解释:
current Target
事件绑定的当前组件
| 属性 | 类型 | 说明 |
|---|---|---|
| id | String | 当前组件的id |
| dataset | Object | 当前组件上由data-开头的自定义属性组成的集合 |
这里需要声明的一点是,小程序的绑定事件里,不允许在事件名后面直接接参数。例如上例:
612<!-- index.wxml -->3<view id="tapTest" data-hi="WeChat" bindtap="tapName"> Click me! </view> <!-- 正确用法 -->45<view id="tapTest" bindtap="tapName("WeChat")"> Click me! </view> <!-- 错误用法 -->6如果我们需要传递参数,则必须要使用id属性或者data-开头的自定义属性。添加完自定义属性后,在逻辑层的事件处理函数中带上参数event。
812// index.js3Page({4 tapName: function(event) {5 console.log(event);6 }7})8参数event的console.log值如下:
3612{3 "type":"tap",4 "timeStamp":895,5 "target": {6 "id": "tapTest",7 "dataset": {8 "hi":"WeChat"9 }10 },11 "currentTarget": {12 "id": "tapTest",13 "dataset": {14 "hi":"WeChat"15 }16 },17 "detail": {18 "x":53,19 "y":1420 },21 "touches":[{22 "identifier":0,23 "pageX":53,24 "pageY":14,25 "clientX":53,26 "clientY":1427 }],28 "changedTouches":[{29 "identifier":0,30 "pageX":53,31 "pageY":14,32 "clientX":53,33 "clientY":1434 }]35}36因此,如果我们想获取到组件传递过来的参数,我们可以使用current Target属性:
912// index.js3Page({4 tapName: function(event) {5 console.log(event);6 let greets = event.currentTarget.dataset.hi; // 这样即可获取到组件传过来的参数7 }8})9通过官方文档的说明,我们发现,dataset属性的说明是:“当前组件上由data-开头的自定义属性组成的集合”,也就是说,我们可以通过自定义属性转递的值不仅仅是单个变量,我们也可以传递字符串,数组,甚至对象。这里就不一一介绍了,用法与单个变量相同。
其余地方官网介绍得很清楚了,这里就不详述了。

