搜索全站
主题 18 分钟阅读

WordPress 搜索结果页优化:空结果提示与推荐内容实现

搜不到东西就只剩一句「没有找到」?本文讲清搜索查询的执行逻辑,给出 search.php 的完整改造、空结果兜底推荐、pre_get_posts 限定搜索范围与 REST 搜索补全的实现代码。

U
UFO_PF 2019-09-13 · 阅读 18 分钟
Wo 主题
阅读提示
本文约 7,089 字,预计阅读 18 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:站内搜索是访客主动表达需求的地方,却被做得最敷衍

访客愿意在搜索框里输入关键词,说明他有明确需求——这是全站转化率最高的场景之一。但多数主题的搜索结果页长这样:顶部一行「搜索结果:xxx」,下面列出十篇文章标题,仅此而已。搜不到时只有一句冷冰冰的「抱歉,没有找到相关内容」。于是访客关掉页面走人,你连他想要什么都不知道。

搜索结果页有三个可以立刻改善的点:空结果时的兜底(给出近义词建议、热门内容、搜索技巧)、结果质量(限定搜索范围、排除无用内容、按相关性排序)、结果呈现(显示摘要、分类、日期,而不是只有标题)。WordPress 的默认搜索只查 post_title、post_content、post_excerpt 三个字段,且不支持分词(中文尤其吃亏),这些都是可以在主题层面改善的。

原理分析:默认搜索查什么、怎么排序

WordPress 的搜索由 WP_Query 的 s 参数驱动,SQL 层面会生成类似 AND ((wp_posts.post_title LIKE '%关键词%') OR (wp_posts.post_excerpt LIKE '%关键词%') OR (wp_posts.post_content LIKE '%关键词%')) 的条件,并按 post_date 或相关性排序。这带来几个已知短板:

短板 表现 可行对策
只查标题/摘要/正文 自定义字段、分类名搜不到 用 posts_search 扩展 SQL,或换搜索插件
中文无分词 搜「WordPress 主题优化」几乎无结果 依赖插件或外部搜索服务
按日期而非相关性排序 最相关的旧文章排在后面 用 posts_orderby 调整,或换引擎
搜索页面、附件等类型 结果里混入无关类型 用 pre_get_posts 限定 post_type

主题能做的、不需要插件的改进有三类:一是用 pre_get_posts 限定搜索范围(只搜文章、排除某些分类、限制条数);二是用 posts_search 或 posts_where 给 SQL 加条件(比如把自定义字段纳入搜索);三是改造 search.php 的呈现与空结果兜底。

如果搜索量较大且中文分词是刚需,别硬撑——换用专门的搜索方案(如支持中文分词的搜索插件或外部搜索服务)更划算。但无论底层用什么引擎,search.php 这一层的体验改造都是通用且必须做的。

另外值得利用的是 REST 搜索端点 /wp-json/wp/v2/search,它能给搜索框做实时补全建议,实现成本低、效果明显。

保姆级实操

  1. 建或改 search.php:没有就新建,WordPress 会优先使用它。
  2. 显示查询词与结果数:用 get_search_query() 和 $wp_query->found_posts,让访客知道搜到了什么。
  3. 限定搜索范围:pre_get_posts 里只对前台搜索生效,排除页面与指定分类。
  4. 结果项补信息:显示分类、日期、摘要,而不是只有标题。
  5. 空结果兜底:found_posts 为 0 时输出热门文章、分类入口和搜索建议。
  6. 加搜索补全:用 REST 端点做简单的下拉建议。
  7. 避免被收录:搜索结果页通常应设为 noindex,避免产生大量低质量页面。
<?php
// 子主题 functions.php:只在前台搜索时限定范围
add_action( 'pre_get_posts', 'mimangfei_search_scope' );
function mimangfei_search_scope( $query ) {
    if ( is_admin() || ! $query->is_main_query() ) {
        return;
    }

    if ( $query->is_search() ) {
        $query->set( 'post_type', 'post' );
        $query->set( 'posts_per_page', 12 );
        $query->set( 'cat', '-35,-41' ); // 排除「公告」「广告」等分类
        $query->set( 'post_status', 'publish' );
    }
}
<?php
/**
 * search.php
 *
 * @package lumen-mimangfei-child
 */

