本文约 5,400 字,预计阅读 14 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:收录 200 篇,实际 400 篇,差在哪
一个典型的国内博客现象:发布量一直在涨,百度资源平台和 Google Search Console 里的”已编入索引”却卡在某个数字不动。排除掉内容质量问题后,最常见的结构性原因是分页链路断了。
默认主题(Twenty Twenty-Four 等)在列表页底部只输出 « 较早的文章 / 较新的文章 » 两个链接。Googlebot 从首页出发,每到一个列表页只能往下走一层,走到第十层基本就放弃。更麻烦的是归档页 /page/2/ 之后几乎没有内链支持,权重衰竭,深层文章如同埋在地下。数字分页把 1、2、3…N 全部铺出来,等于给每一层都追加了入口;同时在「第 3 页」Articles 里的大量深层次页面也能被重新发现。
实现方式两类:装 WP-PageNavi(作者 Lester ‘GaMerZ’ Chan,wordpress.org 安装量 80 万+,末尾虽多年未大更新但兼容性一直很好),或者用 WordPress 内置的 paginate_links() 自己画。后者更干净,没有多余 CSS/JS。
原理分析:分页函数的三种实现路径
| 方案 | 函数/调用 | 依赖 | 灵活性 |
|---|---|---|---|
| 主题默认 | the_posts_pagination() / posts_nav_link() |
无 | 低,只能改 prev_text/next_text |
| WP-PageNavi | wp_pagenavi() 模板标签 |
插件 + 自带 CSS | 中,后台可调页数、省略号、端点 |
原生 paginate_links() |
paginate_links( $args ) |
无 | 高,base/format/add_args 全开放 |
三者的底层都一样:读取 $GLOBALS['wp_query']->max_num_pages 这个总页数,再结合当前 paged 变量算偏移。所以不管用哪个,只要最终输出的 HTML 里保留了 href="/page/3/" 这样的真实链接,对收录的提升效果就是等价的。
需要注意 WordPress 的 paginate_links() 有个历史行为:base 默认会用到 %_% 和 %#% 两个占位符,配合 format 生成。如果你的站点开了 /%category%/%postname%/ 之外的自定义 permalink 结构,base 必须手写,否则会生成 /page/3/?s=xxx 这种混搭 URL。
另一条容易被忽略的线:分页 SEO。Google 早在 2019 年就宣布不再使用 rel="next"/rel="prev" 作为索引信号,但它仍然是有效的 HTML 提示;更重要的是不要把 /page/2/ 之后的页面 noindex,那样深层文章就彻底失去了进入索引的跳板——正确做法是正常 index,并用 rel=canonical 自指向(不要让第二页 canonical 指向首页,那会直接把自己从索引里划掉)。
保姆级实操
步骤 1:安装并接入 WP-PageNavi
后台装好后,找到主题的分页输出位置。Twenty 系列主题在 template-parts/content.php 之后、index.php 里:
// 找到类似这行,注释或替换掉
// the_posts_navigation();
// 换成 WP-PageNavi
if ( function_exists( 'wp_pagenavi' ) ) {
wp_pagenavi();
} else {
the_posts_navigation(); // 保留兜底,避免插件停用后前台崩
}
子主题改造务必用 remove_action / 覆写模板,不要直接改父主题文件。
步骤 2:后台基参
「设置 → PageNavi」里建议:
- Number of pages to show:10(不要设 0/All,深层列表会生成上百个
<a>) - Show “Page X of Y”:关闭,中文站信息冗余
- Style:选 Classic,别选 Drop-down List(下拉对爬虫不可见,且对用户不友好)
- 如果想套自定义 CSS,把「Use pagenavi-css.css」关掉,自己写样式
步骤 3:不装插件,用原生 paginate_links()
下面这段是我在多数站点上直接用的版本,支持中文文案、保留 query 参数(比如搜索页的 ?s=):
function mf_numeric_pagination( $wp_query = null ) {
if ( null === $wp_query ) {
global $wp_query;
}
$total = (int) $wp_query->max_num_pages;
if ( $total <= 1 ) {
return;
}
$current = max( 1, get_query_var( 'paged' ) ? (int) get_query_var( 'paged' ) : 1 );
$links = paginate_links( array(
'base' => str_replace( 999999999, '%#%', esc_url( get_pagenum_link( 999999999 ) ) ),
'format' => '?paged=%#%',
'current' => $current,
'total' => $total,
'mid_size' => 2,
'end_size' => 1,
'prev_text' => '« 上一页',
'next_text' => '下一页 »',
'type' => 'list',
) );
// 搜索页要把 s 参数带上
if ( is_search() ) {
$links = str_replace( '/page/', '/page/', $links );
}
echo '<nav class="mf-pagination" role="navigation" aria-label="分页导航">' . $links . '</nav>';
}
搜索结果页尤其要注意:paginate_links() 默认会丢掉 ?s=关键词,必须补上:
add_filter( 'paginate_links', function( $link ) {
if ( is_search() && ! empty( $_GET['s'] ) ) {
$link = add_query_arg( 's', rawurlencode( wp_unslash( $_GET['s'] ) ), $link );
}
return $link;
} );
步骤 4:替换到指定模板
// archive.php / index.php / search.php 里循环结束之后
mf_numeric_pagination();
WooCommerce 商店页的分页不用改,WC 自带 woocommerce_pagination() 输出的就是同样的结构,改 CSS 即可。
步骤 5:配套 CSS
.mf-pagination ul.page-numbers {
display: flex; flex-wrap: wrap; gap: 6px;
list-style: none; padding: 0; margin: 28px 0;
}
.mf-pagination li { margin: 0; }
.mf-pagination a,
.mf-pagination span {
display: inline-block; min-width: 34px; padding: 7px 11px;
border: 1px solid #e5e7eb; border-radius: 6px;
color: #374151; text-align: center; line-height: 1.2;
font-size: 14px; text-decoration: none;
}
.mf-pagination a:hover { border-color: #c0392b; color: #c0392b; }
.mf-pagination span.current {
background: #c0392b; border-color: #c0392b; color: #fff; font-weight: 600;
}
.mf-pagination .next, .mf-pagination .prev { border-color: transparent; }
步骤 6:验证输出
进任意归档页第 2 页,查看源码确认 <nav class="mf-pagination"> 里包含 href=".../page/3/" 真实链接(不是 javascript:void(0)),确认 <span class="current">2</span> 出现一次。
常见踩坑
max_num_pages恒等于 1:静态首页用自定义WP_Query时忘了传paged,正确写法是'paged' => get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1。这是分页失效的第一大原因。- 首页分页 404:静态首页(Reading Settings 里设了 Front page)要用
get_query_var( 'page' )而不是'paged',两者变量不同。 - 案例:某小说站改造后用
query_posts()重查了一遍再输出 WP-PageNavi,结果无论点第几页都显示同样内容。原因:query_posts()会覆盖主查询,而wp_pagenavi()仍读的是被污染后的$wp_query。改用pre_get_posts钩子改写主查询才彻底解决——这个错踩了整整两周。 - 分页链接带了
?s=后 cyrillic 乱码:用rawurlencode()处理搜索词,并在paginate_links里不要再esc_html一次链接。 - 插件 CSS 未加载:某些主题住了
wp_head(),或者在functions.php里 deregister 了wp-pagenavi的 style 句柄(句柄名就叫wp-pagenavi)。回滚 deregister 或者自己写 CSS。 paged在 pretty permalink 下取不到:确保get_pagenum_link()能正常生成/page/2/形式;如果站点用了%post_id%.html这种后缀式固定链接,/page/2/会与被 RewriteRule 冲突,需要调整 rewrite 优先级。- 把
/page/2/设成了 canonical 到首页:这是自杀式操作,会让第 2 页之后全部从索引消失。Yoast/Rank Math 默认不会这么做,但自己写<link rel=canonical>时极容易写错。
延伸阅读:WordPress 分页与收录策略、WordPress 主题模板结构
总结
三条判断标准:第一,能不能看到真实 href——javascript: 或 AJAX 分页对收录毫无帮助;第二,paged / page 变量是否取对——自定义 WP_Query 场景必查;第三,是否 canonical 自指向——分页页的正确 canonical 是它自己。至于插件还是代码:WP-PageNavi 胜在零代码接入、后台直观,适合新手站;paginate_links() 胜在零依赖、便于自定义结构和 add_query_arg 扩展,适合已有开发能力的站点。两者没有性能差异,因为瓶颈从来不在分页渲染上。
相关处理:WordPress 缓存与动态内容冲突排查
