中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue.js怎么實現多條件篩選、搜索、排序及分頁的表格功能

發布時間:2021-04-25 14:43:30 來源:億速云 閱讀:1576 作者:小新 欄目:web開發

這篇文章主要介紹了Vue.js怎么實現多條件篩選、搜索、排序及分頁的表格功能,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

vue是什么

Vue是一套用于構建用戶界面的漸進式JavaScript框架,Vue與其它大型框架的區別是,使用Vue可以自底向上逐層應用,其核心庫只關注視圖層,方便與第三方庫和項目整合,且使用Vue可以采用單文件組件和Vue生態系統支持的庫開發復雜的單頁應用。

需求分析

還是先從需求入手,想想實現這樣一個功能需要注意什么、大致流程如何、有哪些應用場景。

  • 表格本身是一種非常常用的組件,用于展示一些復雜的數據時表現很好。

  • 當數據比較多時,我們需要提供一些篩選條件,讓用戶更快列出他們關注的數據。

  • 除了預設的一些篩選條件,可能還需要一些個性化的輸入搜索功能。

  • 對于有明顯順序關系的數據,例如排名、價格等,還需要排序功能方便快速倒置數據。

  • 如果數據量較大,需要分頁展示表格。

需要注意的是,上述的這些需求其實和大部分數據庫提供的功能是非常一致的,而且由于數據庫擁有索引等優化方式以及服務器更好的性能,更加適合處理這些需求。不過現在流行的前后端分離,也是希望讓客戶端在合理的范圍內,更多的分擔服務器端的壓力,所以當找到一個平衡時,在前端處理適量的需求是正確的選擇。

接下來就嘗試用vue完成這些需求吧。

完成Table.vue

因為這樣一個多功能表格可能會應用在多個項目中,所以設計思路上盡量將表格相關的內容放在Table.vue組件中,減少耦合,方便復用。

獲取測試數據

為了更好的對比前端實現以上需求的利與弊,我們需要一份較大較復雜的測試數據。幸運的是我之前的一個項目中,設計的一份API正好滿足這一需求,數據為魔獸世界競技場的天梯排行API,目前這個API處于開放狀態,接口詳見Myarena介紹。

與上一篇教程相類似,還是新建一個api文件夾以及一個arena.js用于管理API接口。再在App.vue中引入arena.js,在created階段獲取數據。作為一個demo,我們只獲取region為CN、laddar為3v3的數據,不過只要將兩個參數通過v-model綁定給對應的表單控件,就能很輕松的實現不同地區數據的切換。

引入table.vue組件

如之前所說,思路上我們希望減少table組件與外部環境的耦合,所以我們給Table.vue設置一個props屬性rows,用于獲取App.vue取回的數據。在App.vue中注冊table組建時要注意,命名不能用默認的table,所以注冊為vTable,就能用<v-table>標簽引入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>

實際的App.vue中還有一個獲取API中的最后更新時間的操作,以及一些css設置,篇幅考慮這里進行了省略,對完整代碼有興趣的可以移步文章末尾的Github倉庫。

基礎布局

Table.vue的template中主要為3部分,分別是用于搜索、篩選和分頁的表單控件、用于排序表格的表頭thead以及用于展示數據的tbody。

首先來完成tbody的部分,基本思路就是用v-for遍歷數據,再通過模板填入,需要注意以下幾個重點:

  • 返回的數據不一定完全符合要求。例如我希望實現通過勝率排序,但數據中只包含了勝負場數,需要先計算一次。2. 數據中用于表現玩家職業的數據為classId這個屬性,但在實際項目中我想要用各職業的icon展示職業,所以我在utils.js中實現了各一個classIdToIcon的工具函數,用于映射classId至sprite圖中的background-position。

  • 以上兩點說明我們最好不要遍歷props獲得的rows這一原始數據。因此另建了一個computed屬性players,并在其中完成了前期處理,我把所有的前期處理放在了handleBefore中。

  • 由于即將使用的各種filters操作比較復雜,所以在handlebefore中進行了console.log('before handle'),方便我們驗證handlebefore在什么階段被執行了。

完成布局之后,目前Table.vue中的重點代碼如下:

<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>

可以看到,我還引入了一個Bar.vue組件用于展示勝率,這是因為我希望最終的實際效果是這樣的:

Vue.js怎么實現多條件篩選、搜索、排序及分頁的表格功能

