van-list 一直触发加载问题的排查

移动端分页一般都是向下滑动加载更多,Vant 组件库提供了 van-list 来实现这一功能。

List 组件通过loadingfinished两个变量控制加载状态,当组件滚动到底部时,会触发load事件并将loading设置成true。此时可以发起异步操作并更新数据,数据更新完毕后,将loading设置成false即可。若数据已全部加载完毕,则直接将finished设置成true即可。

实际应用中发现,即使内部元素已经填满一整屏,仍然会触发加载,直到我模拟的20多页全部加载完才停下来。

我没有给这个问题写专门的 CodePen demo,比较麻烦,碰到的人也不一定多。

结论

van-list 会在内容之后增加一个 placeholder,通过 placeholder 的位置判断内容是否已填充满屏。
van-list 本身为 flex 布局时,flex-direction 必须是 column(或 column-reverse)。

过程

首先我的用法是这样的:

template
1
2
3
4
5
6
7
<van-list> <!-- flex -->
<van-grid>
<van-grid-item />
<van-grid-item />
<van-grid-item />
</van-grid>
</van-list>

首先看文档,英文文档没有提到,但中文文档有以下一段话:

使用 float 布局后一直触发加载?
若 List 的内容使用了 float 布局,可以在容器上添加van-clearfix类名来清除浮动,使得 List 能正确判断元素位置

检查了一下,我没有在使用 float 布局呀。

那看一下Vant源码,主要看Vant是如何判断页面何时去加载的:

check()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
this.$nextTick(() => {
if (this.loading || this.finished || this.error) {
return;
}

const { $el: el, scroller, offset, direction } = this;
let scrollerRect;

if (scroller.getBoundingClientRect) {
scrollerRect = scroller.getBoundingClientRect();
} else {
scrollerRect = {
top: 0,
bottom: scroller.innerHeight
};
}

const scrollerHeight = scrollerRect.bottom - scrollerRect.top;

/* istanbul ignore next */
if (!scrollerHeight || isHidden(el)) {
return false;
}

let isReachEdge = false;
const placeholderRect = this.$refs.placeholder.getBoundingClientRect();

if (direction === 'up') {
isReachEdge = placeholderRect.top - scrollerRect.top <= offset;
} else {
isReachEdge = placeholderRect.bottom - scrollerRect.bottom <= offset;
}

if (isReachEdge) {
this.$emit('input', true);
this.$emit('load');
}
});

scrollerRect 是滚动区域的位置,通过 debug 看到获取到的值没有问题,但 placeholderRect 的值一直都是同一个值,导致 isReachEdge 一直是 true

进一步 inspect 页面元素,发现这个 placeholder 的位置在这里:

template
1
2
3
4
5
6
7
8
<van-list>
<van-grid>
<van-grid-item />
<van-grid-item />
<van-grid-item />
</van-grid>
<div class="van-list__placeholder"></div> <!-- placeholder -->
</van-list>

placeholder 是一个没有大小的 div,由于 van-list 是 flex 横向布局,placeholder 自然地落在了 van-grid 的右边,这时不管 van-grid 怎么高,placeholder 的位置都不变了。

知道了问题所在,我们在 van-list 元素上增加了 flex-direction: column; 成功地解决了问题。

# js, vue

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×