一、iScroll.js
iScroll是一个高性能,资源占用少,无依赖,多平台的javascript滚动插件,PC端、移动端都可以使用,在Vue、Nuxt等工程化项目中也可以使用,应用场景使用广泛。具体使用方法请查阅文档。
二、遇到的问题
1、场景描述
我是在 Nuxt 项目中,封装了一个关键词远程搜索的下拉框组件,输入关键词后,去后台搜索相关数据,下拉框内只能展示5条数据,需要向下滚动来查看其他数据,然后为避免数据量过大,限制每次加载十条数据,通过点击列表最后的加载更多项来加载更多数据。
2、 问题描述以及解决方案
第一个坑:
在引入iScroll.js之后,首先要声明一个全局的IScroll
对象,在 Nuxt 则需要借助 Window 对象,来将声明的IScroll
对象挂载到全局:
// 声明 IScroll 对象 并挂载到全局
window.myScroll = new window.IScroll('#wrapper', {
mouseWheel: true,
scrollbars: true,
startY: scrollY
})
一开始我是在 生命周期的mounted阶段生命并挂载的该对象,运行之后发现,iScroll 不起作用,下拉列表无法滚动,但是通过开发者工具,发现iScroll的相关属性已经出现在dom里了。在经过一系列的排查后,发现是因为,下拉列表渲染的数据是从后台获取的,当我们在 mounted 阶段挂载 IScroll 对象的时候,列表数据还并没有从后台获取到并渲染,页面还并不存在该dom元素,所以 iScroll 获取不到对应的元素,无法分辨出列表的真实长度 , IScroll 对象也就无法起作用,滚动条也不会出现。
解决方案:
通过this.$nextTick()
,当页面渲染完成之后,再去声明和挂载 IScroll 对象:
async getSchoolList () {
// 每次只显示10条相关数据
const params = {
pageIndex: this.pageIndex,
pageSize: 10
}
// 去后台获取数据
const res = await this.$axios.post('/api/', params)
// 待页面渲染完成后,再声明和挂载
this.$nextTick(() => {
window.myScroll = new window.IScroll('#wrapper', {
mouseWheel: true,
scrollbars: true,
startY: scrollY
})
})
},
第二个坑:
踩过前面的坑之后,非常顺利的我又掉进的第二个坑,前面说过了,为了避免数据量过大,对数做了分页处理,每次只加载10条数据,然后问题就出现了,第一次加载10条数据后,iScroll 生成的滚动条没有任何问题。但是再次加载10条数据时,问题出现了,因为此时的列表有20条数据了,列表长度变长,滚动条应该变小。滚动条确实变量小了,但是第一次10条数据时生成的滚动条并没有消失,而是跟20条数据的滚动条出现了重叠,30条、40条也是如此,这就很难受。
解决方案:
一开始我以为是 iScroll.js 本身的缺陷,但觉得如此广泛使用的插件怎么可能会有这么严重的问题呢。经过思考我得出了最终破案了,这是因为使用this.$nextTick()
的问题,我是在这中间声明和挂载的iscroll对象,这样就会导致在每一次重新获取数据并渲染页面的时候,都会重新声明并挂载一个iscroll对象,所以才会出现数据变化时,旧滚动条没有消失的现象。
我想了一个取巧的解决方案,在每次重新渲染的时候,都将先将前面的滚送条给隐藏掉:
async getSchoolList () {
// 每次只显示10条相关数据
const params = {
pageIndex: this.pageIndex,
pageSize: 10
}
const res = await this.$axios.post('/api/', params)
// 当数据增加之后 iscroll 原来的滚动条还存在 所以要将之前的所有滚动条隐藏掉
const n = document.querySelectorAll('.iScrollIndicator')
n.forEach((item) => {
item.style.display = 'none'
})
// 待页面渲染完成后,再声明和挂载 生成一个新的滚动条
this.$nextTick(() => {
window.myScroll = new window.IScroll('#wrapper', {
mouseWheel: true,
scrollbars: true,
startY: scrollY
})
})
},