一開始我直接在勝率所在的<th>標簽中進行各種操作,但可想而知在進行一些邊界情況的判斷時,會出現各種含有player.weeklyWins, player.weeklyLosses等長命名變量的三元表達式。本來是出于便利考慮,卻反而導致代碼難以維護。因此新建了個一個bar組件,將勝負傳入組件中,在bar組件內部用更語義化的方式實現,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) + '%':'無場次' }}
 </span>
 <span
 v-if="loss / total > 0"
 :
 class="loss-bar">
 {{ win === 0? '0%':'' }}
 </span>
 </div>
</template>

更好理解和維護了,不是嗎?

在使用vue的過程中,需要注意的是框架中許多方法其實在內部最終是殊途同歸。

例如我們可以直接在元素中執行一些對數據的操作,例如@click="show = !show",同樣的我們也可以對事件綁定方法,再在方法中操作數據,例如@click="toggle", toggle () { this.show = !this.show }。還比如我們可以用computed屬性和watch屬性實現很多相同的功能,接下來還將用computed去實現和filters相同的功能。

vue設計中的靈活性讓我們有了更多的可能性,但在學習時,應該以搞明白不同方式在不同場景中的優劣為目標,實際運用時選擇最好的那一種。

用filters實現需求

在例子中,players實際是一個5000條數據的數組,在不做任何處理時,將直接渲染出5000個<tr>,所以先趕緊過濾吧!

對于v-for循環,vue中提供了3中filters過濾數組,分別為filterBy, orderBy, limitBy,其功能對應了搜索/篩選、排序和分頁,實現分別是使用了Array.filter, Array.sort(), Array.slice()。

這三種filters在使用時非常便利,只要在v-for后用|分離再添加對應的filters即可,這3中filter的具體參數可以查看官方API,這里不多做贅述。

需要注意的是,實際的過程是先將被遍歷的數組(例子中的players)依次通過過濾器,再將最后一個過濾器返回的數組進行v-for操作。

因此,filters放置的順序是需要根據需求來調整的,也因為每種過濾器的內部實現效率不同,所以在需求優先級不明顯時,應該以效率為優先。

注意:實際測試時,發現不論怎么過濾數組,handleBefore方法都沒有再次執行,也就是說players數組并沒有被改動過。

例如在我的例子中,我希望可以篩選出名字或者服務器包含了我所輸入內容的玩家,并且將他們按照某種方式排序,最后的結果每頁只顯示20條。那么顯然剪切數組永遠應該放在最后一步,而排序和過濾在需求中沒有明顯的優先級。但是大部分情況下,sort的效率都要低于filter,所以我們先進行filter,減少數組長度,再sort。

有了這一思路之后,用于v-for的<tr>變為:

<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'">

這里直接將各個變量動態化,再通過Table.vue中的input綁定v-model以及表頭thead綁定@click事件來改變篩選的條件,就已經實現了大部分的搜索、過濾、分頁功能。

表頭改變sort排序我是通過以下代碼實現的,方式可能不是太好,特此列出:

<thead>
 <tr>
 <th
 @click="sort = {key: 'ranking', val: -sort.val}">排名</th>
 <th
 @click="sort = {key: 'rating', val: -sort.val}">分數</th>
 <th>資料</th>
 <th>服務器</th>
 <th
 @click="sort = {key: 'weeklyRate', val: -sort.val}">本周戰績</th>
 <th
 @click="sort = {key: 'seasonRate', val: -sort.val}">賽季戰績</th>
 </tr>
</thead>

可以看到,通過vue的filters功能,已經可以輕松完成我們的大部分功能,代碼量極少。這也是vue2.0前瞻發布之后,提出廢棄部分filters功能后許多人反應較為強烈的原因。但是如同作者在改動說明中所說,filters對于初學者來說不易理解,并且filters的功能都可以用computed屬性進行更靈活、更好把控的實現。而且在一些復雜條件下,堆疊過濾器會造成一些額外的復雜性以及不方便之處。

那么何為復雜條件呢?例如我增添兩個需求,一是按職業篩選玩家,而是篩選出一定分數以上的玩家,那么后者用filterBy就不太好實現了。我們需要將對分數段的過濾放在filters之前進行,但又要注意不破壞players數組本身。在實際完成時,會發現這個過程還是比較糾結的。

除此之外,我們還會發現分頁中最重要的一個信息——總頁數我們獲取不到。因為vue并沒有把一串過濾管道中產出的最終用于v-for的數組暴露出來,所以我們無法獲得這個實際被循環的數組的長度。

在實際hack這些需求時,發現很容易與filters的執行順序發生沖突,因此決定重新用computed屬性來實現一遍所有功能,不借助自帶的filters。

