~/blog/wordpress-wp-query-ultimate-guide-from-parameters-to-performance.md
WordPress 開發與技巧 · 2025 / 08 / 15

WP_Query 完全攻略:參數、迴圈、效能調校,自訂文章列表不再用猜的

Eric — 浪花科技創辦人 / AI 架構師
Eric
浪花科技創辦人 · AI 架構師
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 不只是一個函式,而是一套「如何與資料庫溝通」的思維模式。把握三個重點,你就能游刃有餘:

  1. 選對工具:做新列表用 WP_Query、改現有列表用 pre_get_posts、避開 query_posts()
  2. 寫對結構:參數 → 迴圈 → 別忘了 wp_reset_postdata()
  3. 顧好效能:用參數少做白工,再從索引與表結構解決根本問題。

如果你對 WordPress 網站效能調校、客製化功能開發,或任何與 WP_Query 相關的疑難雜症感到頭痛,浪花科技團隊擁有豐富的實戰經驗。歡迎與我們聯繫,聊聊如何讓你的 WordPress 網站脫胎換骨。

延伸閱讀

// FAQ

常見問題

WP_Query、get_posts() 和 query_posts() 該用哪一個?
WP_Query 是最底層、最完整、最靈活的查詢類別,需要自訂迴圈、複雜條件或控制分頁時用它。get_posts() 是 WP_Query 的簡化包裝,直接回傳文章陣列,適合拿一小批資料簡單跑 foreach。query_posts() 應避免使用,它會覆蓋當前頁面的主查詢,容易在分頁等功能引發難以追查的 Bug。
想修改現有列表(例如分類頁或首頁的查詢)該怎麼做?
若目的是調整頁面本來就會跑的主查詢,正確做法是掛在 pre_get_posts 這個 action 上修改查詢條件,而不是新建 WP_Query 或用 query_posts()。pre_get_posts 在查詢送進資料庫之前動手,不會產生額外查詢也不會破壞分頁。原則是:要做新列表用 WP_Query,要改現有列表用 pre_get_posts。
為什麼 WP_Query 迴圈跑完一定要呼叫 wp_reset_postdata()?
因為 the_post() 會改動 WordPress 的全域變數 $post。若不重設,全域 $post 會停留在迴圈最後一筆資料上,導致頁面其他需要文章資料的區塊抓到錯誤資料而錯亂。這是初學者最常踩的坑,務必在迴圈結束後呼叫。
meta_query 裡的 relation 參數 AND 與 OR 有什麼差別?
relation 決定同一個 meta_query 裡多個子條件之間的邏輯關係:'AND' 表示所有子條件都要成立(預設值),'OR' 表示任一子條件成立即可。meta_query 還可巢狀,在子條件裡再放一組帶 relation 的陣列,組出「A 而且(B 或 C)」這類混合邏輯。
為什麼用了 meta_query 的 WP_Query 特別慢?
因為自訂欄位都存在又長又窄的 wp_postmeta 表,每多一個 meta_query 子條件,底層通常就多一次對 wp_postmeta 的關聯(JOIN),條件堆太多會直接反映在查詢成本上。對沒有索引的欄位做篩選最致命,這是多數查詢變慢的根源。
~/roamer-tech/newsletter // FREE
// newsletter

訂閱免費電子報

把 AI 自動化、企業系統設計與 WordPress / Laravel 開發的真實案例和可直接照做的技巧,整理成電子報寄給你。只寄精選內容、不灌垃圾信,一鍵就能退訂。

$
// final.exec()

準備好讓你的網站開始為你工作了嗎?