WP_Query 完全攻略:參數、迴圈、效能調校,自訂文章列表不再用猜的
☰ 目錄 table-of-contents.md
WP_Query 是什麼?
WP_Query 是 WordPress 內建的 PHP 查詢類別,用來向資料庫請求文章、頁面或任何自訂文章類型(CPT)的資料。當你需要在主題或外掛裡做出「自訂的文章列表」(首頁最新消息、側邊欄熱門文章、產品篩選頁),它就是官方推薦、最標準也最靈活的工具。
本文要解決三個最常見的痛點:一、WP_Query 與 get_posts()、query_posts() 到底差在哪、該用哪個;二、查詢參數與「The Loop」迴圈的正確寫法(包含最常被忘記的收尾步驟);三、當資料量變大時,如何用幾個關鍵參數把查詢效能拉回來。讀完你就能精準組合查詢條件,又不會干擾 WordPress 的主查詢流程。
這些年我在無數次 code review 裡看過太多「能跑但會拖垮網站」的查詢,下面把這套心法一次講清楚。
WP_Query、get_posts()、query_posts() 該用哪一個?
這三個都能撈文章,但定位完全不同。先記結論,再看細節:
WP_Query:最底層、最完整、最靈活的查詢類別。需要自訂迴圈、複雜條件、或要控制分頁時,用它就對了。get_posts():WP_Query的簡化版包裝函式,直接回傳一個文章物件的陣列。適合「拿一小批資料、簡單跑個foreach」的場景,但彈性不如直接用WP_Query。query_posts():請忘了它。它會直接覆蓋掉當前頁面的主查詢(Main Query),極容易在分頁等功能上引發難以追查的 Bug。除非你非常清楚自己在做什麼,否則永遠不要用它。
那「修改現有列表」時呢?用 pre_get_posts,不要另開查詢
這裡補一個關鍵觀念:如果你的目的不是「另外做一個列表」,而是要調整頁面本來就會跑的那個主查詢(例如讓分類頁每頁顯示不同數量、改變首頁的排序),正確做法不是新建 WP_Query,更不是 query_posts(),而是掛在 pre_get_posts 這個 action 上修改查詢條件。
原因很單純:pre_get_posts 是在查詢真正送進資料庫之前動手,不會產生額外查詢,也不會破壞分頁。判斷原則記成一句話就好:「要做新列表 → WP_Query;要改現有列表 → pre_get_posts。」
WP_Query 的核心結構:參數 + The Loop
要駕馭 WP_Query,只需掌握兩件事:設定「查詢參數」,以及運行「The Loop 迴圈」。直接看程式碼最快:
// 1. 設定你的查詢參數 (Arguments)
$args = array(
'post_type' => 'post', // 我們要查詢的是「文章」
'posts_per_page' => 5, // 每頁顯示 5 篇
'orderby' => 'date', // 依照日期排序
'order' => 'DESC', // 降冪排序 (最新的在前面)
);
// 2. 建立一個新的 WP_Query 實例 (Instance)
$the_query = new WP_Query( $args );
// 3. 運行 WordPress 迴圈 (The Loop)
if ( $the_query->have_posts() ) {
echo '<ul>';
while ( $the_query->have_posts() ) {
$the_query->the_post(); // 設定好當前文章的資料
// 在這裡顯示文章內容
echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
}
echo '</ul>';
} else {
// 如果沒有找到任何文章
echo '沒有找到任何文章。';
}
/* 4. 重設文章資料!非常重要! */
wp_reset_postdata();
整個流程就四步:定義規則($args)→ 發出請求(new WP_Query)→ 處理結果(The Loop)→ 清理現場(wp_reset_postdata)。
為什麼 wp_reset_postdata() 不能忘?
因為 the_post() 會去改動 WordPress 的全域變數 $post。如果你跑完迴圈不重設,全域 $post 就會停留在你迴圈的最後一筆資料上;接下來頁面其他需要文章資料的區塊(例如主內容、其他模板標籤)就會抓到錯的資料,畫面開始錯亂。這是初學者最常踩的坑,請務必刻在心裡。
關鍵查詢參數一覽
WP_Query 的強大來自它極其豐富的參數。以下是最常用的幾個:
| 參數 | 作用 | 常見值 |
|---|---|---|
post_type | 指定文章類型 | 'post'、'page'、自訂的 'product',或傳陣列查多種如 array('post','event') |
post_status | 文章狀態 | 預設 'publish';另有 'draft'、'pending'、'private' 等 |
posts_per_page | 每頁顯示幾篇 | 數字;-1 代表全部(效能風險高,慎用) |
orderby | 排序依據 | 'date'、'title'、'comment_count'、'rand',或搭配 meta_key 的 'meta_value' / 'meta_value_num' |
order | 排序方向 | 'ASC'(升冪)/ 'DESC'(降冪) |
meta_query | 依自訂欄位篩選 | ACF 等外掛複雜篩選器的基礎 |
tax_query | 依分類、標籤或自訂分類法篩選 | 分類頁、標籤頁、商品分類的核心 |
進階戰術:組合複雜查詢
真正的開發場景往往要同時套用多個條件。假設電商網站要找出:「電子產品」分類中,價格介於 1000 到 5000 之間,或評分為 5 顆星的商品。用 WP_Query 可以這樣寫:
$args = array(
'post_type' => 'product',
'posts_per_page' => 10,
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => 'electronics',
),
),
'meta_query' => array(
'relation' => 'OR', // 注意這裡!代表以下兩個條件滿足一個即可
array(
'key' => 'price',
'value' => array( 1000, 5000 ),
'type' => 'NUMERIC',
'compare' => 'BETWEEN',
),
array(
'key' => 'rating',
'value' => 5,
'type' => 'NUMERIC',
'compare' => '=',
),
),
);
$products_query = new WP_Query( $args );
// ... 後續的 The Loop ...
relation 的 AND / OR 怎麼想?
relation 參數決定同一個 meta_query(或 tax_query)裡多個子條件之間的邏輯關係:
'AND':所有子條件都要成立(預設值)。'OR':任一子條件成立即可。
更進階的是,meta_query 可以巢狀:在子條件裡再放一組帶有自己 relation 的陣列,就能組出「A 而且(B 或 C)」這類混合邏輯,像堆樂高一樣拼出你要的條件樹。不過要提醒:每多一個 meta_query 子條件,底層通常就會多一次 wp_postmeta 的關聯(JOIN),條件堆太多會直接反映在查詢成本上,這也帶到下一節的效能主題。
效能調校:別讓查詢拖垮網站
當資料量越來越大,一個沒優化的 WP_Query 就可能變成效能瓶頸。好消息是,很多時候只要加幾個參數,告訴 WordPress「我只要這些,其他別忙」,就能省下可觀的資料庫負擔。
四個可以立刻用的效能參數
'no_found_rows' => true:預設情況下,WP_Query會額外跑一次計數查詢,算出總共幾篇符合條件,好產生分頁資訊。如果這個查詢根本不需要分頁(例如側邊欄固定顯示最新 5 篇),加上它就能跳過這次計算,直接省一個查詢。'update_post_meta_cache' => false:若你在迴圈裡不會呼叫get_post_meta()取自訂欄位,設成false可避免 WordPress 預先載入所有文章的 meta 資料,文章一多時效果明顯。'update_post_term_cache' => false:同理,若迴圈裡不會用get_the_terms()之類取分類資訊,設成false也能減少不必要的查詢。'fields' => 'ids':如果你只需要文章的 ID 列表、不需要完整文章物件,這個參數會讓查詢變得極度輕量,特別適合批次處理的場景。
為什麼我的 meta_query 特別慢?從原理理解
很多「WP_Query 很慢」的案例,問題其實不在 WP_Query 本身,而在它最終翻譯成的 SQL 怎麼打資料庫。幾個通用原則值得記住:
- 沒有索引的欄位最致命。WordPress 的自訂欄位都存在
wp_postmeta這張「又長又窄」的表裡。當你用某個meta_key篩選、而資料庫在對應欄位上沒有適當索引時,MySQL 只能掃描整張wp_postmeta來逐筆比對,資料量一大就會明顯卡頓。 - 比較方式會影響能不能吃到索引。像
'compare' => 'LIKE'(尤其是前面就帶萬用字元的模糊比對)或'!='這類「否定 / 模糊」比較,本質上比'='難利用索引,能用精確比對就盡量用精確比對。 - 條件越多、JOIN 越多。如前一節所述,
meta_query子條件會疊加關聯成本;把篩選邏輯設計得精簡,往往比事後補救更有效。
結論很實際:效能調校要分兩層看——上層用 WP_Query 參數少做白工,下層則回到資料庫本身,確保常被查詢的欄位有合適的索引與表結構。兩者搭配,網站才會真的快起來。
總結:把 WP_Query 當成一種思維
WP_Query 不只是一個函式,而是一套「如何與資料庫溝通」的思維模式。把握三個重點,你就能游刃有餘:
- 選對工具:做新列表用
WP_Query、改現有列表用pre_get_posts、避開query_posts()。 - 寫對結構:參數 → 迴圈 → 別忘了
wp_reset_postdata()。 - 顧好效能:用參數少做白工,再從索引與表結構解決根本問題。
如果你對 WordPress 網站效能調校、客製化功能開發,或任何與 WP_Query 相關的疑難雜症感到頭痛,浪花科技團隊擁有豐富的實戰經驗。歡迎與我們聯繫,聊聊如何讓你的 WordPress 網站脫胎換骨。
延伸閱讀
常見問題
WP_Query、get_posts() 和 query_posts() 該用哪一個?
想修改現有列表(例如分類頁或首頁的查詢)該怎麼做?
為什麼 WP_Query 迴圈跑完一定要呼叫 wp_reset_postdata()?
meta_query 裡的 relation 參數 AND 與 OR 有什麼差別?
為什麼用了 meta_query 的 WP_Query 特別慢?
訂閱免費電子報
把 AI 自動化、企業系統設計與 WordPress / Laravel 開發的真實案例和可直接照做的技巧,整理成電子報寄給你。只寄精選內容、不灌垃圾信,一鍵就能退訂。