這篇文章主要介紹了Vue.js怎么實(shí)現(xiàn)多條件篩選、搜索、排序及分頁的表格功能,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
十載的云夢(mèng)網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營銷型網(wǎng)站的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整云夢(mèng)建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)公司從事“云夢(mèng)網(wǎng)站設(shè)計(jì)”,“云夢(mèng)網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
Vue是一套用于構(gòu)建用戶界面的漸進(jìn)式JavaScript框架,Vue與其它大型框架的區(qū)別是,使用Vue可以自底向上逐層應(yīng)用,其核心庫只關(guān)注視圖層,方便與第三方庫和項(xiàng)目整合,且使用Vue可以采用單文件組件和Vue生態(tài)系統(tǒng)支持的庫開發(fā)復(fù)雜的單頁應(yīng)用。
需求分析
還是先從需求入手,想想實(shí)現(xiàn)這樣一個(gè)功能需要注意什么、大致流程如何、有哪些應(yīng)用場(chǎng)景。
表格本身是一種非常常用的組件,用于展示一些復(fù)雜的數(shù)據(jù)時(shí)表現(xiàn)很好。
當(dāng)數(shù)據(jù)比較多時(shí),我們需要提供一些篩選條件,讓用戶更快列出他們關(guān)注的數(shù)據(jù)。
除了預(yù)設(shè)的一些篩選條件,可能還需要一些個(gè)性化的輸入搜索功能。
對(duì)于有明顯順序關(guān)系的數(shù)據(jù),例如排名、價(jià)格等,還需要排序功能方便快速倒置數(shù)據(jù)。
如果數(shù)據(jù)量較大,需要分頁展示表格。
需要注意的是,上述的這些需求其實(shí)和大部分?jǐn)?shù)據(jù)庫提供的功能是非常一致的,而且由于數(shù)據(jù)庫擁有索引等優(yōu)化方式以及服務(wù)器更好的性能,更加適合處理這些需求。不過現(xiàn)在流行的前后端分離,也是希望讓客戶端在合理的范圍內(nèi),更多的分擔(dān)服務(wù)器端的壓力,所以當(dāng)找到一個(gè)平衡時(shí),在前端處理適量的需求是正確的選擇。
接下來就嘗試用vue完成這些需求吧。
完成Table.vue
因?yàn)檫@樣一個(gè)多功能表格可能會(huì)應(yīng)用在多個(gè)項(xiàng)目中,所以設(shè)計(jì)思路上盡量將表格相關(guān)的內(nèi)容放在Table.vue組件中,減少耦合,方便復(fù)用。
獲取測(cè)試數(shù)據(jù)
為了更好的對(duì)比前端實(shí)現(xiàn)以上需求的利與弊,我們需要一份較大較復(fù)雜的測(cè)試數(shù)據(jù)。幸運(yùn)的是我之前的一個(gè)項(xiàng)目中,設(shè)計(jì)的一份API正好滿足這一需求,數(shù)據(jù)為魔獸世界競(jìng)技場(chǎng)的天梯排行API,目前這個(gè)API處于開放狀態(tài),接口詳見Myarena介紹。
與上一篇教程相類似,還是新建一個(gè)api文件夾以及一個(gè)arena.js用于管理API接口。再在App.vue中引入arena.js,在created階段獲取數(shù)據(jù)。作為一個(gè)demo,我們只獲取region為CN、laddar為3v3的數(shù)據(jù),不過只要將兩個(gè)參數(shù)通過v-model綁定給對(duì)應(yīng)的表單控件,就能很輕松的實(shí)現(xiàn)不同地區(qū)數(shù)據(jù)的切換。
引入table.vue組件
如之前所說,思路上我們希望減少table組件與外部環(huán)境的耦合,所以我們給Table.vue設(shè)置一個(gè)props屬性rows,用于獲取App.vue取回的數(shù)據(jù)。在App.vue中注冊(cè)table組建時(shí)要注意,命名不能用默認(rèn)的table,所以注冊(cè)為vTable,就能用<v-table>標(biāo)簽引入table組件了。
目前為止,我們的App.vue完成了它所有的功能,代碼如下:
<template> <div class="container"> <v-table :rows="rows"></v-table> </div> </template> <script> import arena from './api/arena' import vTable from './components/Table' export default { components: { vTable }, data () { return { region: 'CN', laddar: '3v3', rows: [] } }, methods: { getLaddar (region, laddar) { arena.getLaddar(region, laddar, (err, val) => { if (!err) { this.rows = val.rows } }) } }, created () { this.getLaddar(this.region, this.laddar) } } </script>
實(shí)際的App.vue中還有一個(gè)獲取API中的最后更新時(shí)間的操作,以及一些css設(shè)置,篇幅考慮這里進(jìn)行了省略,對(duì)完整代碼有興趣的可以移步文章末尾的Github倉庫。
基礎(chǔ)布局
Table.vue的template中主要為3部分,分別是用于搜索、篩選和分頁的表單控件、用于排序表格的表頭thead以及用于展示數(shù)據(jù)的tbody。
首先來完成tbody的部分,基本思路就是用v-for遍歷數(shù)據(jù),再通過模板填入,需要注意以下幾個(gè)重點(diǎn):
返回的數(shù)據(jù)不一定完全符合要求。例如我希望實(shí)現(xiàn)通過勝率排序,但數(shù)據(jù)中只包含了勝負(fù)場(chǎng)數(shù),需要先計(jì)算一次。2. 數(shù)據(jù)中用于表現(xiàn)玩家職業(yè)的數(shù)據(jù)為classId這個(gè)屬性,但在實(shí)際項(xiàng)目中我想要用各職業(yè)的icon展示職業(yè),所以我在utils.js中實(shí)現(xiàn)了各一個(gè)classIdToIcon的工具函數(shù),用于映射classId至sprite圖中的background-position。
以上兩點(diǎn)說明我們最好不要遍歷props獲得的rows這一原始數(shù)據(jù)。因此另建了一個(gè)computed屬性players,并在其中完成了前期處理,我把所有的前期處理放在了handleBefore中。
由于即將使用的各種filters操作比較復(fù)雜,所以在handlebefore中進(jìn)行了console.log('before handle'),方便我們驗(yàn)證handlebefore在什么階段被執(zhí)行了。
完成布局之后,目前Table.vue中的重點(diǎn)代碼如下:
<template> <tbody> <tr v-for="player of players :class="player.factionId? 'horde':'alliance'"> <th>{{ player.ranking }}</th> <th>{{ player.rating }}</th> <th> <span class="class" :style="{ backgroundImage: 'url(http://7xs8rx.com1.z0.glb.clouddn.com/class.png)', backgroundPosition: player.classIcon }"></span> {{ player.name }} </th> <th>{{ player.realmName }}</th> <th> <bar :win="player.weeklyWins" :loss="player.weeklyLosses"></bar> </th> <th> <bar :win="player.seasonWins" :loss="player.seasonLosses"></bar> </th> </tr> </tbody> </template> <script> import Bar from './Bar' import { classIdToIcon } from '../assets/utils' export default { components: { Bar }, props: { rows: { type: Array, default: () => { return [] } } }, computed: { players () { this.rows = this.handleBefore(this.rows) return this.rows } }, methods: { handleBefore (arr) { console.log('before handle') if (this.rows[0]) { arr.forEach((item) => { if (item.weeklyWins === 0 && item.weeklyLosses === 0) { item.weeklyRate = -1 } else { item.weeklyRate = item.weeklyWins / (item.weeklyWins + item.weeklyLosses) } if (item.seasonWins === 0 && item.seasonLosses === 0) { item.seasonRate = -1 } else { item.seasonRate = item.seasonWins / (item.seasonWins + item.seasonLosses) } item.classIcon = classIdToIcon(item.classId) }) } return arr } } } </script>
可以看到,我還引入了一個(gè)Bar.vue組件用于展示勝率,這是因?yàn)槲蚁M罱K的實(shí)際效果是這樣的:
一開始我直接在勝率所在的<th>標(biāo)簽中進(jìn)行各種操作,但可想而知在進(jìn)行一些邊界情況的判斷時(shí),會(huì)出現(xiàn)各種含有player.weeklyWins, player.weeklyLosses等長(zhǎng)命名變量的三元表達(dá)式。本來是出于便利考慮,卻反而導(dǎo)致代碼難以維護(hù)。因此新建了個(gè)一個(gè)bar組件,將勝負(fù)傳入組件中,在bar組件內(nèi)部用更語義化的方式實(shí)現(xiàn),Bar.vue中模板部分代碼如下:
<template> <div class="clear-fix"> <span v-if="!hasGame || win / total > 0" : :class="hasGame? '':'no-game'" class="win-bar"> {{ hasGame? (100 * win / total).toFixed(1) + '%':'無場(chǎng)次' }} </span> <span v-if="loss / total > 0" : class="loss-bar"> {{ win === 0? '0%':'' }} </span> </div> </template>
更好理解和維護(hù)了,不是嗎?
在使用vue的過程中,需要注意的是框架中許多方法其實(shí)在內(nèi)部最終是殊途同歸。
例如我們可以直接在元素中執(zhí)行一些對(duì)數(shù)據(jù)的操作,例如@click="show = !show",同樣的我們也可以對(duì)事件綁定方法,再在方法中操作數(shù)據(jù),例如@click="toggle", toggle () { this.show = !this.show }。還比如我們可以用computed屬性和watch屬性實(shí)現(xiàn)很多相同的功能,接下來還將用computed去實(shí)現(xiàn)和filters相同的功能。
vue設(shè)計(jì)中的靈活性讓我們有了更多的可能性,但在學(xué)習(xí)時(shí),應(yīng)該以搞明白不同方式在不同場(chǎng)景中的優(yōu)劣為目標(biāo),實(shí)際運(yùn)用時(shí)選擇最好的那一種。
用filters實(shí)現(xiàn)需求
在例子中,players實(shí)際是一個(gè)5000條數(shù)據(jù)的數(shù)組,在不做任何處理時(shí),將直接渲染出5000個(gè)<tr>,所以先趕緊過濾吧!
對(duì)于v-for循環(huán),vue中提供了3中filters過濾數(shù)組,分別為filterBy, orderBy, limitBy,其功能對(duì)應(yīng)了搜索/篩選、排序和分頁,實(shí)現(xiàn)分別是使用了Array.filter, Array.sort(), Array.slice()。
這三種filters在使用時(shí)非常便利,只要在v-for后用|分離再添加對(duì)應(yīng)的filters即可,這3中filter的具體參數(shù)可以查看官方API,這里不多做贅述。
需要注意的是,實(shí)際的過程是先將被遍歷的數(shù)組(例子中的players)依次通過過濾器,再將最后一個(gè)過濾器返回的數(shù)組進(jìn)行v-for操作。
因此,filters放置的順序是需要根據(jù)需求來調(diào)整的,也因?yàn)槊糠N過濾器的內(nèi)部實(shí)現(xiàn)效率不同,所以在需求優(yōu)先級(jí)不明顯時(shí),應(yīng)該以效率為優(yōu)先。
注意:實(shí)際測(cè)試時(shí),發(fā)現(xiàn)不論怎么過濾數(shù)組,handleBefore方法都沒有再次執(zhí)行,也就是說players數(shù)組并沒有被改動(dòng)過。
例如在我的例子中,我希望可以篩選出名字或者服務(wù)器包含了我所輸入內(nèi)容的玩家,并且將他們按照某種方式排序,最后的結(jié)果每頁只顯示20條。那么顯然剪切數(shù)組永遠(yuǎn)應(yīng)該放在最后一步,而排序和過濾在需求中沒有明顯的優(yōu)先級(jí)。但是大部分情況下,sort的效率都要低于filter,所以我們先進(jìn)行filter,減少數(shù)組長(zhǎng)度,再sort。
有了這一思路之后,用于v-for的<tr>變?yōu)椋?/p>
<tr v-for="player of players | filterBy query in 'name' 'realmName' | orderBy sort.key sort.val | limitBy 20 (page-1)*20" :class="player.factionId? 'horde':'alliance'">
這里直接將各個(gè)變量動(dòng)態(tài)化,再通過Table.vue中的input綁定v-model以及表頭thead綁定@click事件來改變篩選的條件,就已經(jīng)實(shí)現(xiàn)了大部分的搜索、過濾、分頁功能。
表頭改變sort排序我是通過以下代碼實(shí)現(xiàn)的,方式可能不是太好,特此列出:
<thead> <tr> <th @click="sort = {key: 'ranking', val: -sort.val}">排名</th> <th @click="sort = {key: 'rating', val: -sort.val}">分?jǐn)?shù)</th> <th>資料</th> <th>服務(wù)器</th> <th @click="sort = {key: 'weeklyRate', val: -sort.val}">本周戰(zhàn)績(jī)</th> <th @click="sort = {key: 'seasonRate', val: -sort.val}">賽季戰(zhàn)績(jī)</th> </tr> </thead>
可以看到,通過vue的filters功能,已經(jīng)可以輕松完成我們的大部分功能,代碼量極少。這也是vue2.0前瞻發(fā)布之后,提出廢棄部分filters功能后許多人反應(yīng)較為強(qiáng)烈的原因。但是如同作者在改動(dòng)說明中所說,filters對(duì)于初學(xué)者來說不易理解,并且filters的功能都可以用computed屬性進(jìn)行更靈活、更好把控的實(shí)現(xiàn)。而且在一些復(fù)雜條件下,堆疊過濾器會(huì)造成一些額外的復(fù)雜性以及不方便之處。
那么何為復(fù)雜條件呢?例如我增添兩個(gè)需求,一是按職業(yè)篩選玩家,而是篩選出一定分?jǐn)?shù)以上的玩家,那么后者用filterBy就不太好實(shí)現(xiàn)了。我們需要將對(duì)分?jǐn)?shù)段的過濾放在filters之前進(jìn)行,但又要注意不破壞players數(shù)組本身。在實(shí)際完成時(shí),會(huì)發(fā)現(xiàn)這個(gè)過程還是比較糾結(jié)的。
除此之外,我們還會(huì)發(fā)現(xiàn)分頁中最重要的一個(gè)信息——總頁數(shù)我們獲取不到。因?yàn)関ue并沒有把一串過濾管道中產(chǎn)出的最終用于v-for的數(shù)組暴露出來,所以我們無法獲得這個(gè)實(shí)際被循環(huán)的數(shù)組的長(zhǎng)度。
在實(shí)際hack這些需求時(shí),發(fā)現(xiàn)很容易與filters的執(zhí)行順序發(fā)生沖突,因此決定重新用computed屬性來實(shí)現(xiàn)一遍所有功能,不借助自帶的filters。
當(dāng)然,在這一段的前半部分中,我們顯而易見的感受到了來自filters的便利性。如果需求中filters可以滿足,那么在1.x版本中使用filters還是十分明智的!
用computed屬性完成需求
在Github倉庫中,我用Table.vue.bak文件儲(chǔ)存了之前一段中用filters實(shí)現(xiàn)的代碼,方便與我們接下里的實(shí)現(xiàn)進(jìn)行比較。
首先整理一下用computed屬性來實(shí)現(xiàn)的思路:
首先要實(shí)現(xiàn)filterBy, orderBy, limitBy這三個(gè)filter的功能,上文中已經(jīng)提到了他們的內(nèi)部實(shí)現(xiàn),所以分別用Array.filter, Array.sort和Array.slice重寫一遍并不復(fù)雜。
說是computed屬性實(shí)現(xiàn),其實(shí)也還是只有players這個(gè)computed屬性,只是在其內(nèi)部執(zhí)行了所有的過濾動(dòng)作,我們實(shí)際是把各種過濾器的邏輯放置在各個(gè)method中。
不建議把各個(gè)過濾method寫的過于抽象,因?yàn)榫褪莾?nèi)置filters高度抽象導(dǎo)致一些特殊需求無法實(shí)現(xiàn),所以不妨就以最針對(duì)性的方式:一個(gè)method對(duì)應(yīng)一種過濾。
在執(zhí)行各個(gè)過濾method時(shí),依然有最初提到的順序帶來的效率問題。因?yàn)関ue牽一發(fā)而動(dòng)全身的特性,任何一個(gè)過濾條件改變時(shí),所有過濾method都會(huì)執(zhí)行一遍,所以盡快用高效的過濾器縮短數(shù)組長(zhǎng)度顯得更為重要。
我嘗試過通過watch屬性實(shí)現(xiàn)最小化method調(diào)用,但無奈功力不夠沒能實(shí)現(xiàn)。同時(shí)我也認(rèn)為前端處理大量數(shù)據(jù)的情況很少見,并且用第4點(diǎn)中的數(shù)據(jù)進(jìn)行優(yōu)化后,執(zhí)行效率不算太低,所以沒必要在這個(gè)方面做過多糾結(jié)。真有性能瓶頸時(shí),從服務(wù)器端尋求解決會(huì)更簡(jiǎn)單。
注意:在實(shí)現(xiàn)各種過濾method時(shí),建議閱讀vue中filterBy, orderBy, limitBy三部分的實(shí)現(xiàn)源碼,其本身對(duì)于數(shù)組的操作就有一些優(yōu)化,非常值得學(xué)習(xí)。在一些特殊情況中,例如數(shù)組中大量相等值時(shí),過于簡(jiǎn)單的sort function會(huì)導(dǎo)致執(zhí)行步數(shù)激增,vue中的一些處理都予以了避免。
根據(jù)需求目標(biāo),我設(shè)置了以下這些method(順序即為執(zhí)行順序):
classFilter:過濾玩家職業(yè),通過item.classId === this.class進(jìn)行判斷,this.class綁定的是一個(gè)select控件。
queryFilter:匹配玩家姓名中的字段,通過item.name.indexOf(this.query)判斷,this.query則綁定一個(gè)input控件。
ratingFilter:篩選玩家分?jǐn)?shù)段,通過item.rating >= this.rating進(jìn)行判斷,this.rating綁定了一個(gè)類型為range的input控件,range的范圍則是用computed屬性進(jìn)行計(jì)算。
sortTable:因?yàn)锳rray.sort進(jìn)行的步數(shù)較多,所以放在數(shù)組被上述3個(gè)method處理的較短后進(jìn)行。
paginate:所有過濾操作完畢之后,就可以進(jìn)行分頁了。在使用Array.slice()之前,先將數(shù)組的長(zhǎng)度傳給this.total儲(chǔ)存起來,用于在分頁后計(jì)算總的頁數(shù)。
除了以上幾個(gè)過濾method以外,當(dāng)然也還有handleBefore方法對(duì)數(shù)組進(jìn)行前期處理。但是由于players每次都會(huì)重新計(jì)算,所以為了放止handleBefore被重復(fù)執(zhí)行,應(yīng)該加上一定的判斷條件,例如handleBefore添加的屬性是否已經(jīng)存在了等等。同時(shí),還可以把一些不需要在過濾之前執(zhí)行的動(dòng)作從handleBefore中拿出,例如例子中的classId轉(zhuǎn)換為Icon,可以在過濾之后對(duì)最終要展示的數(shù)據(jù)進(jìn)行即可,減少一些步數(shù)。所以又設(shè)置了一個(gè)handleAfter方法,用于在分頁完成之后進(jìn)行后續(xù)操作,當(dāng)然在handleAfter中也可能重復(fù)執(zhí)行,所以如果執(zhí)行的操作消耗很大,建議同樣添加判斷,避免重復(fù)執(zhí)行。
在例子代碼中,我在每個(gè)方法中都統(tǒng)計(jì)了執(zhí)行的步數(shù),實(shí)際結(jié)果顯示設(shè)置一個(gè)合理的過濾順序可以避免一些性能問題,結(jié)果如下:
可以看出初始化時(shí),在沒有任何過濾的情況下,sort的步數(shù)較高。而一旦添加了一些過濾條件之后,順位靠后的filter和sort的步數(shù)都會(huì)大幅度減少。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Vue.js怎么實(shí)現(xiàn)多條件篩選、搜索、排序及分頁的表格功能”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!
文章名稱:Vue.js怎么實(shí)現(xiàn)多條件篩選、搜索、排序及分頁的表格功能
URL地址:http://www.rwnh.cn/article16/jjshdg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、網(wǎng)站導(dǎo)航、建站公司、用戶體驗(yàn)、網(wǎng)站策劃、域名注冊(cè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)