欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue二次封裝el-select實現(xiàn)下拉滾動加載效果(el-select無限滾動)

 更新時間:2024年04月10日 09:54:46   作者:Tencent?IT  
el-select默認是不支持虛擬滾動的,需要使用第三方插件來實現(xiàn)虛擬滾動功能,下面這篇文章主要給大家介紹了關于Vue二次封裝el-select實現(xiàn)下拉滾動加載效果的相關資料,需要的朋友可以參考下

前言

平時我們做業(yè)務需求的時候,可能會遇到非常大量的數(shù)據(jù),有時候成百上千條,一般后端都會寫一個分頁的接口,只要我們請求的時候加上頁碼參數(shù)即可。

但是在使用element-ui的el-select下拉菜單組件中,官方?jīng)]有提供相應的方法進行多頁加載。

這時候我們可以實現(xiàn)一個Vue的自定義指令,每當使用el-select滾動到列表底部的時候就請求下一頁數(shù)據(jù),來達到下拉滾動加載更多的目的。

實現(xiàn)自定義指令

首先實現(xiàn)一個el-select下拉加載的自定義指令v-loadmore:

// directives.js
import Vue from 'vue'

Vue.directive("loadmore", {
    bind(el, binding, vnode) {
        const SELECTWRAP = el.querySelector(
            ".el-select-dropdown .el-select-dropdown__wrap"
        );
        SELECTWRAP.addEventListener("scroll", function () {
            // scrollTop 這里可能因為瀏覽器縮放存在小數(shù)點的情況,導致了滾動到底部時
            // scrollHeight 減去滾動到底部時的scrollTop ,依然大于clientHeight 導致無法請求更多數(shù)據(jù)
            // 這里將scrollTop向上取整 保證滾到底部時,觸發(fā)調(diào)用
            const CONDITION = this.scrollHeight - Math.ceil(this.scrollTop) <= this.clientHeight;
            // el.scrollTop !== 0 當輸入時,如果搜索結果很少,以至于沒看到滾動條,那么此時的CONDITION計算結果是true,會執(zhí)行bind.value(),此時不應該執(zhí)行,否則搜索結果不匹配
            if (CONDITION && this.scrollTop !== 0) {
                binding.value();
            }
        });
    },
});

代碼說明:

document.querySelector:querySelector() 方法僅僅返回匹配指定選擇器的第一個元素。

Element.scrollHeight:在不使用滾動條的情況下為了適應視口中所用內(nèi)容所需的最小高度(只讀)

警告: 在使用顯示比例縮放的系統(tǒng)上,scrollTop可能會提供一個小數(shù)。

Element.scrollTop:獲取或設置一個元素的內(nèi)容垂直滾動的像素數(shù)。

Element.clientHeight:讀取元素的可見高度(只讀)。

如果元素滾動到底,下面等式返回true,沒有則返回false。

// scrollTop 這里可能因為瀏覽器縮放不等于100%時,存在小數(shù)點的情況,導致了滾動到底部時

// scrollHeight 減去滾動到底部時的scrollTop ,依然大于clientHeight 導致沒有觸發(fā)加載事件

// 這里將scrollTop向上取整 保證滾到底部時,觸發(fā)調(diào)用

// 此判斷不準確: element.scrollHeight - element.scrollTop === element.clientHeight// 使用下面的判斷方式保證

任何縮放都能觸發(fā):

element.scrollHeight - Math.ceil(element.scrollTop) <= element.clientHeight

在項目中全局注冊v-loadmore指令:

// main.js

import directives from './directive.js'
Vue.use(directives)

最后在組件el-select中使用該指令:

<template>
    <el-select v-model="selected" v-loadmore="loadMore">
        <el-option
            v-for="option in options"
            :label="option.label"
            :value="option.value"
            :key="option.value"
        ></el-option>
    </el-select>
</template>

<script>
export default {
    data() {
        return {
            selected: "",
            options: [
                {
                    label: "1",
                    value: 1
                },
                // ... 此處省略多個選項
                {
                    label: "到達底部啦",
                    value: 9
                }
            ]
        };
    },
    methods: {
        loadMore() {
            console.log("more")
        }
    }
};

使用效果如下:

從效果圖可以看出,每當菜單列表滾動到底部時,指令就會調(diào)用傳入的loadMore函數(shù),控制臺隨即打印出 “more”。
注意事項:

傳入的數(shù)組個數(shù)必須大于或者等于8個選項時才能讓el-select組件出現(xiàn)下拉滾動。
列表里不存在滾動時,無法觸發(fā)傳入指令的函數(shù)。

進行二次封裝

