搜索全站
主题 13 分钟阅读

WordPress 上一篇下一篇优化:同分类导航与样式调整代码

上一篇下一篇跳到不相关文章,读者立刻流失。本文讲清 previous_post_link 的五个参数、同分类导航实现、缩略图导航的代码写法,以及导航区域补充分推荐内容与结构化处理。

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

引言:点「下一篇」,跳到了完全不相干的栏目

文章底部那组「上一篇 / 下一篇」链接,本意是留住读者继续浏览。但如果你的站点既写 WordPress 教程又写主机评测,读者看完一篇教程点「下一篇」,很可能跳到一篇主机广告文——内容断层,跳出率飙升。默认行为是按发布时间排序取相邻文章,完全不考虑分类、标签或相关性。

另一个问题是样式:默认只有两个纯文字链接,窄屏下挤成一团,看不出是导航还是正文。很多人干脆装插件了事,但插件往往带来额外的 CSS 与查询。实际上 WordPress 自带的 previous_post_link() / next_post_link() 已经支持同分类过滤,剩下的只是补样式和补内容。本文给出完整做法,并说明什么时候该用「相关文章」替代上下篇导航。

原理分析:相邻文章的查询逻辑与参数

previous_post_link() 和 next_post_link() 底层都调用 get_adjacent_post(),它会构造一条 SQL:按 post_date(默认)或 ID 比较,取 post_type 为当前类型、post_status 为 publish、且排除了指定分类的一篇文章。参数签名如下:

参数 含义 常用值
$format 输出模板,%link 是链接占位符 '%link'
$link 链接文字模板,%title 是标题占位 '%title'
$in_same_term 是否限定同一分类法下的同 term true
$excluded_terms 排除的 term ID '' 或数组
$taxonomy $in_same_term 为 true 时按哪个分类法 'category'

关键点在第五个参数:默认分类法是 category,如果你的文章主要靠标签或自定义分类法(比如「教程系列」)组织,必须显式传入对应分类法名称,否则 in_same_term 会按默认的 category 过滤,结果可能是空的。

性能上,get_adjacent_post() 每次调用都会产生一条带 WHERE 与 ORDER BY 的查询,加上排除逻辑可能用到 LEFT JOIN。一页两次调用影响不大,但如果在循环里反复调用就会拖慢页面。有对象缓存时这些查询会被缓存,问题不明显。

还要注意「边界情况」:第一篇文章没有上一篇,最后一篇文章没有下一篇。默认会输出空字符串,导致布局缺一块。要么用 CSS 让另一侧占满,要么在两端补上推荐内容。

保姆级实操

  1. 确认组织维度:先想清楚读者最可能想看的是「同分类下一篇」还是「同标签下一篇」,据此决定 $taxonomy。
  2. 改成同分类导航:在 single.php 里把默认调用换成带 true 参数的版本。
  3. 补样式:做成左右两块的卡片式布局,加标题前缀与箭头图标。
  4. 处理边界:无上一篇/下一篇时输出推荐内容或占位。
  5. 加缩略图:用 get_the_post_thumbnail() 让导航更有点击欲。
  6. 清缓存后验证:在分类边界文章(每个分类的第一篇和最后一篇)上测试。
<?php
// single.php 里的上下篇导航,限定同一分类
$prev = get_previous_post( true, '', 'category' );
$next = get_next_post( true, '', 'category' );
?>

<nav class="post-nav" aria-label="文章导航">
    <?php if ( $prev ) : ?>
        <a class="post-nav__item post-nav__item--prev" href="<?php echo esc_url( get_permalink( $prev ) ); ?>">
            <span class="post-nav__label">上一篇</span>
            <span class="post-nav__title"><?php echo esc_html( get_the_title( $prev ) ); ?></span>
        </a>
    <?php endif; ?>

    <?php if ( $next ) : ?>
        <a class="post-nav__item post-nav__item--next" href="<?php echo esc_url( get_permalink( $next ) ); ?>">
            <span class="post-nav__label">下一篇</span>
            <span class="post-nav__title"><?php echo esc_html( get_the_title( $next ) ); ?></span>
        </a>
    <?php endif; ?>
