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

資訊專欄INFORMATION COLUMN

[web前端發(fā)微] 瀟灑地操作 window.history

chnmagnus / 492人閱讀

摘要:如果把操作換成其他操作呢比如一般的操作如此看來,借鑒于的機(jī)制和原理,我們能干的事情很多。對于需要讓瀏覽器記錄的事件操作或者狀態(tài),我們按這個(gè)套路實(shí)現(xiàn)就好了。從需求出發(fā)來考慮設(shè)計(jì)實(shí)現(xiàn)需求驅(qū)動,是培養(yǎng)架構(gòu)能力的好習(xí)慣。原創(chuàng)不易,轉(zhuǎn)稿請注明作者出處

如果你想在 web 應(yīng)用實(shí)現(xiàn)類似 pjax 的功能特性,往往需要做一些準(zhǔn)備,比如對于不支持 history.pushState 方法的部分瀏覽器,怎樣去做優(yōu)雅降級,以滿足頁面整體的可用性等等。這篇文章主要來說說 pjax 相關(guān)的問題和思路。

1. Why pjax?

首先,因?yàn)槲覀儽厝粫玫?ajax 來搞定數(shù)據(jù),在 js 中執(zhí)行的請求和 DOM 操作并不會被 history 記錄(這么說雖然不嚴(yán)謹(jǐn),幫助理解就好);

其次,單頁面應(yīng)用場景(或者某一個(gè)頁面有多個(gè)交互狀態(tài)的情況)下,瀏覽器的前進(jìn)后退功能無法獲取到某一次 ajax 操作或者交互的狀態(tài);

第三(你以為我會說最后?so cute!),接前面所述,當(dāng)頁面在某種狀態(tài)下被分享或者傳播時(shí),新的用戶進(jìn)入后,頁面本應(yīng)該維持在上個(gè)用戶分享或傳播時(shí)的狀態(tài)(比如你經(jīng)常在朋友圈分享的各種活動頁面等等)...

基于以上且不限于以上所述的種種需求,pjax 的策略便應(yīng)運(yùn)而生。

2. Pjax 的機(jī)制

參考上面的示意圖,用一種簡單的方式來描述這個(gè)機(jī)制的過程:

首先,在執(zhí)行 ajax 操作時(shí),我們使用 pushState 方法向 瀏覽器的 history 對象中寫入一個(gè)特定的狀態(tài)值(一組參數(shù)),保證每一次 ajax 請求都能有一個(gè)相應(yīng)的 history 記錄(history.state);

那么之后,當(dāng)我們訪問 history 的不同狀態(tài)的時(shí)候(比如點(diǎn)擊瀏覽器前進(jìn)、后退按鈕),通過當(dāng)前狀態(tài)值我們也能找到與之對應(yīng)的 ajax 操作。

這里 pushState 方法的一個(gè)好處,就是可以在不重載頁面的情況下,改寫瀏覽器地址欄 url(同時(shí)改變
window.location.href)。

3. Pjax 的本質(zhì)

Pjax 給我們提供了一個(gè)方案,而不僅僅是 pjax 的本身內(nèi)容。我們至少可以從兩個(gè)方面來拓展一下:

(1)如果沒有 pushState,可以用其他方式來影響瀏覽器的歷史記錄嗎?

如果你比較了解 React 或者 Angular 的 router 實(shí)現(xiàn),那么這個(gè)問題很容易理解。比如 react-router 給予我們兩種選擇,一種是基于 history.pushState 的路由實(shí)現(xiàn),一種是基于 location.hash 的實(shí)現(xiàn),后者相對前者而言,適用性更強(qiáng)一些,畢竟 錨點(diǎn) 這個(gè)東西,在 web1.0 時(shí)代我們就很熟悉了。使用 location.hash 能夠滿足低版本瀏覽器的需要。

(2)如果把 ajax 操作換成其他操作呢?比如一般的 DOM 操作

如此看來,借鑒于 pjax 的機(jī)制和原理,我們能干的事情很多。對于需要讓瀏覽器記錄的事件操作或者狀態(tài),我們按這個(gè)套路實(shí)現(xiàn)就好了。

4. By the way, and how to do?

基于上面的討論,如果你已經(jīng)有種想做點(diǎn)什么的沖動。那么,我想我們已經(jīng)產(chǎn)生了共鳴。

看到這里,不妨給文章點(diǎn)個(gè)贊或者丟幾個(gè)硬幣什么的,十分感激 (Xie-Xie-Ba-Ba)

拋開單純的 pjax 實(shí)現(xiàn)(比如 jquery-pjax 等等)

如果我們可以自己做一個(gè)小工具(方法類庫之類的)
利用瀏覽器的 history 來驅(qū)動頁面的操作或者行為
解決更多的問題
或者實(shí)現(xiàn)一個(gè)全新的功能
是不是很 cool ?

5. 欲望清單

這個(gè)小標(biāo)題看起來可能的有點(diǎn)中二(或者有點(diǎn)標(biāo)題黨吧)。。。

