為什麼要動態載入文章?
1. 快速向訪客展示頁面
文章很容是包含大量文字和多媒體資源 (如: 圖片, 視頻, 音樂), 載入這些內容需要佔用很多的時間. 如果你的頁面上存在大量文章, 當訪客發現頁面久久沒有載入完成就感到不耐煩. 這是動態載入文章的主要目的.
2. 讓文章列表化
使頁面上的文章成為一個列表, 減少頁面的空間佔用, 訪客可以方便的移動到頁面下方, 提高舊文章被點擊的幾率. 並且你可以在頁面上放置更多的文章而不用擔心頁面過長.
為什麼不動態載入文章?
1. 對搜尋引擎不友好
搜尋引擎最佳化的目的是將有價值的東西盡量多的向搜尋爬蟲展示, 包括最新的文章內容. 只有標題的文章讓爬蟲只知道這個文章而不知其文章側重, 使用 JavaScript 輸出的文章內容未必可以被抓取和分析. 這些對 SEO 來說都是不好的.
後來發現, 如果你的網站有固定的文章類型, 沒有畢業在文章列表頁顯示太多文章內容, 表示影響不大.
2. 增加了請求次數
雖然將文章摺疊起來, 我們一般還是會想辦法向訪客顯示前面的幾篇文章. 這樣對使用者是友好的, 但是要增加請求的次數和資料庫訪問的次數.
後來我有選擇地顯示部分文章內容, 而且不是通過非同步載入的方式, 也就是說, 這個問題是可以通過簡單的修改解決掉的.
3. 一些外掛程式失效
因為需要自訂方法抓取文章, 如果不添加特殊處理, 很可能令部分 WordPress 外掛程式失效.
可以通過特殊處理解決掉, 以後文章中會提及.
動態載入文章的設計思路
1. 找到頁面上所有文章
為每個文章添加一個展開/摺疊按鈕
2. 向文章添加展開/摺疊按鈕
點擊按鈕, 如果文章內容沒有載入, 載入並展開文章內容.
點擊按鈕, 如果文章內容已經載入, 則展開/摺疊文章內容.
3. 載入文章內容
將文章的 id 發往後台, 在資料庫中找到相應的文章內容並進行格式化, 返迴響應顯示在頁面上.
JavaScript 處理程式碼分析
1. 找到頁面上所有文章
/ 在文檔載入完畢的時候遍曆所有匹配文章的元素jQuery(document).ready(function(){ jQuery('div.post').each(function() { // 如果元素相應位置是文章 ID var id = jQuery(this).attr('id'); if(/^post\-[0-9]+$/.test(id)) { // 則為每個文章添加一個展開/摺疊按鈕 ... } });});
2. 向文章添加展開/摺疊按鈕
toggle.toggle(function() { // 展開 // 如果文章內容為空白, 載入文章內容 if(jQuery('#' + id + ' .content').text() == '') { ... } // 顯示文章內容, 並切換按鈕樣式 jQuery('#' + id + ' .content').slideDown(); jQuery(this).removeClass('collapse').addClass('expand');},function() { // 摺疊 // 隱藏文章內容, 並切換按鈕樣式 jQuery('#' + id + ' .content').slideUp(); jQuery(this).removeClass('expand').addClass('collapse');// 將按鈕追加到文章標題前方}).prependTo(jQuery('#' + id + ' h2'));
3. 載入文章內容
// 取得文章 IDvar postId = id.slice(5);// 使用 AJAX 擷取並處理文章內容jQuery.ajax({ type: 'GET' ,url: '?action=load_post&id=' + postId ,cache: false ,dataType: 'html' ,contentType: 'application/json; charset=utf-8' // 取得返回內容之前顯示載入資訊 ,beforeSend: function(data){loadPostContent(id, '<p class="ajax-loader">Loading...</p>');} // 擷取文章內容成功, 更新文章內容 ,success: function(data){loadPostContent(id, data);} // 擷取文章內容失敗, 顯示出錯提示 ,error: function(data){loadPostContent(id, '<p>Oops, failed to load data.</p>');}});
幕後處理
處理思路
從前台傳到背景參數有兩個, 一個是 action ID, 用於確定使用的介面, 另一個是文章的 ID, 用於擷取文章對應的內容.
下面我們來分析一下wp-includes/post-template.php 的 get_the_content 方法.
function get_the_content($more_link_text = null, $stripteaser = 0) { global $id, $post, $more, $page, $pages, $multipage, $preview; // 設定 "查看全文" 的連結文案 if ( null === $more_link_text ) $more_link_text = __( '(more...)' ); // 返回內容 $output = ''; // More 標籤是否存在的標記位 $hasTeaser = false; // 如果文章要求輸入密碼, 並且在 Cookie 中找不到處理過的資訊, 則返回要求輸入密碼的查看錶單 if ( post_password_required($post) ) { $output = get_the_password_form(); return $output; } // 請求的文章片段對應的頁面大於最大頁數 (即文章片段不存在), 則返回最大頁碼的文章片段 if ( $page > count($pages) ) $page = count($pages); // 文章內容是最後分頁中的文章片段 $content = $pages[$page-1]; // 如果文中有 More 標籤, 要求切斷文章並輸出 "查看全文" 連結, 則重定義文章內容, 標記 More 標籤存在 if ( preg_match('/<!--more(.*?)?-->/', $content, $matches) ) { $content = explode($matches[0], $content, 2); if ( !empty($matches[1]) && !empty($more_link_text) ) $more_link_text = strip_tags(wp_kses_no_null(trim($matches[1]))); $hasTeaser = true; } else { $content = array($content); } // 如果進行了文章切斷處理, 且不存在分頁要求, if ( (false !== strpos($post->post_content, '<!-- noteaser -->') && ((!$multipage) || ($page==1))) ) $stripteaser = 1; // 擷取文章內容的第一部分; 如果在獨立文章存在 Read more 和切斷處理, 則文章內容為空白 $teaser = $content[0]; if ( ($more) && ($stripteaser) && ($hasTeaser) ) $teaser = ''; $output .= $teaser; // 如果文章分為多個片段, 在獨立文章中拼接上第二部分, 摘要內容中顯示 "閱讀全文" 連結 if ( count($content) > 1 ) { if ( $more ) { $output .= '<span id="more-' . $id . '"></span>' . $content[1]; } else { if ( ! empty($more_link_text) ) $output .= apply_filters( 'the_content_more_link', ' <a href="' . get_permalink() . "#more-$id\" class=\"more-link\">$more_link_text</a>", $more_link_text ); $output = force_balance_tags($output); } } if ( $preview ) // preview fix for javascript bug with foreign languages $output = preg_replace_callback('/\%u([0-9A-F]{4})/', create_function('$match', 'return "&#" . base_convert($match[1], 16, 10) . ";";'), $output); // 返迴文章內容 return $preview;}
你完全可以這樣想: 只要滿足一些傳入的參數, 去除一些不必要的, 更換一些可取代的, 將頁面返回改成輸出, 就是一個輸出文章內容的介面.
處理方法
如果我們暫時不考慮輸入密碼, 分頁等功能; 另外, 因為 More 和切斷功能不應該在展開文章內容中存在, 響應處理可以變得很簡單. 我們要做的事就這麼幾個:
1. 做出 action 對應的介面
2. 擷取指定文章的內容
3. 格式化文章內容
4. 返迴文章內容
多說無用, 直接上代碼, 加註釋:
function load_post() { // 如果 action ID 是 load_post, 並且傳入的必須參數存在, 則執行回應程式法 if($_GET['action'] == 'load_post' && $_GET['id'] != '') { $id = $_GET["id"]; $output = ''; // 擷取文章對象 global $wpdb, $post; $post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d LIMIT 1", $id)); // 如果指定 ID 的文章存在, 則對他進行格式化 if($post) { $content = $post->post_content; $output = balanceTags($content); $output = wpautop($output); } // 列印文章內容並中斷後面的處理 echo $output; die(); }}// 將介面加到 init 中add_action('init', 'load_post');