摘要:在單頁應用中,我們有很多中分頁方案,最常見的是無限滾動上一頁下一頁和頁碼。本文將談談這三種分頁方式。而前端方面,需要做更多的事情,同時要考慮當前端數據丟失時如用戶刷新頁面的處理方案。
簡介
分頁是開發中最常見的需求之一。
對于分頁,我們討論的最多的是后端的數據庫分頁,這關乎到我們應用程序的性能,也是分頁這個需求的核心。
而前端要做的,是把后端返回的數據呈現在頁面上,工作被認為是簡單瑣碎的。
在單頁應用中,我們有很多中分頁方案,最常見的是無限滾動、上一頁 & 下一頁和頁碼。
本文將談談這三種分頁方式。
無論使用哪種分頁方案,我們都需要處理一些通用的需求,如:
解析 url,提取當前頁面的參數
根據返回數據生成自定義 DOM
移除某個 Node 節點中的所有子元素
往某個 Node 節點中插入元素列表
// 解析 url 中的查詢字符串 // 如 http://host/items?page=5 中提取 page=5 中的 5 function parsePage() { var searchString = window.location.search.substr(1).split("&").filter(v => v.indexOf("page") !== -1)[0]; var page = Number(searchString.split("=")[1]); return isNaN(page) ? 1 : page; } // 生成自定義 DOM // generateItemView :: Object -> DOM Node function generateItemView(object) { /* implementation */ } // 移除 Node 中所有子節點 function removeItems(node) { while (node.firstChild) { node.removeChild(node.firstChild); } } // 往 Node 中插入元素列表 function insertItems(node, items) { items.forEach(item => node.appendChild(generateItemView(item))); }
下文的示例代碼中會直接調用這些函數,不再重復定義。
無限滾動無論對從前端還是后端來說,無限滾動都是我認為最簡單的分頁方案。
對后端來說,按照 page 和 limit 直接查出范圍,然后返回一個數組給前端即可,不需要像其他方案那樣還要查詢總數。
對前端來說,直接根據后端返回的數據進行拼接即可,當后端返回一個空數組時,可以認為已經到最后一頁,這時候就不需要再發請求到后端了。
// 后端返回的數據結構 // GET /items?page=5 { items: [...] }
// 前端處理 function getItems(page) { fetch(`/items?page=${page}`) .then(res => res.json()) .then(res => { if (res.items.length > 0) { insertItems( document.getElementById("container"), res.items ); } else { alert("No more data"); } }); }
無限滾動雖然實現起來簡單,用戶體驗也不錯,但有一些致命的缺點:
容易出現性能問題
容易丟失瀏覽進度
目前有一些方案可以解決這些缺點:性能問題可以通過動態渲染來解決,而丟失瀏覽進度則可以通過簡單的新開窗口來解決。
上一頁 & 下一頁這種分頁方式和無限滾動比起來,會復雜一點點。
最主要是因為后端需要查詢總數,然后根據當前頁數來計算是否可以查詢上一頁或下一頁。
當然,計算這部分可以在后端做,也可以在前端做。
如果在后端計算,那么后端要做的事情就有:
查詢總數
計算 hasPrev 和 hasNext
查詢元素列表
而前端方面則相對簡單:
根據后端返回的 hasPrev 和 hasNext 來判斷是否需要顯示上一頁/下一頁按鈕
移除容器內的所有元素,再插入新的元素(即用新元素替換舊元素)
// 后端返回數據結構 // GET /items?page=5 { // hasPrev 和 hasNext 都需要后端去查詢總數,然后計算出來 hasPrev: true, hasNext: true, items: [...] }
// 前端處理 function getItems(page) { fetch(`/items?page=${page}`) .then(res => res.json()) .then(res => { res.hasPrev ? document.getElementById("prevButton").style.display = "block" : document.getElementById("prevButton").style.display = "none"; res.hasNext ? document.getElementById("nextButton").style.display = "block" : document.getElementById("nextButton").style.display = "none"; var container = document.getElementById("container"); removeItems(container); insertItems(container, res.items); }); }
這個方案實現起來比較簡單,但缺點是每次分頁都需要查詢總頁數,浪費資源。
前端計算如果是前端計算的話,那么后端要做的事情就相對簡單,只要再提供一個查詢總數的接口即可。
而前端方面,需要做更多的事情,同時要考慮當前端數據丟失時(如用戶刷新頁面)的處理方案。
第一次加載頁面時需要調用一次查詢總數的接口,同時調用獲取元素的接口
返回數據后計算 hasPrev 和 hasNext,用來判斷是否需要顯示上一頁/下一頁按鈕
移除容器內的所有元素,再插入新的元素(即用新元素替換舊元素)
// 后端返回數據結構 // GET /itemsCount { total: 100 } // GET /items?page=5 { items: [...] }
// 前端處理 var total = 0; var limit = 10; window.onload = getItemsCount(getItems); // 獲取總數 function getItemsCount(callback) { fetch("/itemsCount") .then(res => res.json()) .then(res => { total = res.total; callback.call(null, parsePage()); }); } function getItems(page) { fetch(`/items?page=${page}`) .then(res => res.json()) .then(res => { var hasPrev = page != 1; var hasNext = page != Math.ceil(total / limit); hasPrev ? document.getElementById("prevButton").style.display = "block" : document.getElementById("prevButton").style.display = "none"; hasNext ? document.getElementById("nextButton").style.display = "block" : document.getElementById("nextButton").style.display = "none"; var container = document.getElementById("container"); removeItems(container); insertItems(container, res.items); }); }
這種方案可以讓后端甩鍋給前端,前端的活又變多拉!
頁碼最后我們談談頁碼分頁。
這個方案和「上一頁 & 下一頁」的方案很類似,不同的地方在于這個方案需要根據當前頁面和總數來生成頁碼。
生成頁碼是這個方案最麻煩的地方。舉個簡單的例子,假設我們的數據有 50 頁,我們不可能把所有頁碼都顯示出來,需要生成一組不連續的頁碼。
我們可以采用下面的形式來顯示頁面:
// ------------------------------ // 我個人比較喜歡用 -1 來表示省略的區域 // 在生成 DOM 的時候,可以用省略號來展示 // ------------------------------ // 假設當前是第 1 頁 [1, 2, 3, -1, 50] // 假設當前是第 3 頁 [1, 2, 3, 4, 5, -1, 50] // 假設當前是第 25 頁 [1, -1, 23, 24, 25, 26, 27, -1, 50] // 假設當前是第 48 頁 [1, -1, 46, 47, 48, 49, 50] // 假設當前是第 50 頁 [1, -1, 48, 49, 50]
生成頁碼的原則通常都是:
第一頁和最后一頁必須展示
其他頁面按需展示,通常是當前頁面的前后兩頁(即 x +- 2)
當頁數少于 10 頁的時候,直接顯示出所有頁碼(為什么是 10 頁?其實在滿足前兩個原則的情況下,只要 7 頁省略號就會正常顯示了。但頁數較少的情況下顯示省略號感覺怪怪的。)
var lastPage = Math.ceil(total / limit); // 根據當前頁生成動態頁碼 function genPages() { if (lastPage <= 10) { return Array(lastPage).fill().map((v, i) => i + 1); } // dynamicPages 為除第一頁和最后一頁之外的頁碼,-1 表示省略號 var dynamicPages; if (page === 1) { dynamicPages = [2, 3, -1]; } else if (page === 2) { dynamicPages = [2, 3, 4, -1]; } else if (page === 3) { dynamicPages = [2, 3, 4, 5, -1]; } else if (page === lastPage - 2) { dynamicPages = [-1, page - 2, page - 1, page, page + 1]; } else if (page === lastPage - 1) { dynamicPages = [-1, page - 2, page - 1, page]; } else if (page === lastPage) { dynamicPages = [-1, page - 2, page - 1]; } else { dynamicPages = [-1, page - 2, page - 1, page, page + 1, page + 2, -1]; } dynamicPages.unshift(1); dynamicPages.push(lastPage); return dynamicPages; }
生成動態頁碼這部分的邏輯,無論放在前端還是后端都影響不大,可以按照自己需要去選擇。
至于其他部分的細節,和「上一頁 & 下一頁」類似,這里就不再重復了。
http://scarletsky.github.io/2...
參考資料https://github.com/xitu/gold-...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/90968.html
摘要:中的哈希號單頁應用只有一個頁面,視圖的變化通常是通過路由來驅動,首先,我們先來談一談單頁應用的中的號,很多采用單元結構網站的都出現了這個符號。 單頁應用SEO淺談 單頁應用(Single Page Application)越來越受web開發者歡迎,單頁應用的體驗可以模擬原生應用,一次開發,多端兼容。單頁應用并不是一個全新發明的技術,而是隨著互聯網的發展,滿足用戶體驗的一種綜合技術。 S...
摘要:基礎布局的中主要為部分,分別是用于搜索篩選和分頁的表單控件用于排序表格的表頭以及用于展示數據的。這也是前瞻發布之后,提出廢棄部分功能后許多人反應較為強烈的原因。 與上周的第一篇實踐教程一樣,在這篇文章中,我將繼續從一種常見的功能——表格入手,展示Vue.js中的一些優雅特性。同時也將對filter功能與computed屬性進行對比,說明各自的適用場景,也為vue2.0版本中即將刪除的部...
摘要:基礎布局的中主要為部分,分別是用于搜索篩選和分頁的表單控件用于排序表格的表頭以及用于展示數據的。這也是前瞻發布之后,提出廢棄部分功能后許多人反應較為強烈的原因。 與上周的第一篇實踐教程一樣,在這篇文章中,我將繼續從一種常見的功能——表格入手,展示Vue.js中的一些優雅特性。同時也將對filter功能與computed屬性進行對比,說明各自的適用場景,也為vue2.0版本中即將刪除的部...
摘要:我們將創建一個簡單的,它將從到返回一個隨機數。我們來改變組件顯示隨機數在這個階段,我們只是模仿客戶端的隨機數生成過程。 在這個教程中,我們將講解如何將vue.js單頁應用與Flask后端進行連接。 一般來說,如果你只是想通過Flask模板使用vue.js庫也是沒有問題的。但是,實際上是一個很明顯的問題那就是,Jinja(模板引擎)也和Vue.js一樣采用雙大括號用于渲染,但只是一個還算...
閱讀 2563·2021-11-22 12:05
閱讀 3449·2021-10-14 09:42
閱讀 1683·2021-07-28 00:15
閱讀 1988·2019-08-30 11:08
閱讀 1486·2019-08-29 17:31
閱讀 928·2019-08-29 16:42
閱讀 2336·2019-08-26 11:55
閱讀 2117·2019-08-26 11:49