當然,在這一段的前半部分中,我們顯而易見的感受到了來自filters的便利性。如果需求中filters可以滿足,那么在1.x版本中使用filters還是十分明智的!

用computed屬性完成需求

在Github倉庫中,我用Table.vue.bak文件儲存了之前一段中用filters實現的代碼,方便與我們接下里的實現進行比較。

首先整理一下用computed屬性來實現的思路:

  • 首先要實現filterBy, orderBy, limitBy這三個filter的功能,上文中已經提到了他們的內部實現,所以分別用Array.filter, Array.sort和Array.slice重寫一遍并不復雜。

  • 說是computed屬性實現,其實也還是只有players這個computed屬性,只是在其內部執行了所有的過濾動作,我們實際是把各種過濾器的邏輯放置在各個method中。

  • 不建議把各個過濾method寫的過于抽象,因為就是內置filters高度抽象導致一些特殊需求無法實現,所以不妨就以最針對性的方式:一個method對應一種過濾。

  • 在執行各個過濾method時,依然有最初提到的順序帶來的效率問題。因為vue牽一發而動全身的特性,任何一個過濾條件改變時,所有過濾method都會執行一遍,所以盡快用高效的過濾器縮短數組長度顯得更為重要。

  • 我嘗試過通過watch屬性實現最小化method調用,但無奈功力不夠沒能實現。同時我也認為前端處理大量數據的情況很少見,并且用第4點中的數據進行優化后,執行效率不算太低,所以沒必要在這個方面做過多糾結。真有性能瓶頸時,從服務器端尋求解決會更簡單。

注意:在實現各種過濾method時,建議閱讀vue中filterBy, orderBy, limitBy三部分的實現源碼,其本身對于數組的操作就有一些優化,非常值得學習。在一些特殊情況中,例如數組中大量相等值時,過于簡單的sort function會導致執行步數激增,vue中的一些處理都予以了避免。

根據需求目標,我設置了以下這些method(順序即為執行順序):

  • classFilter:過濾玩家職業,通過item.classId === this.class進行判斷,this.class綁定的是一個select控件。

  • queryFilter:匹配玩家姓名中的字段,通過item.name.indexOf(this.query)判斷,this.query則綁定一個input控件。

  • ratingFilter:篩選玩家分數段,通過item.rating >= this.rating進行判斷,this.rating綁定了一個類型為range的input控件,range的范圍則是用computed屬性進行計算。

  • sortTable:因為Array.sort進行的步數較多,所以放在數組被上述3個method處理的較短后進行。

  • paginate:所有過濾操作完畢之后,就可以進行分頁了。在使用Array.slice()之前,先將數組的長度傳給this.total儲存起來,用于在分頁后計算總的頁數。

  • 除了以上幾個過濾method以外,當然也還有handleBefore方法對數組進行前期處理。但是由于players每次都會重新計算,所以為了放止handleBefore被重復執行,應該加上一定的判斷條件,例如handleBefore添加的屬性是否已經存在了等等。同時,還可以把一些不需要在過濾之前執行的動作從handleBefore中拿出,例如例子中的classId轉換為Icon,可以在過濾之后對最終要展示的數據進行即可,減少一些步數。所以又設置了一個handleAfter方法,用于在分頁完成之后進行后續操作,當然在handleAfter中也可能重復執行,所以如果執行的操作消耗很大,建議同樣添加判斷,避免重復執行。

在例子代碼中,我在每個方法中都統計了執行的步數,實際結果顯示設置一個合理的過濾順序可以避免一些性能問題,結果如下:

Vue.js怎么實現多條件篩選、搜索、排序及分頁的表格功能

可以看出初始化時,在沒有任何過濾的情況下,sort的步數較高。而一旦添加了一些過濾條件之后,順位靠后的filter和sort的步數都會大幅度減少。

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Vue.js怎么實現多條件篩選、搜索、排序及分頁的表格功能”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

天等县| 四川省| 徐汇区| 信阳市| 成安县| 四平市| 宁化县| 沂水县| 三门峡市| 庆城县| 星座| 湟中县| 岑巩县| 秭归县| 肃南| 高尔夫| 天峻县| 桂平市| 商都县| 尼玛县| 桐庐县| 花垣县| 泽库县| 赞皇县| 滁州市| 理塘县| 通渭县| 玛曲县| 洛南县| 伊宁市| 大关县| 镇坪县| 三台县| 绵阳市| 上杭县| 临沂市| 阿拉善左旗| 福海县| 汝城县| 万安县| 永宁县|