updated: 2021/7/18
無限スクロールは読み込むほどDOMが大きくなるので画面外のDOMは極力消して軽量化する必要がある。
より簡単な方法があったので加筆
vuetify の intersection-observer ディレクティブを使うと楽
環境:
vue@2.6.12
vuetify@2.5.6
vue-infinite-loading@2.4.5
参考:
New Way)
wrapper v-intersect=onItersect
v-card v-if="visible"
mock v-else
onIntersect (entries, observer) {
this.visible = entries[0].isIntersecting
},
wrapperとなるエレメントで v-intersect するだけでDOM消しを実装できる
下記方法のviewport の計算等は不要
注意)
ページを遷移させたときに無限スクロールの該当list の値を変化させると結局再計算コストが大きくかかるのでlistの値はページ遷移しても保持する
以下old way
方針1
listからviewportに表示されている部分をcomputedで抽出し、viewport外に空エレメントを挿入
>>> うまく滑らかな表示ができなかった
方針2
画面外に出たらv-ifで消し、v-elseで空エレメントを挿入。
兄弟コンポーネント間で同一のメソッド(getCurrentDistance)をコールする方法は下記参照
data() {
return {
scrollWatcher: 0,
initScrollPassive: false
}
}
created() {
window.addEventListner('scroll', this.detectScroll)
}
methods: {
detectScroll() {
this.scrollWatcher = 1
// スクロールするたびにgetCurrentDistanceすると過剰にメソッドをコールするので頻度を調整
if(!this.initScrollPassive) {
this.initScrollPassive = true
clearTimeout(this.passiveScrollStatus())
}
},
passiveScrollStatus() {
setTimeout(() => {
this.scrollPassive = false
this.scrollWatcher = 0
}, 300)
}
<template>
<div id="targetElement">
<div v-if=visible>
// list rendering content
</div>
<div v-else>
// empty element
</div>
</div>
</template>
props: ['scrollWatcher']
data() {
return {
el: '',
visible: true,
viewportHeight: ''
}
}
created() {
this.el = doscument.getElementById("targetElement")
this.viewportHeight = window.innerHeight
}
watch() {
'scrollWatcher'() {
if(this.scrollWatcher) {
this.getCurrentDistance()
}
}
},
methods: {
}
Need Fix
scrollWatcher の値が変化したときすべての子コンポーネントでgetCurrentDistaneを処理する必要無く、viewport 周辺のコンポーネントのみコールすればよい.
スクロール速度によってはviewport上に空エレメントを表示した状態で処理が止まってしまうことがあるのでスクロールイベントの取得法を見直す必要あり。