</nav>
<?php
// 想保留默认函数、只改输出格式时,用过滤器统一加工
add_filter( 'previous_post_link', 'mimangfei_wrap_adjacent_link', 10, 5 );
add_filter( 'next_post_link', 'mimangfei_wrap_adjacent_link', 10, 5 );
function mimangfei_wrap_adjacent_link( $output, $format, $link, $post, $adjacent ) {
    if ( ! $post ) {
        return '';
    }
    $thumb = get_the_post_thumbnail( $post, 'thumbnail', array( 'class' => 'post-nav__thumb' ) );
    $title = esc_html( get_the_title( $post ) );
    $href  = esc_url( get_permalink( $post ) );

    return sprintf(
        '<a class="post-nav__item post-nav__item--%1$s" href="%2$s">%3$s<span class="post-nav__title">%4$s</span></a>',
        esc_attr( $adjacent ),
        $href,
        $thumb,
        $title
    );
}
.post-nav {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
    margin: 2rem 0;
}

@media ( min-width: 600px ) {
    .post-nav { grid-template-columns: repeat( 2, minmax( 0, 1fr ) ); }
    .post-nav__item--next { text-align: right; }
}

.post-nav__item {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 14px 16px;
    border: 1px solid var( --border, #d0d7de );
    border-radius: 8px;
    text-decoration: none;
    color: inherit;
    min-height: 72px;
}

.post-nav__label {
    font-size: 0.8rem;
    color: var( --muted, #656d76 );
}

.post-nav__title {
    font-weight: 600;
    line-height: 1.4;
    /* 标题过长时截断,避免卡片高度失控 */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

常见踩坑

  1. in_same_term 传 true 但没传 $taxonomy:默认按 category 过滤,如果你的文章没分类(只有标签),导航会直接消失。显式传分类法名。
  2. 文章属于多个分类:get_adjacent_post() 的逻辑是「属于其中任一分类」,不是「属于全部分类」,结果仍可能跨栏目。分类体系要设计成互斥的单分类结构。
  3. 案例:某站把站点从「按时间组织」改成「按系列组织」,上线后发现系列的第一篇文章完全没有「上一篇」。原因是他们用了 previous_post_link( '%link', '%title', true ),同分类过滤后第一篇确实没有上一篇,页面只剩右侧一块,布局塌陷。最后改成:无上一篇时输出「返回系列目录」的链接,无下一篇时输出该系列的最新一篇,既填满布局又提升连续性。教训:边界情况必须在设计阶段就考虑。
  4. 标题过长撑破卡片:长标题让两侧卡片高度差很大。用 -webkit-line-clamp 限制两行。
  5. 密码保护文章的标题泄漏:get_the_title() 对加密文章会加「受保护:」前缀,但链接点进去要密码。可用 post_password_required() 判断后跳过或标注。
  6. 缓存导致导航不变:全页缓存会缓存上下篇 HTML,发新文章后旧页面的「下一篇」还指向老文章。要么清缓存,要么把这块做成 AJAX 加载。
  7. 与 SEO 插件重复输出:部分主题/插件会在内容下方再插一组导航,出现两组重复链接。检查 the_content 过滤器里是否有人挂载了导航。
  8. 性能上无节制扩展:为了「推荐 6 篇相关文章」在导航区又跑了 3 条 WP_Query。相关文章请用一次查询 + post__not_in,别循环调用相邻文章函数。

总结

上下篇导航的判断标准是:读者点完之后,是否还在同一个内容脉络里。如果答案是「经常跳到不相关栏目」,就该改成同分类/同标签导航,或直接换成相关文章推荐。行动清单:确定组织维度 → 用 get_previous_post( true, '', '分类法名' ) 限定范围 → 做成卡片式布局并限制标题行数 → 处理首末篇的边界情况 → 加密文章与草稿要排除 → 清缓存后在分类边界文章上验证。导航做好后,配合 主题面包屑与结构化数据配合 把层级关系一并表达清楚,读者和搜索引擎都能更好地理解站点结构。

U
UFO_PF作者

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

留下你的想法

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