vue 中的自定義指令是對底層 dom 進行操作,下面以實現(xiàn)滾動到底部加載數(shù)據(jù),實現(xiàn)無限加載來介紹如何自定義一個簡單的指令。
無限加載的原理是通過對滾動事件的監(jiān)聽,每一次滾動都要獲取到已滾動的距離,如果滾動的距離加上瀏覽器窗口高度,會大于等于內(nèi)容高度,就觸發(fā)函數(shù)加載數(shù)據(jù)。
先介紹不使用 vue 的情況如何實現(xiàn)無限加載。
不使用框架
首先是html:
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 39 | <!DOCTYPE html>< html lang = "en" >
< head >< meta charset = "UTF-8" >
< title >實現(xiàn)滾動加載</ title >
< style >
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
li, ul {
list-style: none;
}
.container {
width: 980px;
margin: 0 auto;
}
.news__item {
height: 80px;
margin-bottom: 20px;
border: 1px solid #eee;
}</ style >
</ head >
< body >
< div class = "container" >
< ul class = "news" id = "news" >
< li class = "news__item" >1、hello world</ li >
< li class = "news__item" >2、hello world</ li >
< li class = "news__item" >3、hello world</ li >
< li class = "news__item" >4、hello world</ li >
< li class = "news__item" >5、hello world</ li >
< li class = "news__item" >6、hello world</ li >
< li class = "news__item" >7、hello world</ li >
< li class = "news__item" >8、hello world</ li >
< li class = "news__item" >9、hello world</ li >
< li class = "news__item" >10、hello world</ li >
</ ul >
</ div >
</ body >
</ html >
|
打開瀏覽器,調(diào)整下瀏覽器窗口高度,讓頁面可以滾動。
先了解三個變量
- document.body.scrollTop 滾動條滾動的距離
- window.innerHeight 瀏覽器窗口高度
- document.body.clientHeight 內(nèi)容高度
對應(yīng)上面的原理就是
1 2 3 4 5 6 7 8 9 10 | window.addEventListener( 'scroll' , function () {
var scrollTop = document.body.scrollTop;
if (scrollTop + window.innerHeight >= document.body.clientHeight) {
// 觸發(fā)加載數(shù)據(jù)
loadMore();
}
});
function loadMore() {
console.log( '加載數(shù)據(jù)' )'
}
|
loadMore() 函數(shù)就是從接口獲取到數(shù)據(jù),組裝 html,插入到原先到節(jié)點后面。
1 2 3 4 5 6 7 8 9 10 11 12 | // 表示列表的序號
var index = 10;
function loadMore() {
var content = '' ;
for ( var i=0; i< 10; i++) {
content += '<li class="news__item">' +(++index)+ '、hello world</li>'
}
var node = document.getElementById( 'news' );
// 向節(jié)點內(nèi)插入新生成的數(shù)據(jù)
var oldContent = node.innerHTML;
node.innerHTML = oldContent+content;
}
|
這樣就實現(xiàn)了無限加載。
在 vue 中使用指令實現(xiàn)
為什么要用指令實現(xiàn)呢?好像只有指令是可以獲取到底層 dom 的?而實現(xiàn)無限加載,是需要拿到內(nèi)容高度的。
首先初始化一個項目,添加一個組件,用來顯示列表。
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 | // components/Index.vue
<template>
<div>
<ul class= "news" >
<li class= "news__item" v- for = "(news, index) in newslist" >
{{index}}-{{news.title}}
</li>
</ul>
</div>
</template>
<style>
.news__item {
height: 80px;
border: 1px solid #ccc;
margin-bottom: 20px;
}
</style>
<script>
export default {
data(){
return {
newslist: [
{title: 'hello world' },
{title: 'hello world' },
{title: 'hello world' },
{title: 'hello world' },
{title: 'hello world' },
{title: 'hello world' },
{title: 'hello world' },
{title: 'hello world' },
{title: 'hello world' },
{title: 'hello world' }
]
}
}
}
</script>
|
OK,現(xiàn)在開始編寫指令。從傳統(tǒng)實現(xiàn)中,我們了解到要注冊對滾動事件對監(jiān)聽,同時拿到內(nèi)容高度。
1 2 3 4 5 6 7 8 9 10 11 | directives: {
scroll: {
bind: function (el, binding){
window.addEventListener( 'scroll' , ()=> {
if (document.body.scrollTop + window.innerHeight >= el.clientHeight) {
console.log( 'load data' );
}
})
}
}
}
|
首先是在組件內(nèi)注冊了 scroll 指令,然后在指令第一次綁定到組件時,也就是對應(yīng)著 bind鉤子,注冊滾動監(jiān)聽。
鉤子函數(shù)就是一些生命周期改變時會調(diào)用的函數(shù)。bind 在第一次綁定到組件時調(diào)用、unbind 在指令與組件解綁時調(diào)用。
還可以注意到 bind 對應(yīng)到函數(shù)有兩個參數(shù),el 和 binding,這是鉤子函數(shù)參數(shù),比如 el 對應(yīng)綁定的節(jié)點,binding 有很多數(shù)據(jù),比如傳給指令的參數(shù)等。
下面的el.clientHeight就是表示獲取綁定指令的這個節(jié)點的內(nèi)容高度。
和之前一樣,判斷滾動的高度加上窗口高度是否大于內(nèi)容高度。
綁定指令到節(jié)點上:
1 2 3 4 5 6 7 8 9 | <template>
<div v-scroll= "loadMore" >
<ul class= "news" >
<li class= "news__item" v- for = "(news, index) in newslist" >
{{index}}-{{news.title}}
</li>
</ul>
</div>
</template>
|
可以看到給指令傳了一個值,這個值就是加載數(shù)據(jù)的函數(shù):
1 2 3 4 5 6 7 8 9 | methods: {
loadMore() {
let newAry = [];
for (let i = 0; i < 10; i++) {
newAry.push({title: 'hello world' })
}
this .newslist = [... this .newslist, ...newAry];
}
}
|
當然,現(xiàn)在在滾動到底部時,只會打印load data,只要把這里改成調(diào)用函數(shù)就OK了:
1 2 3 4 5 6 | window.addEventListener( 'scroll' , ()=> {
if (document.body.scrollTop + window.innerHeight >= el.clientHeight) {
let fnc = binding.value;
fnc();
}
})
|
v-scroll="loadMore"的 loadMore可以在鉤子函數(shù)參數(shù)的 binding 上拿到。
至此,一個簡單的指令就完成了。
優(yōu)化
上面的例子并沒有真正從接口獲取數(shù)據(jù),所以存在一個隱藏的 bug:當接口響應(yīng)很慢的情況,滾動到底部正在加載數(shù)據(jù)時,稍微滾動一下仍會觸發(fā)獲取數(shù)據(jù)函數(shù),這會造成同時請求多次接口,一次性返回大量數(shù)據(jù)。
解決辦法是添加一個全局變量 scrollDisable,當?shù)谝淮斡|發(fā)加載數(shù)據(jù)函數(shù)時,將該值設(shè)置為 true,根據(jù)該值判斷是否要執(zhí)行加載函數(shù)。
以普通實現(xiàn)為例:
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 | var scrollDisable = false ;
window.addEventListener( 'scroll' , function () {
var scrollTop = document.body.scrollTop;
if (scrollTop + window.innerHeight >= document.body.clientHeight) {
// 觸發(fā)加載數(shù)據(jù)
if (!scrollDisable) {
//
loadMore();
}
}
});
// 表示列表的序號
var index = 10;
function loadMore() {
// 開始加載數(shù)據(jù),就不能再次觸發(fā)這個函數(shù)了
scrollDisable = true ;
var content = '' ;
for ( var i=0; i< 10; i++) {
content += '<li class="news__item">' +(++index)+ '、hello world</li>'
}
var node = document.getElementById( 'news' );
// 向節(jié)點內(nèi)插入新生成的數(shù)據(jù)
var oldContent = node.innerHTML;
node.innerHTML = oldContent+content;
// 插入數(shù)據(jù)完成后
scrollDisable = false ;
}
|
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
原文鏈接:http://www.jianshu.com/p/47c086dab73d 如對本文有疑問,請?zhí)峤坏浇涣魃鐓^(qū),廣大熱心網(wǎng)友會為你解答??! 點擊進入社區(qū)
|