站長資訊網
        最全最豐富的資訊網站

        Javascript PJAX 原理和使用

        Javascript PJAX 原理和使用

        pjax 即 pushState + ajax,它被封裝成了一個 jQuery 擴展以方便使用。pjax 主要用來解決 HTML 頁面局部刷新 url 不更新和不支持后退和前進的問題,提升用戶體驗。

        pjax原理

        pjax 的實現是利用 HTML5 的 pushState() 和 replaceState() 新特性和 ajax 結合實現。pushState() 和 replaceState() 用來操作 State(狀態)對象,即可添加和修改歷史記錄,進而更新 url 和提供前進、后退操作 ajax 實現數據的異步加載進而局部刷新。

        工作流程圖

        Javascript PJAX 原理和使用

        源碼分析

        • pjax支持判斷
        (function($){     $.support.pjax =        window.history && window.history.pushState && window.history.replaceState &&        // pushState isn't reliable on iOS until 5.        !navigator.userAgent.match(/((iPod|iPhone|iPad).+bOSs+[1-4]D|WebApps/.+CFNetwork)/)     if ($.support.pjax){        enable()   //啟用     } else {        disable()  //禁用     } })(jQuery)
        • enable()
        function enable() {     $.fn.pjax = fnPjax             //注冊jQuery的pjax方法     $.pjax = pjax                  //注冊pjax對象     $.pjax.enable = $.noop     $.pjax.disable = disable     $.pjax.click = handleClick     //注冊click回調     $.pjax.submit = handleSubmit   //注冊submit回調     $.pjax.reload = pjaxReload     //注冊reload回調     $.pjax.defaults = {}           //設置默認值     $(window).on('popstate.pjax', onPjaxPopstate)  //綁定popstate事件回調 }

        $.noop是一個空方法,不做任何事,即function(){}popstate.pjax是 JS 事件的命名空間寫法,popstate是事件類型,每當激活的歷史記錄發生變化時(瀏覽器操作前進、后退按鈕、調用 back() 或者 go() 方法),都會觸發 popstate 事件,但調用 pushState()、replaceState() 不會觸發 popstate 事件。.pjax是該事件的命名空間,這樣方便解綁指定命名空間的事件響應,在綁定匿名函數時常使用,例如:this.on('click.pjax', selector, function(event){})

        • fnPjax()

        該方法返回一個 jQuery 對象,等同于 $.fn.pjax。

        return this.on('click.pjax', selector, function(event) {     //獲取pjax配置信息     options = optionsFor(container, options)     //自動綁定click事件響應     return this.on('click.pjax', selector, function(event) {        var opts = options        if (!opts.container) {            opts = $.extend({}, options)            //如果不配置container,則默認獲取data-pjax屬性值對應的            opts.container = $(this).attr('data-pjax')        }        handleClick(event, opts)     //調用click回調     }) }
        • pjax()
        // Use it just like $.ajax: // //   var xhr = $.pjax({ url: this.href, container: '#main' }) //   console.log( xhr.readyState ) // // Returns whatever $.ajax returns. function pjax(options) {     //獲取設置     options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)     //判斷檢測     if (containerType !== 'string')        /**      * ajax響應回調注冊      */     //beforeSend     options.beforeSend = function(xhr, settings) {         //設置pjax頭信息,供后端做兼容處理         xhr.setRequestHeader('X-PJAX', 'true')         xhr.setRequestHeader('X-PJAX-Container', options.container)         //設置超時     }     //complete     options.complete = function(xhr, textStatus) {        //綁定pjax:complete事件        fire('pjax:complete', [xhr, textStatus, options])        //綁定pjax:end事件        fire('pjax:end', [xhr, options])     }     //error     options.error = function(xhr, textStatus, errorThrown) {        //綁定pjax:error事件        fire('pjax:error', [xhr, textStatus, errorThrown, options])     }     //success,重點     options.success = function(data, status, xhr) {        //判斷檢測        if (currentVersion && latestVersion && currentVersion !== latestVersion)        ... ...        window.history.replaceState(pjax.state, container.title, container.url)        //綁定pjax:beforeReplace事件        fire('pjax:beforeReplace', [container.contents, options], {            state: pjax.state,            previousState: previousState        })        //渲染頁面        context.html(container.contents)        //綁定pjax:success事件        fire('pjax:success', [data, status, xhr, options])     }     //初始化ajax     var xhr = pjax.xhr = $.ajax(options)     if (xhr.readyState > 0) {        //緩存頁面cache        cachePush(pjax.state.id, [options.container, cloneContents(context)])        //pushState        window.history.pushState(null, "", options.requestUrl)        //綁定pjax:start事件        fire('pjax:start', [xhr, options])        //綁定pjax:send事件        fire('pjax:send', [xhr, options])     }     //返回jQuery對象     return pjax.xhr }
        • 回調函數

        1) handleClick()

        // Examples // //   $(document).on('click', 'a', $.pjax.click) //   // is the same as //   $(document).pjax('a') // // Returns nothing. function handleClick(event, container, options) {     options = optionsFor(container, options)     //環境檢測     if (link.tagName.toUpperCase() !== 'A')     ... ...     //綁定pjax:click事件     var clickEvent = $.Event('pjax:click')     $link.trigger(clickEvent, [opts])     //執行pjax     pjax(opts)     //成功則阻止默認行為     event.preventDefault()     //綁定pjax:clicked事件     $link.trigger('pjax:clicked', [opts]) }

        2)handleSubmit()

        // Examples // //  $(document).on('submit', 'form', function(event) { //    $.pjax.submit(event, '[data-pjax-container]') //  }) // // Returns nothing. function handleSubmit(event, container, options) {     options = optionsFor(container, options)     //環境檢測     if (form.tagName.toUpperCase() !== 'FORM')     ... ...     //默認配置     var defaults = {         type: ($form.attr('method') || 'GET').toUpperCase(),         url: $form.attr('action'),         container: $form.attr('data-pjax'),         target: form     }     if (defaults.type !== 'GET' && window.FormData !== undefined) {         //POST時data域         defaults.data = new FormData(form)     }     //執行pjax     pjax($.extend({}, defaults, options))     //成功則阻止默認行為     event.preventDefault() }

        3)pjaxReload()

        // Reload current page with pjax. function pjaxReload(container, options) {     var defaults = {         //當前url         url: window.location.href,         push: false,         replace: true,         scrollTo: false     }     //執行pjax     return pjax($.extend(defaults, optionsFor(container, options))) }

        4)onPjaxPopstate()

        // popstate handler takes care of the back and forward buttons function onPjaxPopstate(event) {      //環境監測      if (state && state.container)      ... ...      //獲取頁面cache      var cache = cacheMapping[state.id] || []      //綁定pjax:popstate事件      var popstateEvent = $.Event('pjax:popstate', {          state: state,          direction: direction      })      container.trigger(popstateEvent)      if (contents) {          //有頁面cache,直接渲染頁面          //綁定pjax:start事件      container.trigger('pjax:start', [null, options])      //綁定pjax:beforeReplace事件      var beforeReplaceEvent = $.Event('pjax:beforeReplace', {               state: state,               previousState: previousState          })          container.trigger(beforeReplaceEvent, [contents, options])          //渲染頁面          container.html(contents)          //綁定pjax:end事件          container.trigger('pjax:end', [null, options])      } else {          //無頁面cache,執行pjax          pjax(options)      } }

        pjax使用

        經過上述分析,就可以很容易使用 pjax 了。

        客戶端

        pjax 支持 options 配置和事件機制。

        • options配置
        參數名 默認值 說明
        timeout 650 ajax 超時時間(單位 ms),超時后會執行默認的頁面跳轉,所以超時時間不應過短,不過一般不需要設置
        push true 使用 window.history.pushState 改變地址欄 url(會添加新的歷史記錄)
        replace false 使用 window.history.replaceState 改變地址欄 url(不會添加歷史記錄)
        maxCacheLength 20 緩存的歷史頁面個數(pjax 加載新頁面前會把原頁面的內容緩存起來,緩存加載后其中的腳本會再次執行)
        version 是一個函數,返回當前頁面的 pjax-version,即頁面中 標簽內容。使用 response.setHeader(“X-PJAX-Version”, “”) 設置與當前頁面不同的版本號,可強制頁面跳轉而不是局部刷新
        scrollTo 0 頁面加載后垂直滾動距離(與原頁面保持一致可使過度效果更平滑)
        type “GET” ajax 的參數,http 請求方式
        dataType “html” ajax 的參數,響應內容的 Content-Type
        container 用于查找容器的 CSS 選擇器,[container] 參數沒有指定時使用
        url link.href 要跳轉的連接,默認 a 標簽的 href 屬性
        fragment 使用響應內容的指定部分(css 選擇器)填充頁面,服務端不進行處理導致全頁面請求的時候需要使用該參數,簡單的說就是對請求到的頁面做截取
        • pjax事件

        為了方便擴展,pjax 支持一些預定義的事件。

        事件名 支持取消 參數 說明
        pjax:click options 點擊按鈕時觸發。可調用 e.preventDefault() 取消 pjaxa
        pjax:beforeSend xhr, options ajax 執行 beforeSend 函數時觸發,可在回調函數中設置額外的請求頭參數。可調用 e.preventDefault() 取消 pjax
        pjax:start xhr, options pjax 開始(與服務器連接建立后觸發)
        pjax:send xhr, options pjax:start之后觸發
        pjax:clicked options ajax 請求開始后觸發
        pjax:beforeReplace contents, options ajax請求成功,內容替換渲染前觸發
        pjax:success data, status, xhr, options 內容替換成功后觸發
        pjax:timeout xhr, options ajax 請求超時后觸發。可調用 e.preventDefault() 繼續等待 ajax 請求結束
        pjax:error xhr, textStatus, error, options ajax 請求失敗后觸發。默認失敗后會跳轉 url,如要阻止跳轉可調用 e.preventDefault()
        pjax:complete xhr, textStatus, options ajax請求結束后觸發,不管成功還是失敗
        pjax:end xhr, options pjax所有事件結束后觸發
        pjax:popstate forward / back(前進/后退)
        pjax:start null, options pjax開始
        pjax:beforeReplace contents, options 內容替換渲染前觸發,如果緩存了要導航頁面的內容則使用緩存,否則使用pjax加載
        pjax:end null, options pjax結束

        客戶端通過以下 2 個步驟就可以使用 pjax :

        1. 引入jquery 和 jquery.pjax.js
        2. 注冊事件

        JS

        <script src="jquery.pjax.js"></script>  /**  * 方式1 監聽按鈕父節點事件  */ $(document).pjax(selector, [container], options); /**  * 方式2 直接監聽按鈕,可以不用指定容器,默認使用按鈕的data-pjax屬性值查找容器  */ $("a[data-pjax]").pjax(); /**  * 方式3 主動綁定點擊事件監聽  */ $(document).on('click', 'a', $.pjax.click); $(document).on('click', 'a', function(event) {     //獲取container     var container = $(this).closest('[data-pjax-container]');     //click回調     $.pjax.click(event, container); }); /**  * 方式4 主動綁定表單提交事件監聽  */ $(document).on('submit', 'form', function(event) {     //獲取container     var container = $(this).closest('[data-pjax-container]');     //submit回調     $.pjax.submit(event, container); }); /**  * 方式5 加載內容到指定容器  */ $.pjax({url: this.href, container: '#main'}); /**  * 方式6 重新加載當前頁面容器的內容  */ $.pjax.reload('#container');

        YII

        在 Yii 中,已經將 pjax 封裝成了 widgets,故在渲染時如下使用即可:

        //view <?php Pjax::begin(); ?> ... ... <?php Pjax::end(); ?>

        pjax 封裝成的 widgets 源碼文件widgets/Pjax.php ,事件注冊部分如下:

        public function registerClientScript() {     //a標簽的click     if ($this->linkSelector !== false) {         $linkSelector = Json::htmlEncode($this->linkSelector !== null ? $this->linkSelector : '#' . $id . ' a');         $js .= "jQuery(document).pjax($linkSelector, "#$id", $options);";     }     //form表單的submit     if ($this->formSelector !== false) {         $formSelector = Json::htmlEncode($this->formSelector !== null ? $this->formSelector : '#' . $id . ' form[data-pjax]');         $submitEvent = Json::htmlEncode($this->submitEvent);         $js .= "njQuery(document).on($submitEvent, $formSelector, function (event) {jQuery.pjax.submit(event, '#$id', $options);});";     }     $view->registerJs($js); }

        服務端

        由于只是 HTML5 支持 pjax,所以后端需要做兼容處理。通過 X-PJAX 頭信息可得知客戶端是否支持 pjax,如果支持,則只返回局部頁面,否則 a 鏈接默認跳轉,返回整個頁面。

        /**  * IndexController示例  */ public function actionIndex() {     $dataProvider = new CActiveDataProvider('Article', array(         'criteria' => array('order' => 'create_time DESC')     ));     //存在X-Pjax頭,支持pjax     if (Yii::$app->getRequest()->getHeaders()->get('X-Pjax')) {         //返回局部頁面         $this->renderPartial('index', array(             'dataProvider' => $dataProvider,         ));     } else {         //返回整個頁面         $this->render('index', array(             'dataProvider' => $dataProvider,         ));     } }

        pjax失效情況

        在以下 9 種情況時候 pjax 會失效,源碼部分如下:

        //click回調 function handleClick(event, container, options) {     ...     // 1. 點擊的事件源不是a標簽。a標簽可以對舊版本瀏覽器的兼容,因此不建議使用其他標簽注冊事件     if (link.tagName.toUpperCase() !== 'A')         throw "$.fn.pjax or $.pjax.click requires an anchor element"     // 2. 使用鼠標滾輪點擊、點擊超鏈接的同時按下Shift、Ctrl、Alt和Meta     if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)         return     // 3. 跨域     if (location.protocol !== link.protocol || location.hostname !== link.hostname)         return     // 4. 當前頁面的錨點定位     if (link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location))         return     // 5. 已經阻止元素發生默認的行為     if (event.isDefaultPrevented())         return     ...     var clickEvent = $.Event('pjax:click')     $(link).trigger(clickEvent, [opts])     // 6. pjax:click事件回調中已經阻止元素發生默認的行為     if (!clickEvent.isDefaultPrevented()) {         pjax(opts)     } } //pjax function pjax(options) {     options.beforeSend = function(xhr, settings) {         //7. ajx超時        timeoutTimer = setTimeout(function() {        if (fire('pjax:timeout', [xhr, options]))            xhr.abort('timeout')        }, settings.timeout)     }     options.success = function(data, status, xhr) {     //8. 當前頁面和請求的新頁面版本不一致     if (currentVersion && latestVersion && currentVersion !== latestVersion) {        return     }     //9. ajax失敗     context.html(container.contents) }

        其他方案

        除了使用 pjax 解決局部刷新并支持前進和后退問題外,也可以使用 browserstate/history.js + ajax 方案來實現

        推薦教程:《PHP》《JS教程》

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 成人免费精品网站在线观看影片| 国内精品久久久久久久久电影网| 国产精品乱伦| 国产亚洲精品精华液| 欧美亚洲成人精品| 国产精品部在线观看| 成人精品在线视频| 欧美精品久久久久久久自慰| 日韩精品毛片| 国产精品亚洲w码日韩中文| 久草视频在线这里精品| 国产亚洲精品美女久久久| 亚洲日韩精品一区二区三区| 三级国产精品| 欧美精品91欧美日韩操| 国产成人精品久久亚洲高清不卡 | 国产a∨精品一区二区三区不卡| 亚洲国产精品一区二区成人片国内| 精品一区二区三区高清免费观看| 97久久精品人人做人人爽| 老司机69精品成免费视频| 国产成人精品天堂| 成人久久精品一区二区三区| 国产精品丝袜一区二区三区| 精品欧洲av无码一区二区三区 | 亚洲国产精品无码专区影院| 久久97久久97精品免视看| 国产精品亚洲w码日韩中文| 国产精品电影在线| 国产精品成人一区二区| 国产精品你懂的在线播放| 91精品国产91久久久久久蜜臀| 日本精品不卡视频| 亚洲国产精品国自产拍电影| 桃花岛精品亚洲国产成人| 亚洲午夜精品一区二区| 99热成人精品免费久久| 午夜精品一区二区三区在线视| 久久er国产精品免费观看2| 国产亚洲精品国看不卡| 国内精品在线视频|