任意
2.1.8
2.6.10
https://codesandbox.io/embed/vant-issue-moban-l9y3q
demo 下来回切换 foo , bar Tab
@ystarlongzi 有空帮忙看下~
嗯嗯,好的,今天我看下
这个问题还是由于切换后 <van-list /> 在可视区域外,因此导致不会触发 onLoad 事件
问:为什么快速的在 Foo 和 Bar 页面之间切换时,有时候触发 onLoad 呢?
答:首先,需要注意的一个点是:页面在左右切换时,它们的 UI 排列布局是「上下」的,而不是「左右」的。假设当前页面是 Foo,然后快速点击 Bar --> Foo --> Bar 时,切换大致过程如下:
Bar:此时由于 Foo 是一屏高,因此 Bar 是屏幕区域外,不会触发 onLoad
Foo:由于上次 Bar 未加载内容,因此高度是 0,所以此次 Foo 是在可视区域内,会触发 onLoad,开始加载数据
Bar:如果速度够快,切换会早于上次 Foo 加载数据完成前,此时 Foo 的高度也可认为是 0,所以这次 Bar 也是在可视区域内,所以会触发 onLoad
可以在 Layout.vue 里添加以下样式,延迟页面切换动画,然后观察这个过程
.van-slide-left-enter-active {
animation-duration: 10s;
}
.van-slide-left-leave-active {
animation-duration: 10s;
}
解决方案是:
this.$refs.list.check(); 手动触发,或👍到位
@ystarlongzi 十分感谢,但是涉及 list 的页面布局一般不太可能用左右,所以方案 2 是不太可能。方案一虽不是特别优雅,但我试了试,也没效果呢?https://codesandbox.io/embed/vant-issue-moban-l9y3q
麻烦再帮忙看下。
@kid1412621
更严谨的说话应该是在页面切换动画完成之后在触发 this.$refs.list.check();
但我发下在路由组件内部好像没办法监听切换动画是否完成,所以只能加个定时器了。如下:
mounted() {
this.listCheckTimer = setTimeout(() => {
this.$refs.list.check();
// 因为切换动画时长是 300ms,所以这个定时器的时间一定要大于 300ms
}, 350);
},
beforeDestroy() {
// 页面销毁前,记得清空计时器
clearTimeout(this.listCheckTimer)
}
@ystarlongzi 再次感谢, 感觉有点 hack 的意思, 有没有更内建的解决方案呢?
嗯嗯,其实还有个方案我觉得比较合理,大致思路是:
不要依赖 onLoad 事件去加载数据(被动获取),还要结合用户的实际操作去加载数据(主动获取)
methods: {
loadData() {
if (this.loading) {
return;
}
this.loading = true;
// ........ 请求数据
},
onLoad() {
// 被动加载数据
this.loadData();
}
},
mounted() {
// 主动加载数据
this.loadData();
}
另外,如果要使用这种方案,还需要将 <van-list v-model="loading" /> 修改为 <van-list :loading="loading" />,否则第一次加载数据之后,后面的将无法再加载更多。原因如下代码,可以看到它会先将 loading 赋值为 true,然后再触发 load 事件
https://github.com/youzan/vant/blob/2d7a188c2daeaf460bc15ed2b59e7bd2b7d76a2d/src/list/index.js#L91-L94
@chenjiahan 是不是调换下两个 emit 顺序,会相对合理些?
我觉得目前的事件触发顺序从逻辑上说没有问题,如果要实现上述的主动触发的需求,可以按下面这样写
methods: {
onLoad() {
// 请求数据
},
// 主动加载数据
loadData() {
if (!this.loading) {
this.loading = true;
this.onLoad();
}
}
},
mounted() {
this.loadData();
}
嗯嗯,你这种方案是可行的。但假如调整了那两个 emit 的顺序,它能支持我们两个的写法,用户使用起来也是更「无感知」的
其实,我还是比较倾向 loading 的赋值位置应该是:在事件函数头部之后 、但在 ajax 之前(之后也可以,因为是异步的),个人的一些开发习惯 😹😹。大致代码如下:
loadXXX() {
// 在函数最顶部,获取的 loading 值应该是「上次」的
// ....... 一些逻辑判断
// .......
// ajax 前,去更新 loading
loading = true;
ajax().then(() => {
// ajax 后,再更新 loading
loading = false;
});
}
对于组件来说,不会关心用户是通过异步请求还是同步读取缓存的方式去获取数据,只要触发 load 事件,就代表已经进入 loading 状态了
为什么不建议调整 emit 的顺序,一是目前的顺序从逻辑上是合理的,二是你不知道现有用户在 onLoad 中是否根据 loading 做了某些判断,如果有这样的逻辑,那我们改动就相当于造成了 breaking change
嗯嗯,明白了
Most helpful comment
这个问题还是由于切换后
<van-list />在可视区域外,因此导致不会触发 onLoad 事件问:为什么快速的在 Foo 和 Bar 页面之间切换时,有时候触发 onLoad 呢?
答:首先,需要注意的一个点是:页面在左右切换时,它们的 UI 排列布局是「上下」的,而不是「左右」的。假设当前页面是 Foo,然后快速点击 Bar --> Foo --> Bar 时,切换大致过程如下:
Bar:此时由于 Foo 是一屏高,因此 Bar 是屏幕区域外,不会触发 onLoad
Foo:由于上次 Bar 未加载内容,因此高度是 0,所以此次 Foo 是在可视区域内,会触发 onLoad,开始加载数据
Bar:如果速度够快,切换会早于上次 Foo 加载数据完成前,此时 Foo 的高度也可认为是 0,所以这次 Bar 也是在可视区域内,所以会触发 onLoad
可以在 Layout.vue 里添加以下样式,延迟页面切换动画,然后观察这个过程
解决方案是:
this.$refs.list.check();手动触发,或