近期做了一些程序,分享个小知识点,就是模仿WEB端的TAB点击切换。在浏览器端开发时,经常会有这种情况:单击某个元素,让其高亮显示,例如下图:
这个用 js 很容易实现, 因为单击事件触发时,函数传入的 event 事件包含 target 对象,里面会有触发事件的 DOM 元素,我们只需要操作这个 DOM 元素,为其添加 class 名就好了。
而在微信小程序开发时,由于其类似于 vue 不建议直接操作 DOM (两者都有API可以做到),事件触发的时候同样会有默认参数传入, 但是无法直接取到 DOM 节点本身,而是包含挂载的一些数据,和点击部位的坐标信息等,具体参阅官方文档《事件·小程序》 ,this
也总是指向 Page ,所以我们就需要通过数据间接操纵 DOM来实现。
例如我遇到的问题是,我想做一个月历,当你选中某一天的时候,那一天高亮显示。
参考解决方案
由于自己在做这一块时遇到了很多这方面的困惑
-
wxml
<view class="list-wrapper"> <view class="list-top"> <view data-num="1" class="list-menu list-menu1 {{_num==1?'cur':''}}" bindtap="menuClick">头条</view> <view data-num="2" class="list-menu list-menu2 {{_num==2?'cur':''}}" bindtap="menuClick">活动</view> <view data-num="3" class="list-menu list-menu3 {{_num==3?'cur':''}}" bindtap="menuClick">公告</view> </view></view>
-
js
menuClick:function(e){ this.setData({ _num:e.target.dataset.num }) },
这个方法绑定了一个动态的 class 名,用一个变量 _num
可以做到切换 class 的作用,当点击元素时,js 获取到节点上 data-num
上的值,这里将值赋给变量 _num
,相应的由于是数据驱动,节点上的 class 名经过计算变化为 cur
,其他的同理。
原始解决方案
在没有搞清这个方法前,我制作月历是使用的条件渲染。具体做法是,每个日期节点准备两个 DOM 元素,一个带有 class="selected"
,一个没有, 经过列表渲染之后每个单位实际上存在两个逻辑上的元素,这个时候通过点击改变 Page
中 data
里面的 selectedDate
和 selectedDate
,进一步控制 wx:if
的条件来实现元素的渲染与否。
<!-- 列表渲染 --><view wx:for="{{unit.dates}}" wx:key="item.date"> <view wx:if="{{item.date == selectedDate && item.month == selectedMonth}}" class="selected" data-year="{{item.year}}" data-month="{{item.month}}" data-date="{{item.date}}">{{item.date}}</view> <view wx:else data-year="{{item.year}}" data-month="{{item.month}}" data-date="{{item.date}}">{{item.date}}</view></view>
点击事件发生时,获取节点中的 data-month
和 data-date
值, 并赋给 selectedDate
和 selectedDate
由于每个月都有某些日期, 所以加个月份限制,这里我设置了只做从这个月到未来6个月的月历,所以不需要加年份限制。
深知这个方案问题很大,是这一类的MVVM框架因的条件渲染切换消耗较大。
wx:if
vshidden
因为wx:if
之中的模板也可能包含数据绑定,所有当wx:if
的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。
同时wx:if
也是惰性的,如果在初始渲染条件为false
,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。
相比之下,hidden
就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。
一般来说,wx:if
有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则wx:if
较好。
当用户点击某个日期的时候会重新渲染整个 DOM ,所以这个方案并不好。
改进方案
<view class="day {{item.date > 0 ? '' : 'hidden'}}" wx:for="{{unit.dates}}" wx:key="item.date"> <view wx:if="{{item.date > 0}}"> <view class="{{item.date == selectedDate && item.month == selectedMonth ? 'selected' : ''}}" data-year="{{item.year}}" data-month="{{item.month}}" data-date="{{item.date}}">{{item.date}}</view> </view></view>
搞懂前面的逻辑,再来看这个方案就会很明白了。
总结
小程序里的TAB切换是个很常用的功能,大家一定要掌握。