成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

淺談單頁應用中前端分頁的實現(xiàn)方案

denson / 1639人閱讀

摘要:在單頁應用中,我們有很多中分頁方案,最常見的是無限滾動上一頁下一頁和頁碼。本文將談?wù)勥@三種分頁方式。而前端方面,需要做更多的事情,同時要考慮當前端數(shù)據(jù)丟失時如用戶刷新頁面的處理方案。

簡介

分頁是開發(fā)中最常見的需求之一。
對于分頁,我們討論的最多的是后端的數(shù)據(jù)庫分頁,這關(guān)乎到我們應用程序的性能,也是分頁這個需求的核心。
而前端要做的,是把后端返回的數(shù)據(jù)呈現(xiàn)在頁面上,工作被認為是簡單瑣碎的。
在單頁應用中,我們有很多中分頁方案,最常見的是無限滾動、上一頁 & 下一頁和頁碼。
本文將談?wù)勥@三種分頁方式。

通用

無論使用哪種分頁方案,我們都需要處理一些通用的需求,如:

解析 url,提取當前頁面的參數(shù)

根據(jù)返回數(shù)據(jù)生成自定義 DOM

移除某個 Node 節(jié)點中的所有子元素

往某個 Node 節(jié)點中插入元素列表

// 解析 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 中所有子節(jié)點
function removeItems(node) {
    while (node.firstChild) {
        node.removeChild(node.firstChild);
    }
}

// 往 Node 中插入元素列表
function insertItems(node, items) {
    items.forEach(item => node.appendChild(generateItemView(item)));
}

下文的示例代碼中會直接調(diào)用這些函數(shù),不再重復定義。

無限滾動

無論對從前端還是后端來說,無限滾動都是我認為最簡單的分頁方案。
對后端來說,按照 pagelimit 直接查出范圍,然后返回一個數(shù)組給前端即可,不需要像其他方案那樣還要查詢總數(shù)。
對前端來說,直接根據(jù)后端返回的數(shù)據(jù)進行拼接即可,當后端返回一個空數(shù)組時,可以認為已經(jīng)到最后一頁,這時候就不需要再發(fā)請求到后端了。

// 后端返回的數(shù)據(jù)結(jié)構(gòu)
// 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");
            }
        });
}

無限滾動雖然實現(xiàn)起來簡單,用戶體驗也不錯,但有一些致命的缺點:

容易出現(xiàn)性能問題

容易丟失瀏覽進度

目前有一些方案可以解決這些缺點:性能問題可以通過動態(tài)渲染來解決,而丟失瀏覽進度則可以通過簡單的新開窗口來解決。

上一頁 & 下一頁

這種分頁方式和無限滾動比起來,會復雜一點點。
最主要是因為后端需要查詢總數(shù),然后根據(jù)當前頁數(shù)來計算是否可以查詢上一頁或下一頁。
當然,計算這部分可以在后端做,也可以在前端做。

后端計算

如果在后端計算,那么后端要做的事情就有:

查詢總數(shù)

計算 hasPrevhasNext

查詢元素列表

而前端方面則相對簡單:

根據(jù)后端返回的 hasPrevhasNext 來判斷是否需要顯示上一頁/下一頁按鈕

移除容器內(nèi)的所有元素,再插入新的元素(即用新元素替換舊元素)

// 后端返回數(shù)據(jù)結(jié)構(gòu)
// GET /items?page=5
{
    // hasPrev 和 hasNext 都需要后端去查詢總數(shù),然后計算出來
    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);
        });
}

這個方案實現(xiàn)起來比較簡單,但缺點是每次分頁都需要查詢總頁數(shù),浪費資源。

前端計算

如果是前端計算的話,那么后端要做的事情就相對簡單,只要再提供一個查詢總數(shù)的接口即可。
而前端方面,需要做更多的事情,同時要考慮當前端數(shù)據(jù)丟失時(如用戶刷新頁面)的處理方案。

第一次加載頁面時需要調(diào)用一次查詢總數(shù)的接口,同時調(diào)用獲取元素的接口

返回數(shù)據(jù)后計算 hasPrevhasNext,用來判斷是否需要顯示上一頁/下一頁按鈕

移除容器內(nèi)的所有元素,再插入新的元素(即用新元素替換舊元素)

// 后端返回數(shù)據(jù)結(jié)構(gòu)
// GET /itemsCount
{ total: 100 }

// GET /items?page=5
{ items: [...] }
// 前端處理
var total = 0;
var limit = 10;

window.onload = getItemsCount(getItems);

// 獲取總數(shù)
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);
        });
}

這種方案可以讓后端甩鍋給前端,前端的活又變多拉!

頁碼