滾動到底部調(diào)用函數(shù)的指令已經(jīng)實現(xiàn)了,下面只要調(diào)用接口,把獲取到下一頁的數(shù)據(jù)拼接到當前的數(shù)據(jù)中即可。

接下來把el-select進行二次封裝,封裝成公用的組件之后,傳入必要的參數(shù)就可以在項目中調(diào)用。

首先新建一個文件load-select.vue:

<template>
    <el-select :value="value" v-loadmore="loadMore" @focus="focus" v-bind="$attrs" v-on="$listeners">
        <el-option
            v-for="option in data"
            :label="option[dictLabel]"
            :value="option[dictValue]"
            :key="option.value"
        ></el-option>
    </el-select>
</template>

<script>
export default {
    props: {
        value: {
            type: String,
            default: ""
        },
        // 列表數(shù)據(jù)
        data: {
            type: Array,
            default: () => []
        },
        dictLabel: {
            type: String,
            default: "label"
        },
        dictValue: {
            type: String,
            default: "value"
        },
        // 調(diào)用頁數(shù)的接口
        request: {
            type: Function,
            default: () => {}
        },
        page: {
            type: [Number, String],
            default: 1
        }
    },
    data() {
        return {};
    },
    methods: {
        // 請求下一頁的數(shù)據(jù)
        loadMore() {
            this.request({ page: this.page + 1 })
        },
        // 選中下拉框沒有數(shù)據(jù)時,自動請求第一頁的數(shù)據(jù)
        focus() {
            if (!this.data.length) {
                this.request({page: 1})
            }
        }
    }
};
</script>

在頁面組件中調(diào)用load-select.vue:

<!-- page.vue -->

<template>
    <div class="xxx-page">
        <load-select v-model="selected" :data="data" :page="page" :request="getData"></load-select>
    </div>
</template>

<script>
// 導入該組件
import loadSelect from "@/components/load-select/index";

export default {
    name: "app",
    components: {
        loadSelect
    },
    data() {
        return {
            selected: "",
            page: 1,
            more: true,
            data: []
        };
    },
    methods: {
        // 傳入給load-select組件的函數(shù)
        getData({ page = 1 } = {}) {
            // 輸出頁數(shù)
            console.log(page)
            // 訪問后端接口API
            this.requestAPI({ page }).then(res => {
                this.data = [...this.data, ...res.result]
                this.page = res.page
            });
        },
        // 模擬后端接口的API
        requestAPI({ page = 1, size = 10 } = {}) {
            return new Promise(resolve => {
                let responseData = []
                // 假設總共的數(shù)據(jù)有50條
                let total = 50; 
                for (let index = 1; index <= size; index++) {
                    // serial:處于第幾個元素,就顯示多少序號
                    let serial = index + (page - 1) * size
                    if (serial <= 50) {
                        responseData.push({
                            label: serial,
                            value: serial
                        });
                    }
                }
                // 模擬異步請求,500ms之后返回接口的數(shù)據(jù)
                setTimeout(() => {
                    resolve({
                        total,
                        page,
                        size,
                        result: responseData
                    });
                }, 500);
            });
        }
    }
};
</script>

代碼解析:

首次點擊下拉框時,會觸發(fā)focus事件請求第一頁的數(shù)據(jù),之后只要每次滾動列表到底部,就會自動請求下一頁的數(shù)據(jù)然后拼接到當前的數(shù)組中。

我們來看看效果:

完美!但是在實際使用的過程中,可能會因為接口還來不及返回數(shù)據(jù),然后列表又向下滾動再次觸發(fā)了請求,結果就是返回了兩份相同的數(shù)據(jù)。

現(xiàn)在把接口的延遲調(diào)到2000ms重現(xiàn)這個場景:

在兩次快速滾動到底部的時候,請求的參數(shù)頁數(shù)都是2,如何解決這個問題?可以在加載函數(shù)中加入一個攔截操作,在接口沒有響應之前,不調(diào)用加載函數(shù),不過這樣做要把getData轉(zhuǎn)換成異步函數(shù)的形式。

首先在load-select.vue中的loadMore()中加入一個攔截操作:

<!-- load-select.vue -->

<template>
    ...
</template>

<script>
    // 請求下一頁的數(shù)據(jù)
    methods: {
        loadMore() {
            // 如果 intercept 屬性為 true 則不請求數(shù)據(jù)
            if (this.loadMore.intercept) {
                return 
            }
            this.loadMore.intercept = true
            this.request({ page: this.page + 1 }).then(() => {
                // 接口響應之后才把 intercept 設置為 false
                this.loadMore.intercept = false
            })
        }
    }
</script>

然后在page.vue中的getData()函數(shù)轉(zhuǎn)換成異步函數(shù)的形式:

<template>
    ...