從需求出發(fā)來考慮設(shè)計(jì)實(shí)現(xiàn)(需求驅(qū)動),是培養(yǎng)架構(gòu)能力的好習(xí)慣。(~嚶~嚶~嚶)

5.1 需求清單:

(1)我們想做一個(gè)更通用的 pushState 方法,用法如下(考慮逼格,展示 ES6 語法的偽代碼):

// 以 import 形式引入依賴,easierHistory 是我們最終構(gòu)造的方法集(一個(gè)對象或構(gòu)造器)或者工具包
import easierHis from "./easierHistory";

// ...do something...

// 向?yàn)g覽器歷史插入一條記錄 (例如:我們做一個(gè)翻頁的效果時(shí),傳入值為一個(gè)頁碼)
easierHis.putState({page: 3});

/* 注:為與原有 pushState 方法區(qū)別,故將新方法命名為 putState */


(2)我們想通過一個(gè)方法(或者接口)訪問到當(dāng)前的歷史狀態(tài)(更通用的 history.state 方法):

// 獲取當(dāng)前歷史狀態(tài) state
let { state } = easierHis.getState();

/* 注:為與原有 state 方法區(qū)別,故將新方法命名為 getState */

(3)構(gòu)造一個(gè)通用的方法,當(dāng)進(jìn)行瀏覽器前進(jìn)后退操作時(shí),可以觸發(fā)一些操作:

// 獲取當(dāng)前歷史狀態(tài) state
easierHis.popState( (state) => { do something... } );

/* 注:這里我們給 popState 方法傳入一個(gè)回調(diào),回調(diào)的內(nèi)容就是我們想要觸發(fā)的操作 */
5.2 一個(gè)完整的需求實(shí)例:

綜合考慮一個(gè)實(shí)際的應(yīng)用場景,比如我們想要用自己構(gòu)造的這種類 pjax 機(jī)制實(shí)現(xiàn)一個(gè)有記錄、可前進(jìn)回退的翻頁效果。大致的實(shí)現(xiàn)如下:

import easierHis from "./easierHistory";

// 默認(rèn)加載第 1 頁數(shù)據(jù)
if (!easierHis.getState()) {
  loadPage(1);      // 用于翻頁和加載數(shù)據(jù)的方法
  easierHis.putState({page: 1});
}

// 瀏覽器前進(jìn)/后退時(shí),根據(jù) state 數(shù)據(jù)加載對應(yīng)頁碼的數(shù)據(jù)
easierHis.popState((state) => {
  let cur_page = !state ? 1 : parseInt(state.page);
  loadPage(cur_page);
});

// 加載或跳轉(zhuǎn)某頁的方法
function goto(page){
  loadPage(page);
  easierHis.putState({page: page});
}
6. 具體實(shí)現(xiàn)

從上一小節(jié)的需求出發(fā),我們來看一看這個(gè)小工具(包)的具體實(shí)現(xiàn)。
這里直接看代碼,行文思路和具體方法的用法,可以參考代碼注釋:

/* 基于 ES5 的 easierHistory 實(shí)現(xiàn) */
"use strict";

// 全局對象
var easierHistory = {};

/*
** @method putState : 實(shí)現(xiàn) 類PJAX 機(jī)制的輔助函數(shù),用于在 history 菊花上插一刀
** @param {Object} state_content : 第 1 個(gè)參數(shù)(必填),表示當(dāng)前 state 的對象字面量
** @param {Boolean} sync_prior : 第 2 個(gè)參數(shù)(選填),傳 true 則優(yōu)先使用方案 $1,反之直接使用方案 $2,默認(rèn)值為 true
** @return {Object} _state : 返回 state
**
** $1 : 基于 history.pushState (絕大部分現(xiàn)代瀏覽器均支持)
** $2 : 通過操作 url 的 hash 字符串內(nèi)容的方式來進(jìn)行兼容
*/
easierHistory.putState = function (state_content, sync_prior) {
  var _state = arguments[0] || {};
  var _prior = typeof arguments[1] == "undefined" ? true : arguments[1];

  // 拼接 search 和 hash 字符串
  var _search = "?";
  var _hash = "";
  for (var key in _state) {
    _search += key + "=" + _state[key] + "&";
    _hash += "#" + key + "=" + _state[key];
  }
  _search = _search.replace(/&$|?$/, "");

  // 根據(jù)瀏覽器支持情況,選擇一種實(shí)現(xiàn)方式
  if (!history.pushState || !_prior) {
    location.hash = _hash;                       // $2 基于 location.hash 的實(shí)現(xiàn)
  } else {
    history.pushState(_state, "", _search);      // $1 基于 pushState 的實(shí)現(xiàn)
  }

  // 返回當(dāng)前 state
  return _state;
}