get_header();
global $wp_query;
$keyword = get_search_query();
$found   = (int) $wp_query->found_posts;
?>
<main id="primary" class="site-main site-main--search">

    <header class="search-header">
        <h1>搜索:<?php echo esc_html( $keyword ); ?></h1>
        <p class="search-count">
            <?php
            printf(
                '共找到 %d 条结果',
                $found
            );
            ?>
        </p>
        <?php get_search_form(); ?>
    </header>

    <?php if ( have_posts() ) : ?>

        <ul class="search-results">
            <?php
            while ( have_posts() ) :
                the_post();
                ?>
                <li class="search-result">
                    <h2 class="search-result__title">
                        <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
                    </h2>
                    <div class="search-result__meta">
                        <?php echo esc_html( get_the_date() ); ?>
                        <?php
                        $cats = get_the_category();
                        if ( $cats ) {
                            echo ' · ' . esc_html( $cats[0]->name );
                        }
                        ?>
                    </div>
                    <p class="search-result__excerpt"><?php echo esc_html( get_the_excerpt() ); ?></p>
                </li>
                <?php
            endwhile;
            ?>
        </ul>

        <?php the_posts_pagination(); ?>

    <?php else : ?>

        <section class="search-empty">
            <h2>没有找到与「<?php echo esc_html( $keyword ); ?>」相关的内容</h2>
            <p>可以试试:换用更短的关键词、检查是否有错别字,或者看看下面这些热门内容。</p>

            <h3>热门文章</h3>
            <ul>
                <?php
                $q = new WP_Query( array(
                    'posts_per_page'      => 6,
                    'orderby'             => 'comment_count',
                    'ignore_sticky_posts' => true,
                    'no_found_rows'       => true,
                ) );
                while ( $q->have_posts() ) :
                    $q->the_post();
                    ?>
                    <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
                    <?php
                endwhile;
                wp_reset_postdata();
                ?>
            </ul>

            <h3>热门标签</h3>
            <?php wp_tag_cloud( array( 'number' => 20, 'orderby' => 'count', 'order' => 'DESC' ) ); ?>
        </section>

    <?php endif; ?>

</main>
<?php
get_footer();
// 搜索框实时建议:调用 REST 搜索端点
const input = document.querySelector( 'input[name="s"]' );
if ( input ) {
    let timer;
    input.addEventListener( 'input', () => {
        clearTimeout( timer );
        const kw = input.value.trim();
        if ( kw.length < 2 ) {
            return;
        }
        timer = setTimeout( async () => {
            const res = await fetch(
                `${ window.location.origin }/wp-json/wp/v2/search?search=${ encodeURIComponent( kw ) }&per_page=5&_fields=id,title,url`
            );
            const list = await res.json();
            renderSuggestions( list );
        }, 250 );
    } );
}

function renderSuggestions( list ) {
    const box = document.querySelector( '[data-search-suggestions]' );
    if ( ! box ) {
        return;
    }
    box.innerHTML = list
        .map( ( item ) => `<a href="${ item.url }">${ item.title }</a>` )
        .join( '' );
}
<?php
// 搜索结果页通常应设 noindex,避免产生大量低质量索引页
add_filter( 'wp_robots', 'mimangfei_search_noindex' );
function mimangfei_search_noindex( $robots ) {
    if ( is_search() ) {
        $robots['noindex'] = true;
        $robots['nofollow'] = true;
    }
    return $robots;
}

常见踩坑

  1. pre_get_posts 没判断 is_main_query():把后台、侧边栏的小查询也改了,出现莫名其妙的结果。两个判断缺一不可。
  2. 空结果页直接 return:只在 have_posts() 为 true 时输出内容,else 分支没做,访客看到空白页。空结果分支必须认真写。
  3. 案例:某站搜索「发票」永远返回 0 条,但文章里明明有相关内容。原因是这些文章在分类 35(「站内公告」)里,而主题早在半年前用 pre_get_posts 排除了公告分类,没人记得。最后把排除逻辑集中到一个带注释的配置数组里,并加了后台说明。教训:搜索排除规则要集中管理并写注释,否则半年后没人知道为什么搜不到。
  4. 搜索结果页被大量收录:没设 noindex,Google 收录了几千个 ?s=xxx 页面,全是低质量页面。用 wp_robots 过滤器加 noindex,并在 robots.txt 里屏蔽 /?s=。
  5. get_search_query() 没转义:直接 echo 会把用户输入原样输出,存在 XSS 风险。必须用 esc_html()。
  6. REST 端点未授权问题:/wp-json/wp/v2/search 默认只返回公开内容,若站点启用了鉴权插件需要确认可匿名访问,否则补全功能失效。
  7. 中文分词问题没解决就盲目优化:底层搜不到,前端做得再好也没用。中文站点应尽早评估专门的搜索方案。
  8. 搜索表单被缓存:全页缓存把带搜索结果的 HTML 缓存了,不同关键词拿到同一份结果。搜索页必须排除缓存。相关取舍可结合 主题首页静态页 vs 最新文章的选择 一起规划缓存策略。

总结

搜索结果页的判断标准是:搜到了能快速定位目标,搜不到也能留下来继续逛。行动清单:建 search.php → 显示查询词与结果数 → pre_get_posts 限定范围并集中管理排除规则 → 结果项补摘要、分类、日期 → 空结果分支加热门文章与标签云 → 用 REST 端点做补全 → 给搜索页加 noindex → 排除页面缓存。做完这套,还能顺手处理 主题 404 页面的自定义设计,把两条「兜底通路」的体验统一起来。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。