</template>

<script>
    methods: {
        // 傳入給load-select組件的函數(shù)
        getData({ page = 1 } = {}) {
            // 返回 Promise 對象
            return new Promise( resolve => {
                // 訪問后端接口API
                this.requestAPI({ page }).then(res => {
                    this.data = [...this.data, ...res.result]
                    this.page = res.page
                    resolve()
                });
            })
        }, 
    
    }
</script>

現(xiàn)在問題來了:

一般分頁的接口都支持關鍵字的搜索,load-select.vue組件能不能加入關鍵字搜索的功能呢?

關鍵字搜索功能

還好el-select組件支持遠程搜索功能,只要傳入filterable和remote參數(shù),具體的可以查看element-ui的官方文檔。

接下來對load-select.vue進行以下修改:

<!-- load-select.vue -->
<template>
    <el-select
        :value="value"
        v-loadmore="loadMore"
        @focus="focus"
        filterable
        remote
        :filter-method="handleSearch"
        :loading="loading"
        clearable
        v-bind="$attrs"
        v-on="$listeners"
    >
        <el-option
            v-for="option in data"
            :label="option[dictLabel]"
            :value="option[dictValue]"
            :key="option.value"
        ></el-option>
        <!-- 此處加載中的value可以隨便設置,只要不與其他數(shù)據(jù)重復即可 -->
        <el-option v-if="hasMore" disabled label="加載中..." value="-1"></el-option>
    </el-select>
</template>

<script>
export default {
    props: {
        value: {
            default: ""
        },
        // 列表數(shù)據(jù)
        data: {
            type: Array,
            default: () => []
        },
        dictLabel: {
            type: String,
            default: "label"
        },
        dictValue: {
            type: String,
            default: "value"
        },
        // 調(diào)用頁數(shù)的接口
        request: {
            type: Function,
            default: () => {}
        },
        // 傳入的頁碼
        page: {
            type: [Number, String],
            default: 1
        },
        // 是否還有更多數(shù)據(jù)
        hasMore: {
            type: Boolean,
            default: true
        }
    },
    data() {
        return {
            // 存儲關鍵字用
            keyword: "", 
            loading: false
        };
    },
    methods: {
        // 請求下一頁的數(shù)據(jù)
        loadMore() {
            // 如果沒有更多數(shù)據(jù),則不請求
            if (!this.hasMore) {
                return
            }
            // 如果intercept屬性為true則不請求數(shù)據(jù),
            if (this.loadMore.intercept) {
                return
            }
            this.loadMore.intercept = true;
            this.request({
                page: this.page + 1,
                more: true,
                keyword: this.keyword
            }).then(() => {
                this.loadMore.intercept = false
            });
        },
        // 選中下拉框沒有數(shù)據(jù)時,自動請求第一頁的數(shù)據(jù)
        focus() {
            if (!this.data.length) {
                this.request({ page: 1 })
            }
        },
        // 關鍵字搜索
        handleSearch(keyword) {
            this.keyword = keyword
            this.loading = true
            this.request({ page: 1, keyword }).then(() => {
                this.loading = false
            });
        },
        // 刪除選中時,如果請求了關鍵字,則清除關鍵字再請求第一頁的數(shù)據(jù)
        clear() {
            if (this.keyword) {
                this.keyword = ""
                this.request({ page: 1 })
            }
        }
    }
};
</script>

頁面調(diào)用時,getData()請求函數(shù)需要接收keyword和more參數(shù)并進行相應的處理:

<!-- page.vue -->

<template>
    <div class="xxx-page">
        <load-select v-model="selected" :data="data" :page="page" :hasMore="more" :request="getData"></load-select>
    </div>
</template>

<script>
// 導入該組件
import loadSelect from "@/components/load-select/index";

