本文约 5,661 字,预计阅读 15 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:文档站的用户是带着问题来的
文档站和博客的访问行为完全不同。博客读者是”逛”,文档读者是”找”——他遇到一个具体报错,希望在三秒内定位到那一段说明。这意味着文档站的三个技术指标和博客不一样:层级必须反映心智模型、搜索必须能命中代码块里的内容、同一份文档的不同版本必须能并存。如果这三点没做好,用户会直接去搜搜索引擎,然后落在你的竞争对手站点上。
原理分析:文档的模型是”树 + 标签 + 版本”
文档天然是树形结构:产品 → 模块 → 具体页面。WordPress 的 hierarchical CPT 能直接表达这棵树,配合 wp_list_pages() 或 get_pages() 就能渲染侧边栏目录,不需要额外的导航插件。
但树形结构有两个天然缺陷:同一篇文档可能属于多个模块(交叉引用),以及搜索时树形结构帮不上忙。前者用分类法补(一个分类法做”产品线”,一个做”标签”),后者需要单独处理搜索。
版本管理是文档站的独有需求。WordPress 原生的”修订版本”(revision)存的是同一篇文档的历次修改,天然适合做”版本快照”,但默认不对外展示。做法是:用分类法 doc_version 区分主版本(如 v2.x / v1.x),同一篇文档按版本存为不同文章,互相用 _version_group meta 关联;修订版本则用于追溯单次修改。
| 需求 | 实现方式 | 说明 |
|---|---|---|
| 目录树 | hierarchical CPT | post_parent + menu_order |
| 交叉分类 | 两个分类法 | 产品线 + 标签 |
| 站内搜索 | pre_get_posts + 权重 |
标题权重高于正文 |
| 版本切换 | 分类法 + 关联 meta | 前端提供版本下拉 |
保姆级实操
步骤 1:注册层级文档类型。 关键参数:hierarchical => true、publicly_queryable、show_in_rest(便于做前端搜索)。
add_action( 'init', 'mimangfei_register_doc_type' );
function mimangfei_register_doc_type() {
register_post_type( 'doc', array(
'labels' => array( 'name' => '文档', 'singular_name' => '文档' ),
'public' => true,
'hierarchical' => true,
'supports' => array( 'title', 'editor', 'page-attributes', 'revisions', 'thumbnail' ),
'rewrite' => array( 'slug' => 'docs', 'hierarchical' => true ),
'show_in_rest' => true,
'has_archive' => true,
) );
register_taxonomy( 'doc_version', array( 'doc' ), array(
'labels' => array( 'name' => '文档版本', 'singular_name' => '文档版本' ),
'public' => true,
'hierarchical' => true,
'show_in_rest' => true,
'rewrite' => array( 'slug' => 'docs/version' ),
) );
}
步骤 2:让搜索限定在文档内。 文档站最好给一个”文档内搜索”,避免结果混杂博客文章。用一个 post_type 参数即可,配合 s 参数走原生搜索。
add_filter( 'pre_get_posts', 'mimangfei_doc_search' );
function mimangfei_doc_search( $query ) {
if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
return;
}
if ( isset( $_GET['scope'] ) && 'docs' === $_GET['scope'] ) {
$query->set( 'post_type', 'doc' );
$query->set( 'posts_per_page', 20 );
}
}
步骤 3:提升标题命中权重。 WordPress 默认搜索只按相关性排序,标题命中和正文命中权重接近。用 posts_search 过滤器改写 SQL,把 post_title 的命中前置。
add_filter( 'posts_search', 'mimangfei_search_weight', 10, 2 );
function mimangfei_search_weight( $search, $query ) {
global $wpdb;
if ( ! $query->is_search() || empty( $query->get( 's' ) ) ) {
return $search;
}
$term = $wpdb->esc_like( $query->get( 's' ) );
$extra = $wpdb->prepare(
" OR ({$wpdb->posts}.post_title LIKE %s)",
'%' . $term . '%'
);
return $search . $extra;
}
步骤 4:渲染侧边栏目录。 用 wp_list_pages() 传入 post_type 参数,它支持层级 CPT,会自动按 menu_order 排序并输出嵌套 <ul>。
function mimangfei_doc_sidebar( $doc_id ) {
$ancestors = get_post_ancestors( $doc_id );
$root = $ancestors ? end( $ancestors ) : $doc_id;
return wp_list_pages( array(
'post_type' => 'doc',
'child_of' => $root,
'title_li' => '',
'echo' => false,
'sort_column' => 'menu_order',
'link_before' => '<span>',
'link_after' => '</span>',
) );
}
步骤 5:做版本切换。 同一篇文档的不同版本用 _version_group 关联,切换时找到同组的其他版本并重定向。
function mimangfei_switch_version( $doc_id, $target_version ) {
$group = get_post_meta( $doc_id, '_version_group', true );
if ( ! $group ) {
return new WP_Error( 'no_group', '该文档没有版本组' );
}
$found = get_posts( array(
'post_type' => 'doc',
'post_status' => 'publish',
'posts_per_page' => 1,
'meta_query' => array(
array( 'key' => '_version_group', 'value' => $group ),
),
'tax_query' => array(
array(
'taxonomy' => 'doc_version',
'field' => 'slug',
'terms' => sanitize_title( $target_version ),
),
),
) );
if ( empty( $found ) ) {
return new WP_Error( 'not_found', '该版本暂无对应文档' );
}
return get_permalink( $found[0]->ID );
}
步骤 6:用修订版本做变更追溯。 文档底部展示”最近更新时间”与”本次更新了什么”,数据来自修订版本。
function mimangfei_doc_revisions( $doc_id, $limit = 5 ) {
$revisions = wp_get_post_revisions( $doc_id, array(
'posts_per_page' => $limit,
'orderby' => 'date',
'order' => 'DESC',
) );
$out = array();
foreach ( $revisions as $rev ) {
$out[] = array(
'time' => get_the_modified_date( 'Y-m-d H:i', $rev->ID ),
'author' => get_the_author_meta( 'display_name', $rev->post_author ),
);
}
return $out;
}
步骤 7:控制修订版本数量。 文档频繁更新会产生大量修订,在 wp-config.php 里限制最多保留 10 条。
define( 'WP_POST_REVISIONS', 10 );
步骤 8:用现成插件兜底。 BetterDocs(WPDeveloper)与 weDocs(weDevs)都是官方目录里的成熟文档插件,自带即时搜索、目录与反馈按钮,自己写之前先评估是否能满足。
常见踩坑
- 层级 URL 没开
hierarchicalrewrite。 子文档 URL 不带父路径,导致/docs/a/b/无法访问。 - 搜索结果混着博客文章。 用户搜报错信息却返回三篇营销文章,体验极差。
- 侧边栏目录全量渲染。 文档上千篇时,
wp_list_pages不限制child_of会生成巨大 DOM,应只渲染当前分支。 - 案例: 某 SaaS 文档站把版本信息写在文章标题里(”xxx v1″”xxx v2″),两年后积累 400 多篇同名文档,编辑自己都分不清哪个是最新版,搜索引擎也把两个版本都收录了。改成
doc_version分类法 +_version_group关联后,只保留最新版可被索引,旧版本加noindex。 - 修订版本无限增长。 数据库膨胀到几 GB,后台列表变慢。
- 代码块内容搜不到。 默认搜索只查
post_content,被代码块包裹的报错信息其实在 HTML 里,需要额外处理。 - 文档 CPT 未加入站点地图。 用 Yoast 或 Rank Math 时要确认
doc类型在站点地图中是开启状态。
总结
文档站的三个硬指标:目录只渲染当前分支、搜索能限定范围且标题优先、旧版本明确加 noindex。层级用原生 hierarchical CPT,交叉分类用分类法,版本用分类法 + 关联 meta,修订版本限制在 10 条以内。判断标准:让一个不熟悉站点的人找一段报错说明,超过 30 秒就是结构有问题。资讯类站点的信息流组织方式见杂志资讯站首页设计;专题聚合的做法见专题聚合页实现。