最后我們談?wù)勴摯a分頁。
這個方案和「上一頁 & 下一頁」的方案很類似,不同的地方在于這個方案需要根據(jù)當前頁面和總數(shù)來生成頁碼。
生成頁碼是這個方案最麻煩的地方。舉個簡單的例子,假設(shè)我們的數(shù)據(jù)有 50 頁,我們不可能把所有頁碼都顯示出來,需要生成一組不連續(xù)的頁碼。

我們可以采用下面的形式來顯示頁面:

// ------------------------------
// 我個人比較喜歡用 -1 來表示省略的區(qū)域
// 在生成 DOM 的時候,可以用省略號來展示
// ------------------------------
// 假設(shè)當前是第 1 頁
[1, 2, 3, -1, 50]

// 假設(shè)當前是第 3 頁
[1, 2, 3, 4, 5, -1, 50]

// 假設(shè)當前是第 25 頁
[1, -1, 23, 24, 25, 26, 27, -1, 50]

// 假設(shè)當前是第 48 頁
[1, -1, 46, 47, 48, 49, 50]

// 假設(shè)當前是第 50 頁
[1, -1, 48, 49, 50]

生成頁碼的原則通常都是:

第一頁和最后一頁必須展示

其他頁面按需展示,通常是當前頁面的前后兩頁(即 x +- 2)

當頁數(shù)少于 10 頁的時候,直接顯示出所有頁碼(為什么是 10 頁?其實在滿足前兩個原則的情況下,只要 7 頁省略號就會正常顯示了。但頁數(shù)較少的情況下顯示省略號感覺怪怪的。)

var lastPage = Math.ceil(total / limit);

// 根據(jù)當前頁生成動態(tài)頁碼
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;
}

生成動態(tài)頁碼這部分的邏輯,無論放在前端還是后端都影響不大,可以按照自己需要去選擇。
至于其他部分的細節(jié),和「上一頁 & 下一頁」類似,這里就不再重復了。

出處

http://scarletsky.github.io/2...

參考資料

https://github.com/xitu/gold-...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/90968.html

相關(guān)文章

  • 單頁應用SEO淺談(轉(zhuǎn)載)

    摘要:中的哈希號單頁應用只有一個頁面,視圖的變化通常是通過路由來驅(qū)動,首先,我們先來談一談單頁應用的中的號,很多采用單元結(jié)構(gòu)網(wǎng)站的都出現(xiàn)了這個符號。 單頁應用SEO淺談 單頁應用(Single Page Application)越來越受web開發(fā)者歡迎,單頁應用的體驗可以模擬原生應用,一次開發(fā),多端兼容。單頁應用并不是一個全新發(fā)明的技術(shù),而是隨著互聯(lián)網(wǎng)的發(fā)展,滿足用戶體驗的一種綜合技術(shù)。 S...

    xuhong 評論0 收藏0
  • Vue.js 實踐(2):實現(xiàn)多條件篩選、搜索、排序及頁的表格功能

    摘要:基礎(chǔ)布局的中主要為部分,分別是用于搜索篩選和分頁的表單控件用于排序表格的表頭以及用于展示數(shù)據(jù)的。這也是前瞻發(fā)布之后,提出廢棄部分功能后許多人反應較為強烈的原因。 與上周的第一篇實踐教程一樣,在這篇文章中,我將繼續(xù)從一種常見的功能——表格入手,展示Vue.js中的一些優(yōu)雅特性。同時也將對filter功能與computed屬性進行對比,說明各自的適用場景,也為vue2.0版本中即將刪除的部...

    Profeel 評論0 收藏0
  • Vue.js 實踐(2):實現(xiàn)多條件篩選、搜索、排序及頁的表格功能

    摘要:基礎(chǔ)布局的中主要為部分,分別是用于搜索篩選和分頁的表單控件用于排序表格的表頭以及用于展示數(shù)據(jù)的。這也是前瞻發(fā)布之后,提出廢棄部分功能后許多人反應較為強烈的原因。 與上周的第一篇實踐教程一樣,在這篇文章中,我將繼續(xù)從一種常見的功能——表格入手,展示Vue.js中的一些優(yōu)雅特性。同時也將對filter功能與computed屬性進行對比,說明各自的適用場景,也為vue2.0版本中即將刪除的部...

    ChanceWong 評論0 收藏0
  • 使用 Vue.js 和 Flask 來構(gòu)建一個單頁的App

    摘要:我們將創(chuàng)建一個簡單的,它將從到返回一個隨機數(shù)。我們來改變組件顯示隨機數(shù)在這個階段,我們只是模仿客戶端的隨機數(shù)生成過程。 在這個教程中,我們將講解如何將vue.js單頁應用與Flask后端進行連接。 一般來說,如果你只是想通過Flask模板使用vue.js庫也是沒有問題的。但是,實際上是一個很明顯的問題那就是,Jinja(模板引擎)也和Vue.js一樣采用雙大括號用于渲染,但只是一個還算...

    LiuZh 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<