微信小程序自定义组件重渲染

场景描述

        首页循环渲染多个以折叠面板形态展示的分组,每个分组中循环渲染多个子项。

        在点击“添加子项”后,通过wx.navigateTo()前往添加页面。向一个分组中添加子项后,通过wx.navigateBack()返回首页。

        返回首页后,在首页的onShow()中使用setData()重新给整个分组列表赋值,触发页面渲染。此处的折叠面板为自定义组件,本应该随着新子项的加入而重新调整高度,可实际上却只看到了子项的增加,而高度依然保持原样。显然,这时的自定义组件并未重新渲染。

原因分析

        根据官方文档中对于组件生命周期的描述,我们不难发现,在上述的场景中,折叠面板组件在节点树的状态并没有发生改变,因此也就没有窗口让组件来调用调整高度的方法。

探索解决方案

1. 重新加载整个页面

        既然在onShow()中无法触发组件的生命周期,那么使整个页面重新加载,重新渲染整个页面节点树,就成了一个比较直接可行的想法。一种方法,就是用wx.redirectTo()或者wx.navigateTo()来代替wx.navigateBack(),迫使页面重新加载。这种方法虽然可行,但是也破坏了原本的页面栈,显然并不试用于所有的场景。还有种想法,就是调用页面的onLoad()方法,来使页面重新加载。可是在上述的场景中,其实并没有合适的窗口去调用onLoad()。切记不可在onShow()中调用onLoad()方法,这会触发死循环!

2. onHide()中清空data

        由于在调用wx.navigateTo()时,当前页面会被压入页面栈而触发onHide()方法,因此可以在这个时候,用setData()把列表的数据清空,迫使页面重新渲染,把原来的组件从节点树中移除。当我们重新返回页面,并在onShow()中重新setData()时,组件由于是新添加进节点树的,所以其生命周期会正常被触发。这种方法虽然可以解决问题,但是当发生页面跳转时,会先闪一下屏幕(其实是在重新渲染页面),然后再跳转。个人认为这对用户体验的影响还是蛮大的,因此并不能算是一种很优雅的解决方案。

3.组件所在页面的生命周期

         官方文档对于组件生命周期的描述中,还介绍了组件所在页面的生命周期。当组件所在页面发生状态变化时,这些生命周期就会被触发。其中正好有个show()生命周期,它会在组件所在页面被展示时触发。因此只要在组件的这个生命周期中,放入我们希望调用的方法,就能在页面重新显示时重新渲染组件。然而,根据我的实际测试,组件的show()生命周期的触发并未按照预想的那样进行。页面首次渲染时,确实能正常触发,可是当从其他页面返回时,却并没有触发。此外,这种方法需要对组件本身的逻辑十分熟悉,如果是使用其他人写的组件,并且组件逻辑很复杂,那么这种方法也就不太适用。

合适的解决方案

        上述方法,一定程度上可以解决问题,但都或多或少存在一定的问题。相对比较合适的方法,就是调用selectComponent()或者selectAllComponents()来选择组件,并调用组件内部的方法。selectComponent()可以选择某个id或者class的组件,然后对其调用内部方法。例如下面的代码中,有一个id为group的自定义组件custom-component。

<custom-component id="group">
    ...
</custom-component>

        我们就可以在通过selectComponent("#group")来选择这个组件,并调用其内部的init()方法。

this.selectAllComponents("#group").init()

        但是selectComponent()只能选择一个组件,而在上述的场景中,我们循环渲染了好几个相同id的组件,此时就应该使用selectAllComponents()。该方法会选择所有相同id或者class的组件,然后你就可以对这些组件逐一进行操作。例如,下面的代码中,当从其他页面返回当前页面时,可以在onShow()中调用setData()来重新赋值列表数据,并在其回调中选择所有id为group的组件,通过forEach()对每一个组件调用其内部的init()方法来使其重新渲染成我们期望的样子。

onShow() {
    const that =this

    this.setData(
      {
        groups: app.globalData.groups
      },
      () => that.selectAllComponents("#group").forEach((g) => g.init())
    )
  }

代码片段

        如果想复现上述场景,以及解决方案,可以下载编辑我写的代码片段

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值