export default {
    name: "app",
    components: {
        loadSelect
    },
    data() {
        return {
            selected: "",
            page: 1,
            more: true,
            data: []
        };
    },
    methods: {
        // 傳入給load-select組件的函數(shù)
        getData({ page = 1, more = false, keyword = "" } = {}) {
            return new Promise(resolve => {
                // 訪問后端接口API
                this.requestAPI({ page, keyword }).then(res => {
                    // 如果是加載更多,則合并之前的數(shù)據(jù)
                    if (more) {
                        this.data = [...this.data, ...res.result]
                    } else {
                        this.data = res.result
                    }

                    this.page = res.page;
                    let { total, page, size } = res
                    // 如果為最后一頁,則設置more為false
                    this.more = page * size < total
                    this.page = page
                    resolve()
                });
            });
        },
        // 模擬后端接口的API
        requestAPI({ page = 1, size = 10, keyword = "" } = {}) {
            return new Promise(resolve => {
                // 如果有 keyword 參數(shù),則返回帶有 keyword 的數(shù)據(jù)
                if (keyword) {
                    setTimeout(() => {
                        resolve({
                            total: 3,
                            page: 1,
                            size: 10,
                            result: [
                                {
                                    label: keyword,
                                    value: 1
                                },
                                {
                                    label: keyword + 1,
                                    value: 2
                                },
                                {
                                    label: keyword + 2,
                                    value: 3
                                }
                            ]
                        })
                    }, 500)
                    return
                }

                let responseData = [];
                // 假設總共的數(shù)據(jù)有50條
                let total = 50; 
                for (let index = 1; index <= size; index++) {
                    // serial:處于第幾個元素,就顯示多少序號
                    let serial = index + (page - 1) * size
                    if (serial <= 50) {
                        responseData.push({
                            label: serial,
                            value: serial
                        });
                    }
                }
                setTimeout(() => {
                    resolve({
                        total,
                        page,
                        size,
                        result: responseData
                    })
                }, 500)
            })
        }
    }
};
</script>

接下來看看搜索關鍵字的效果:

搜索功能也完成啦!

總結

為了適用于大部分的請求接口,因此在設計這個組件的時候只能把請求與組件剝離開來,易用程度不算太高,不過我們可以適當?shù)貍魅胍恍┖唵伪匾膮?shù)去維持基本地使用。

當然,在項目中遇到某些固定的加載請求時,我們也可以對該組件進行再次封裝,具體可以根據(jù)自身的業(yè)務需求進行修改。

到此這篇關于Vue二次封裝el-select實現(xiàn)下拉滾動加載效果(el-select無限滾動)的文章就介紹到這了,更多相關Vue el-select無限滾動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • vue如何自定義配置運行run命令

    vue如何自定義配置運行run命令

    這篇文章主要介紹了vue如何自定義配置運行run命令,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue.js條件渲染和列表渲染以及Vue中key值的內(nèi)部原理

    Vue.js條件渲染和列表渲染以及Vue中key值的內(nèi)部原理

    這篇文章主要介紹了Vue.js條件渲染和列表渲染,以及Vue中key值的內(nèi)部原理,文中有詳細的代碼示例,感興趣的同學可以參考閱讀
    2023-04-04
  • Vue數(shù)據(jù)更新但頁面沒有更新的多種情況問題及解決

    Vue數(shù)據(jù)更新但頁面沒有更新的多種情況問題及解決

    這篇文章主要介紹了Vue數(shù)據(jù)更新但頁面沒有更新的多種情況問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • vue2.0實現(xiàn)檢測無用的代碼并刪除

    vue2.0實現(xiàn)檢測無用的代碼并刪除

    這篇文章主要介紹了vue2.0實現(xiàn)檢測無用的代碼并刪除方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • vue的傳參方式匯總和router使用技巧

    vue的傳參方式匯總和router使用技巧

    這篇文章主要介紹了vue的傳參方式和router使用技巧,本文給大家列舉了好幾種傳參方式,需要的朋友可以參考下
    2018-05-05
  • Vue3 框架Arco Design詳解

    Vue3 框架Arco Design詳解

    Arco Design 作為一款專為 Vue3 打造的企業(yè)級 UI 組件庫,以其豐富的特性和卓越的性能,成為現(xiàn)代 Web 應用開發(fā)者值得關注的選擇,這篇文章主要介紹了Vue3 框架Arco Design詳解,需要的朋友可以參考下
    2024-08-08
  • vue2中l(wèi)ess的安裝以及使用教程

    vue2中l(wèi)ess的安裝以及使用教程

    less是css預處理器,對原先css進行了擴展和補充,下面這篇文章主要給大家介紹了關于vue2中l(wèi)ess的安裝以及使用的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-12-12
  • Vue.js彈出模態(tài)框組件開發(fā)的示例代碼

    Vue.js彈出模態(tài)框組件開發(fā)的示例代碼

    本篇文章主要介紹了Vue.js彈出模態(tài)框組件開發(fā)的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • Vue.js學習之計算屬性

    Vue.js學習之計算屬性

    Vue.js 的內(nèi)聯(lián)表達式非常方便,但它最合適的使用場景是簡單的布爾操作或字符串拼接。如果涉及更復雜的邏輯,你應該使用計算屬性。下面這篇文章主要給大家介紹了Vue.js中的計算屬性,需要的朋友可以參考借鑒,一起來看看吧。
    2017-01-01
  • vue如何動態(tài)配置ip與端口

    vue如何動態(tài)配置ip與端口

    這篇文章主要介紹了vue如何動態(tài)配置ip與端口,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09

最新評論