異步輸出網頁列表查看詳情后返回定位打賞

最近有個需求,移動端網站,列表上拉加載,點擊詳情后返回,每次都固定返回到頂部,感覺這樣不夠人性化,希望固定到進列表前的頁面,于是簡單實現了一下。

這里有兩個問題

1、數據都是異步的

2、只有返回的時候定位(刷新正常回頂部)

簡單的實現思路及主要代碼

為了方便二次加載,異步數據每次緩存到本地,同時攔截頁面所有鏈接,在即將跳轉的時候記錄當前頁面或者滾動元素的scrollTop值,下次進頁面判斷是否返回進來的,如果是直接進來或者刷新,則重新請求,如果為返回,則直接使用已緩存數據迅速加載后使用已緩存的scrollTop值定位到進詳情頁之前的位置,同時清除值,即scrollTop值的緩存僅一次有效。

判斷瀏覽器返回關鍵代碼如下:

var navigationType = window.performance.navigation.type;

var navigation = {
    enter: navigationType === 0,
    refresh: navigationType === 1,
    back: navigationType === 2,
    type: navigationType
}

navigation.back為true的時候則為頁面返回進入,當然這里兼容性并不是很好,但由于是移動端,普遍WebView內核,再加上個別瀏覽器如QQ、UC等返回實則直接使用緩存,觸發不了JS運行,所以并不存在很大的問題,經測試Android、iOS皆可。

本地緩存,使用localStorage,但由于數據量可能比較大,這里對localStorage做了類似cookie的時效性封裝,默認數據緩存30分鐘,如果超時在取數據時直接返回null并移除已失效緩存,以此來降低緩存占用率,同時,每次進入頁面實例化緩存封裝的同時,異步掃描一次所有緩存,無效的自動移除。

至于輸出,可以根據需要,對數據進行預處理或者干脆存儲html,進入頁面后首先對緩存進行加載,之后再做事件綁定等動作。由于我一開始上拉加載的頁面都統一使用一個自己事先封裝的組件,回調傳回數據進行業務渲染,為了方便,我在組件內對數據進行緩存和重載,外部保持原有的業務渲染邏輯,只是每次請求數據前先獲取緩存,存在則直接通過回調傳回緩存數據,每次回調執行完畢后觸發scrollTop,由于scrollTop傳值大于當前頁面長度情況,頁面會滾動到底部,于是又會根據原有封裝的上拉加載調用下一頁的數據,如果存儲的是第二頁中某個位置,則可能出現第一次加載完滾動到底部立即加載第二頁,然后繼續觸發scrollTop向底部滾動到目標位置,同理,如果存儲的是第三、第四頁也會持續向下滾動,于是,這里就出現了一個沒法解決的問題,這樣依次加載幾頁效果還好,如果十頁、二十頁,那么會有明顯的滾動效果,不是很理想,由于頁面數量多,且都使用了現成封裝組件,這樣改動量最小就暫時這樣做了,最佳方案還是每次存儲已加載的所有數據,甚至直接html,這樣返回時一次性輸出,滾動效果會達到最大限度的消除,但弊端是要在存儲前對數據干涉,輸出時又要對原有的輸出進行干涉,大家可以權衡利弊。當然,大家也可以根據具體需要,將這種方案進一步封裝,緩存的輸出保持不影響原有程序,這里僅說出我的簡單思路,不再贅述。

另外,如果30分鐘后用戶才觸發緩存,這樣的情況可以根據業務需要,適當增加緩存時效或者干脆做永久緩存,和坐標的存儲、使用一樣,使用一次直接清除,第一次之后每次進頁面異步請求后默默將新數據存入緩存(如果數據變動快的話)。

注:這里不是也sessionStorage主要是因為實際應用中發現個別瀏覽器sessionStorage存在無故丟失甚至直接不可用等情況,所以直接采用localStorage

本地緩存簡單實現代碼如下:

/**
 * Created by William.Wei on 2016/5/5.
 * 參考js.cookie插件api,實現類似cookie的expires
 * var store = new Store([local|session])
 * store.set('key',{expires:1})
 * store.get('key')
 * store.getJSON('key')
 * store.remove('key')
 */
define(function (require, exports, module) {

    var global = window,
        encode = encodeURIComponent,
        decode = decodeURIComponent,
        expiresMap = {
            d: 86400000,
            h: 3600000,
            m: 60000,
            s: 1000
        },
        support = {
            local: global.localStorage,
            session: global.sessionStorage
        };

    function compare(oldData, newData) {
        return oldData === newData;
    }

    function setValue(key, value, attributes) {
        var store = this.store;
        if (typeof value === 'undefined' && attributes.expires === -1) {
            try {
                store.removeItem(encode(key));
            } catch (e) {
            }
            return;
        }

        attributes = $.extend(true, {}, this.defaults, attributes);

        if (typeof attributes.expires === 'number') {
            var expires = new Date();
            var unit = expiresMap[attributes.unit] || 1;
            expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * unit);
            attributes.expires = expires;
        }

        try {
            var result = JSON.stringify(value);
            if (/^[\{\[]/.test(result)) {
                value = result;
            }
        } catch (e) {
        }

        if (attributes.compare && ($.isFunction(attributes.compare) ? attributes.compare : compare)(getValue.call(this, key), value)) {
            return false;
        }

        value = {v: value};

        if (attributes.expires) {
            value.t = attributes.expires.getTime();
        }

        try {
            store.setItem(encode(key), JSON.stringify(value));
        } catch (e) {
        }
        return true;
    }

    function parse(key, value) {
        try {
            var temp = JSON.parse(value);
            if (!temp.t || temp.t > +new Date()) {
                value = temp.v;
                if (this.json) {
                    value = JSON.parse(value);
                }
            } else {
                //無效移除
                setValue.call(this, key, undefined, {expires: -1});
                return;
            }
        } catch (e) {
            value = undefined;
        }
        return value;
    }

    function getValue(key) {
        var value,
            store = this.store;
        if (key) {
            value = parse.call(this, key, store.getItem(encode(key)));
        } else {
            value = {};
            var len = store.length,
                tempKey;
            for (var i = 0; i < len; i++) {
                tempKey = store.key(i);
                value[tempKey] = parse.call(this, decode(tempKey), store.getItem(tempKey));
            }
        }
        return value;
    }

    function Store(type) {
        this.store = support[type || 'local'];
        //延遲1s清理失效臟數據
        setTimeout(function () {
            for (var i = 0; i < this.store.length; i++) {
                this.get(this.store.key(i));
            }
        }.bind(this), 1000);
    }

    Store.prototype = {
        defaults: {
            expires: null
        },
        get: getValue,
        set: setValue,
        getJSON: function () {
            return getValue.apply({
                store: this.store,
                json: true
            }, arguments);
        },
        remove: function (key, attributes) {
            setValue.call(this, key, undefined, $.extend(true, {}, attributes, {
                expires: -1
            }));
        },
        clear: function () {
            this.store.clear();
        }
    };

    module.exports = Store;
});
異步輸出網頁列表查看詳情后返回定位
文章《異步輸出網頁列表查看詳情后返回定位》二維碼
  • 微信打賞
  • 支付寶打賞

暫無評論

(必填)

(必填)

(可選)

黑龙江22选5开奖