应用里需要做一个瀑布流的页面,类似于小红书的首页,今天终于比较完美地把这个页面做完,记录总结一下思路,分享给大家。
第一部分:CSS将页面分成左右两列,左边循环一个数组,右边循环一个数组。在data里,给左右两列的div设置一个初始高度为0。从后端获取数据的时候,对后端数组进行循环处理,根据左右两列的div高度,决定把当前对象分给左边还是右边的数组。每次分配的时候,根据元素的高低,把左/右边div的高度新增上去,以便下一个对象分配的时候使用。
元素的高需要大家根据自身情况判断,比如图片高度、标题的高度等等,自己判断一下有几种情况。比如我的元素里,图片的高度是比较固定的,影响高度的主要是标题的行数,就根据标题的字数进行判断每个元素的整体高度。
// 获取当前页的清单数据,pageNum默认为1,hasMore默认为true
async getLists(pageNum) {
if (this.hasMore) {
// 如果hasMore是true,就可以继续去获取数据
const moreLists = await ListService.getLists(pageNum)
this.moreLists = moreLists
// 如果获取到的lists为空,说明没有更多数据了,将hasMore改成false,页面提示"没有更多数据"了
if (moreLists.length === 0) {
this.hasMore = false
// 如果已经没有更多数据了,就取消监听窗口变化,因为全部数据都展示出来了不需要再监听
window.removeEventListener('scroll', this.monitorScrollToEnd)
return
}
// 将获取的数据添加到lists中
for (let list of moreLists) {
this.lists.push(list)
}
// 将获取到的数据分列
this.spiltList()
}
},
// 将获取到的数据,分为左右两列
spiltList() {
// 定义变量,接受list数据源
let data = {}
for (let i = 0; i < this.moreLists.length; i++) {
// 每次只处理新增的lists
data = this.moreLists[i]
if (this.heightOfLeft <= this.heightOfRight) {
// 如果左边矮,就放到左边
this.listLeft.push(data)
// console.log('左边有' + this.listLeft)
// 根据自己的元素逻辑更新容器的高度
if (data.listTitle.length <= 10) {
this.heightOfLeft = this.heightOfLeft + 249.67
} else {
this.heightOfLeft = this.heightOfLeft + 271.67
}
// console.log(
// '左边高变成' + this.heightOfLeft + '右边高是' + this.heightOfRight
// )
} else {
// 如果右边矮,就放到右边
this.listRight.push(data)
// console.log('右边有' + this.listRight)
// 根据自己的元素逻辑更新容器的高度
if (data.listTitle.length <= 10) {
this.heightOfRight = this.heightOfRight + 249.67
} else {
this.heightOfRight = this.heightOfRight + 271.67
}
// console.log(
// '右边高变成' + this.heightOfRight + '左边高是' + this.heightOfLeft
// )
}
}
// 数据处理完后,重新启动监听
window.addEventListener('scroll', this.monitorScrollToEnd)
},
第二部分:页面加载完后,开始监听窗口高度,如果已经滚动到最下面,就向后台请求下一页的数据。这里注意触发请求时,要暂时关闭监听,否则就会出现一连串请求的情况,一下子给请求个七八次。等到新的数据加载完后,再重新启动监听即可。做完这一部分就可以实现瀑布流无限加载到最后。
mounted() {
// 监听窗口变化
window.addEventListener('scroll', this.monitorScrollToEnd)
},
monitorScrollToEnd() {
var scrollTop =
document.documentElement.scrollTop || document.body.scrollTop
var windowHeight =
document.documentElement.clientHeight || document.body.clientHeight
var scrollHeight =
document.documentElement.scrollHeight || document.body.scrollHeight
// 滚动到底部触发
if (Math.round(scrollTop) + windowHeight > scrollHeight - 220) {
// 暂时取消监听,等数据都拿到了再重启监听
window.removeEventListener('scroll', this.monitorScrollToEnd)
// 获取下一页的数据
this.pageNum += 1 // 页数加1
// 要解决触发后连续触发的问题
this.getLists(this.pageNum) // 获取下一页数据
}
},
第三部分:由于开启了窗口高度监听,这时候如果跳到详情页,或者其它页面的时候,会发现窗口监听还会继续存活,在别的页面滚动窗口,瀑布流页面还是会不断地加载加载,这个问题就需要使用路由守卫来解决。具体就是设置当路由变化的时候,取消窗口监听事件,当然回到页面的时候,要重新启动窗口监听。
这里有一个坑是beforeRouteLeave千万不要使用箭头函数,否则会获取不到this,感谢知乎大拿的解答。
beforeRouteLeave: function (to, from, next) {
// 获取当前高度
let scrollTop =
document.documentElement.scrollTop || document.body.scrollTop
// 缓存当前高度
sessionStorage.setItem('scrollTop', scrollTop)
// 取消监听窗口变化,否则在别的路径页面也会继续监听
window.removeEventListener('scroll', this.monitorScrollToEnd)
next()
},
第四部分:下一个问题就是,瀑布流页面加载到前几屏了,这时候访问了其它路由,又回来的时候,会发现页面会被重新加载,导致瀑布流又回到了顶部。这个问题也很麻烦。解决的办法是使用keep-alive功能,将这个页面保存在缓存中。同时在离开这个路由的时候,将当前页面的高度缓存到sessionStorage里,等回到瀑布流页面,使用scrollTo直接跳转到缓存的位置,即可无感保留瀑布流的位置。
// 第一步:在route/index.js中,添加meta
const routes = [
{
path: '/',
name: 'HomePage',
component: HomePage,
meta: {
keepAlive: true,
},
},
]
// 第二步:在App.vue中,添加keep-alive标签
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
// 第三步:跳出路由时,获取并缓存当前的高度
beforeRouteLeave: function (to, from, next) {
// 获取当前高度
let scrollTop =
document.documentElement.scrollTop || document.body.scrollTop
// 缓存当前高度
sessionStorage.setItem('scrollTop', scrollTop)
// 取消监听窗口变化,否则在别的路径页面也会继续监听
window.removeEventListener('scroll', this.monitorScrollToEnd)
next()
},
// 第四步:页面被重新激活时,获取缓存中的高度,并scrollTo缓存位置,同时重新启动监听窗口
activated() {
if (sessionStorage.getItem('scrollTop') !== 0) {
window.scrollTo(0, sessionStorage.getItem('scrollTop'))
}
// 监听窗口变化
window.addEventListener('scroll', this.monitorScrollToEnd)
},
做完这些,基本上就是一个比较完善的瀑布流页面了,参考了很多前辈的总结。
如果大家还有疑问,欢迎加我微信交流,微信号:zdplist
评论已关闭