PJAX效果javascript
經過url能夠跟蹤ajax的動態加載內容。這種技術尤爲在two step view佈局的視圖中有很大的好處。無刷新加載頁面,意味着響應速度和用戶體驗獲得了極大的提高,在靜態腳本和通用模塊比較多的狀況下,最大程度上節省了重用部分的開銷。應用例子能夠參考如今的google+、facebook和新版微博,一樣是基於html5的pushState實現。g plus的表現最爲明顯,點擊導航欄地址,箭頭隨目標移動,同時加載的頁面淡入,效果很炫。php
Dirty url 和 Clean urlhtml
在pjax出現以前,要實現頁面的無刷新加載並經過url能夠追蹤,須要瀏覽器支持window.location.hash屬性。經過判斷url#錨後記錄的地址來決定須要加載的內容,具體的構建方法是寫一個hashchange的監視函數,當觸發到hash改變時便判斷加載內容。它的不足在於,對於低版本的瀏覽器例如ie6不支持hash,須要另外構建一個iframe來記錄歷史url實現前進和後退。最大的問題,即是#後生成的內容不會被搜索引擎索引到,google以前提供了解決方案,提倡使用#!把地址引導到一個?escape_fragment=url的請求地址中,我在twitter、facebook、人人、新浪微博和已經關閉了的豆瓣說中都看見曾經或正在使用這種hash bang。經過#!來實現無刷新加載的url,因爲通常的方法不容易被搜索引擎收錄(例如國內百度),稱其爲dirty url,相對而言,pjax可以使用clean url獲得一樣效果,並能很好地兼容各類瀏覽器,是如今最爲適合的方法前端
使用PHP+jQuery實現PJAXhtml5
不須要從頭編寫基於pushState的javascript插件,由於jQuery已有項目把它開源出來,並且很輕易便能實現。目前我已經在開發中的項目裏引入,並且很好地在原有的基礎上兼容,況且新版微博的推廣,我但願讓觀衆看到,我用完以後是這個樣子,大家用完以後也會是這個樣子java
開始前的準備:jquery
1. jQuery libray http://code.jquery.com/jquery-1.8.2.min.jsgit
2. 基於jQ的pjax插件(github上的開源項目) https://github.com/defunkt/jquery-pjax github
3. PHP項目代碼(方便分享,本文使用codeigniter和yaf框架演示,實際開發中大同小異)web
一 前端實現
最最最基本須要先引入jquery庫,在頁頭中引入腳本地址 (sina的cdn)
<script src=」http://lib.sinaapp.com/js/jquery/1.8/jquery.min.js」></script>
根據第二條中的項目插件,加入jq的pjax插件地址
<script src=」BASEURL_TO_PJAX_PLUGIN」></script>
接下來,咱們來看看github上做者的說明,更直接,能夠直接打開插件看代碼。最簡單的使用方法以下
$('#main').pjax('a')
上述語句表示,點擊dom中的任何a標籤,將會發起pjax請求並將內容替換到id爲main的頁面元素內
使用實在簡單,並且jquery-pjax這個插件封裝得很好,絕對能夠按照你的喜愛來定製(例如複製g plus的效果),下面是一個整合以上步驟的基本html示例代碼
<html> <head> <script type="text/javascript" src="http://lib.sinaapp.com/js/jquery/1.8/jquery.min.js"></script> <script type="text/javascript" src="/js/jquery-pjax.js"></script> </head> <body> <script type="text/javascript"> $(document).ready(function(){ $("#main").pjax("a"); }); </script> <div id="nav"> <a href="/test1">test1</a><a href="/test2">test2</a> </div> <div id="main">替換的內容</div> </body> </html>
最終的目的,就是點擊地址爲/test1和/test2的a標籤時,經過ajax返回的結果將id爲main的div內的文字替換爲相應的地址內容,url自動更新,同時頁面不會從新載入。下面開始實現後端要處理的內容
二.PHP端的實現
php端須要處理的任務主要是兩件:1.實現layout視圖佈局 2.判斷pjax過來的請求
layout使每一個視圖的輸出都是在一個公用模版之上。
CI的實現:
寫一個自定義的layout library文件:
<?php /* * * -------------------------------------------------------------------- * 視圖佈局類 * -------------------------------------------------------------------- * * @author ekin.cen <weibo.com/2839892994> * */ class Layout{ public $obj; public $disable_layout = FALSE; protected $_layout; protected $_layout_dir = 'layout'; protected $_template_dir = 'templates'; protected $_data = array(); public function __construct() { $this->obj = &get_instance(); } public function set_layout($layout) { $this->_layout = $layout; return $this; } public function set_layout_dir($dirname) { $this->_layout_dir = $dirname; return $this; } public function set_template_dir($dirname) { $this->_template_dir = $dirname; return $this; } public function view($view, $data = NULL, $return = FALSE) { $view = $this->_template_dir . DIRECTORY_SEPARATOR . $view; $this->_layout = $this->_layout_dir . DIRECTORY_SEPARATOR . $this->_layout; if ($this->_data) { $data = $data ? array_merge($this->_data, $data) : $this->_data; } if ($this->disable_layout) { echo $this->obj->load->view($view, $data, TRUE); } else { $data = array('TEMPLATE_CONTENT' => $this->obj->load->view($view, $data, TRUE)); $this->obj->load->view($this->_layout,$data,$return); } } public function assign($key, $value = null) { $data = is_array($key) ? $key : array($key => $value); $this->_data = array_merge($data, $this->_data); return $this; } } /* End of file */
將以上文件命名未Layout.php放在application/libraries文件夾中,並在autoload文件中加入:
$autoload['libraries'] = array('layout');
調用時,在通常的controller中輸出視圖的方法
$this->load->view(TEMPLATE_PATH,$data);
變成:
$this->layout->view(TEMPLATE_PATH,$data);
以上Layout文件中視圖模版的輸出路徑默認在 /view/templates/,layout模版對應的位置爲/view/layout/ ,在/view/layout中新建一個模版文件(layout_test.php), 代碼以下:
<html> <head> <script type="text/javascript" src="http://lib.sinaapp.com/js/jquery/1.8/jquery.min.js"></script> <script type="text/javascript" src="/js/jquery-pjax.js"></script> </head> <body> <script type="text/javascript"> $(document).ready(function(){ $("#main").pjax("a"); }); </script> <div id="nav"> <a href="/test1">test1</a><a href="/test2">test2</a> </div> <div id="main"><?=$LAYOUT_CONTENT;?></div> </body> </html>
上面只是將html模版中要替換的內容變成了咱們控制器須要輸出的視圖模版內容($LAYOUT_CONTENT)。
到目前爲止已經能夠在CI中使用layout,剩下的就是在前端控制器中判斷請求類型,若是存在$_SERVER['HTTP_X_PJAX']類型,就不輸出layout,直接返回組件的渲染內容。
使用自定義的控制器,在application/core文件夾中新建文件 MY_Controller(默認的前綴根據配置定義),添加以下代碼:
<?php /* * * @author ekin.cen<weibo.com/2839892994> * */ class MY_Controller extends CI_Controller { //默認的layout模版 protected $_layout = 'layout_test'; public function __construct() { parent::__construct(); $this->_initialize(); } protected function _initialize() { //如設置了autoload文件,則此步省略 $this->_set_layout(); // check if is pjax request if (array_key_exists('HTTP_X_PJAX', $_SERVER) && $_SERVER['HTTP_X_PJAX']) { $this->layout->disable_layout = true; } } protected function _set_layout() { $this->load->library('layout'); $this->layout->set_layout($this->_layout); } } /* End of file MY_Controller.php */
到此爲止,已經準備好並能測試效果了 :)
編寫兩個測試控制器test1和test2, 經過layout方法輸出視圖
class Test1 extends MY_Controller{ public function index(){ $this->layout->view('test1'); } }
打開瀏覽器查看網絡請求狀態
上圖是點擊地址爲「/test」的a標籤後獲得的pjax請求,說明整合成功
--------------------------------------
jquery-pjax使用中須要注意的事項:
1.返回的模版內容不能爲純文本,須要用html標籤包裹
2.插件的使用方法,詳情參考github的項目說明,更新後使用方法或有不一樣
3.對於不支持pushstate的低版本瀏覽器,pjax插件會自動判斷並使用傳統的頁面加載模式
4.當一個頁面的pjax請求時間超過設定時間時,會使用刷新來加載,須要調整插件內的相關參數
目前整理了適用於CI和yaf的demo,CI論壇的傳送門地址包含下載 http://codeigniter.org.cn/forums/thread-14433-1-1.html