/*
** @method getState_byHistory : 用于獲取 history 狀態(tài)
** @return {Object} curState : 當(dāng)前 history 狀態(tài)
*/
easierHistory.getState_byHistory = function () {
  if (history.state) {
    return history.state;
  }

  if (location.search) {
    return location.search.substring(1).split("&").reduce(function (curState, queryStr) {
      if (queryStr.indexOf("=") !== -1) {
        curState[queryStr.split("=")[0]] = queryStr.split("=")[1];
      }

      return curState;
    }, {});
  }

  return null;
};

/*
** @method getState_byHash : 將 location.hash 的內(nèi)容解析為 json 對象
** @return {Object} curState : 轉(zhuǎn)換后的 json 對象
*/
easierHistory.getState_byHash = function () {
  if (!location.hash) {
    return null;
  }

  return location.hash.split("#").reduce(function (curState, hashStr) {
    if (hashStr.indexOf("=") !== -1) {
      curState[hashStr.split("=")[0]] = hashStr.split("=")[1];
    }

    return curState;
  }, {});
};

easierHistory.getState = function () {
  return easierHistory.getState_byHistory() || easierHistory.getState_byHash();
};

/*
** @method popState : 給 window對象 綁定 popState 事件,若瀏覽器不支持則向下兼容 hashchange 事件
** @param {Function} cbFunc : 事件回調(diào)
*/
easierHistory.popState = function (cbFunc) {
  if (easierHistory.getState_byHistory()) {
    window.onpopstate = function () {          // 基于 popstate 方法的實(shí)現(xiàn)(html5 特性)
      cbFunc(easierHistory.getState());
    };
  } else {
    window.onhashchange = function () {        // 基于 hashchange 方法的實(shí)現(xiàn)(兼容性更強(qiáng))
      cbFunc(easierHistory.getState());
    };
  }
};


module.exports = easierHistory;

當(dāng)然,上面的代碼可以直接在瀏覽器運(yùn)行(直接使用 easierHistory對象),把 module.exports 語句去掉即可。

原創(chuàng)不易,轉(zhuǎn)稿請注明作者、出處

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

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

相關(guān)文章

  • SPA那點(diǎn)事

    摘要:單頁面應(yīng)用的出現(xiàn)依然存在著爭議性,我們該如何看待他的兩面性呢接下來小生給大家總結(jié)一下他的優(yōu)缺點(diǎn)。單頁面應(yīng)用的優(yōu)勢無刷新體驗(yàn)沒有了令人詬病的頁面頻繁刷新,同時(shí)節(jié)約瀏覽器資源,路由響應(yīng)比較及時(shí),提升了用戶的體驗(yàn)。 前端猿一天不學(xué)習(xí)就沒飯吃了,后端猿三天不學(xué)習(xí)仍舊有白米飯擺于桌前。IT行業(yè)的快速發(fā)展一直在推動著前端技術(shù)棧在不斷地更新?lián)Q代,前端的發(fā)展成了互聯(lián)網(wǎng)時(shí)代的一個(gè)縮影。而單頁面應(yīng)用的發(fā)展...

    PumpkinDylan 評論0 收藏0
  • SPA那點(diǎn)事

    摘要:單頁面應(yīng)用的出現(xiàn)依然存在著爭議性,我們該如何看待他的兩面性呢接下來小生給大家總結(jié)一下他的優(yōu)缺點(diǎn)。單頁面應(yīng)用的優(yōu)勢無刷新體驗(yàn)沒有了令人詬病的頁面頻繁刷新,同時(shí)節(jié)約瀏覽器資源,路由響應(yīng)比較及時(shí),提升了用戶的體驗(yàn)。 前端猿一天不學(xué)習(xí)就沒飯吃了,后端猿三天不學(xué)習(xí)仍舊有白米飯擺于桌前。IT行業(yè)的快速發(fā)展一直在推動著前端技術(shù)棧在不斷地更新?lián)Q代,前端的發(fā)展成了互聯(lián)網(wǎng)時(shí)代的一個(gè)縮影。而單頁面應(yīng)用的發(fā)展...

    Lsnsh 評論0 收藏0
  • SPA那點(diǎn)事

    摘要:單頁面應(yīng)用的出現(xiàn)依然存在著爭議性,我們該如何看待他的兩面性呢接下來小生給大家總結(jié)一下他的優(yōu)缺點(diǎn)。單頁面應(yīng)用的優(yōu)勢無刷新體驗(yàn)沒有了令人詬病的頁面頻繁刷新,同時(shí)節(jié)約瀏覽器資源,路由響應(yīng)比較及時(shí),提升了用戶的體驗(yàn)。 前端猿一天不學(xué)習(xí)就沒飯吃了,后端猿三天不學(xué)習(xí)仍舊有白米飯擺于桌前。IT行業(yè)的快速發(fā)展一直在推動著前端技術(shù)棧在不斷地更新?lián)Q代,前端的發(fā)展成了互聯(lián)網(wǎng)時(shí)代的一個(gè)縮影。而單頁面應(yīng)用的發(fā)展...

    lijinke666 評論0 收藏0

發(fā)表評論

0條評論

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