场景描述
首页循环渲染多个以折叠面板形态展示的分组,每个分组中循环渲染多个子项。
在点击“添加子项”后,通过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()方法。(此处的init()方法为定义在组件的"methods"下的自定义方法,你可以根据组件的实际情况调用组件"methods"下的任意方法来实现相同的效果,也可以自定义相关方法来实现对组件内部成员的重新渲染)
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())
)
}
代码片段
如果想复现上述场景,以及解决方案,可以下载编辑我写的代码片段。