返回博客

微信小程序统计文本不能挂 reveal flag:wx:if 导致整块闪烁

做列表页时,我习惯给卡片区域加一个入场 reveal flag:数据加载完再把卡片整体渲染出来,配合 CSS transition 做一个从透明到不透明的进场效果。这个模式本身没什么问题——直到某次下拉刷新,我发现顶部的「共 N 条」文字整块消失了 1 帧,然后又重新出现。而且每次翻页、切筛选 tab 都会触发一次。

现象

列表页顶部有一行统计文字:共 {{total}} 条

  • 下拉刷新时,该文字整块消失约 100~300ms,再重新出现
  • 切换筛选条件时同样触发
  • 翻页加载下一页时也会闪一下
  • 文字消失期间位置不保留,两侧的兄弟元素会发生轻微抖动

根因

翻了一下 WXML,问题一眼就找到了:

<!-- 反模式:统计文字跟卡片入场 flag 共用同一个 wx:if -->
<view class="list-summary" wx:if="{{pageReady}}">
  共 {{total}} 条
</view>
 
<view class="card-zone" wx:if="{{pageReady}}">
  <!-- 卡片列表 -->
</view>

刷新逻辑大概是这样:

// 刷新时先把 flag 置 false,请求完成再还原
this.setData({ pageReady: false })
fetchList().then(res => {
  this.setData({ pageReady: true, total: res.total })
})

wx:if 与 CSS display:nonehidden 不同——它会真实地卸载节点,等 flag 变回 true 再重新挂载。这意味着:

  1. pageReady: false 时,list-summary 节点从 DOM 树中完全移除
  2. 其兄弟元素填充了原来的空间,触发 flex/inline-block reflow
  3. pageReady: true 时,节点重新插入,再触发一次 reflow
  4. 如果数字位数发生变化(比如从 9 变成 10),iOS WebKit 还会多做一次 kerning 计算,加剧抖动

这个 flag 最初是为卡片入场动画设计的,统计文字只是被"顺手"塞进了同一个 wx:if 下,没有人意识到两者的语义完全不同:卡片可以出场/入场,但统计数字是页面元信息,应该始终存在。

坑:统计文字混进了 reveal 动画的 wx:if

wx:if 不是隐藏,是卸载。把「共 N 条」「合计 ¥X」「已选 N/M」这类页面元信息文字放进刷新 flag 里,每次刷新都会触发卸载 → 布局坍塌 → 重新挂载 → 布局恢复的完整过程,肉眼可见地闪烁。

正解

统计文字永远渲染,只让数据本身是响应式的。 把文字拆成独立的 <text> 节点,数字用 || 0 兜底防 undefined 闪空:

<!-- 正解:永远渲染,数据用 fallback -->
<view class="list-summary">
  <text>共 </text>
  <text>{{total || 0}}</text>
  <text> 条</text>
</view>
 
<view class="card-zone" wx:if="{{pageReady}}">
  <!-- 卡片入场动画正常保留 -->
</view>

拆成 3 个 <text> 节点而不是 <view>共 {{total}} 条</view> 写在一起,是因为 WXML 把整段字符串视为一个文本节点,total 变化时会重新 tokenize 整段——包括汉字的 kerning 重算,以及数字位数变化时的宽度重排。拆开后,渲染器只需 diff 中间那个数字节点,两边汉字保持稳定。

关键要点:

  1. 删掉 wx:if——统计文字不参与 reveal 动画
  2. 数字用 || 0|| '-' 兜底——防止请求还未返回时 totalundefined 导致短暂空白
  3. 加载态用 disabled / 半透明覆盖层,而不是 wx:if=false 卸载文字
  4. hidden="{{!pageReady}}" 同样不行——hidden 虽然保留节点,但切换时仍触发样式重算,效果差不多
正解:统计文字脱离 reveal flag,独立常驻渲染

将页面元信息(统计数、合计金额、时间戳等)的容器节点从 wx:if 中解放出来,只让数字本身跟数据绑定,并加 || 0 fallback。卡片入场动画的 wx:if 放在它下面的卡片区,两者互不干扰。

排查决策树

遇到微信小程序统计文字闪烁,按以下顺序排查:

文字整块消失?
└─ 检查容器是否有 wx:if / hidden / block wx:if
   └─ 找到 wx:if → 这个 flag 是否在刷新时切换?
      └─ 是 → 把统计文字提到 wx:if 外面,加 || 0 fallback
         └─ 仍然抖动 → 检查父级是否有 wx:if(祖先级卸载)

一句话外卖

页面元信息(统计数、合计、时间戳)要像页面标题一样始终存在——把它塞进 reveal 动画的 wx:if 等于每次刷新都让用户看一次"数字消失又出现"的魔术。