本文约 4,142 字,预计阅读 11 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:作品堆在首页,改一次要动几十张图
设计师、摄影师、独立开发者搭作品集站,最常见的做法是用一个多用途主题自带的 Portfolio 模块,把所有作品平铺在首页。短期看没问题,一年后就崩了:作品数量涨到五六十个,首页加载几十张大图;想给某个作品加个”客户评价”字段,发现主题根本不支持;换个主题,所有作品内容全部丢失,因为数据存在主题的私有字段里。
根本问题在于作品被当成了”页面内容”,而不是”结构化数据”。正确的做法是把作品定义成一种独立的文章类型(CPT),每个作品是一条数据,字段固定,展示层随便换。这样首页只是作品的一个”视图”,可以按类型筛选、按时间排序、按置顶权重排,换主题也不影响数据。
原理分析:CPT、分类法与展示层的关系
| 层 | 负责什么 | 用什么实现 | 换主题时是否受影响 |
|---|---|---|---|
| 数据层 | 作品标题、描述、封面、客户、年份 | 自定义文章类型 + 自定义字段 | 不受影响 |
| 分类层 | 作品类型、行业、标签 | 自定义分类法 taxonomy | 不受影响 |
| 展示层 | 归档页、详情页的 HTML | 主题模板文件 | 受影响 |
| 交互层 | 筛选、灯箱、联系表单 | JS + 表单插件 | 部分受影响 |
关键结论:CPT 和字段必须自己注册、存在数据库里,不依赖任何主题。主题只负责 archive-portfolio.php 和 single-portfolio.php 两个模板文件。用 CPT UI 插件注册也可以,它把配置存在 option 里,换主题同样不丢。
保姆级实操
步骤 1:注册作品文章类型与分类法
写在子主题 functions.php 或一个小插件里。注意 has_archive 必须为 true,否则没有归档页;rewrite 的 slug 决定 URL:
add_action( 'init', function () {
register_post_type( 'portfolio', array(
'labels' => array(
'name' => '作品',
'singular_name' => '作品',
'add_new_item' => '添加作品',
),
'public' => true,
'has_archive' => true,
'rewrite' => array( 'slug' => 'work' ),
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
'menu_icon' => 'dashicons-portfolio',
'show_in_rest' => true,
) );
register_taxonomy( 'work_type', 'portfolio', array(
'labels' => array( 'name' => '作品类型' ),
'public' => true,
'hierarchical' => true,
'rewrite' => array( 'slug' => 'work-type' ),
'show_in_rest' => true,
) );
} );
注册后去”设置 → 固定链接”点一次保存刷新规则,否则归档页会 404。
步骤 2:用 ACF 定义作品字段
装 Advanced Custom Fields,为 portfolio 建一个字段组:客户名称(text)、项目年份(number)、项目角色(text)、交付成果(textarea)、作品图集(gallery)、项目链接(url)。字段组的位置规则设为”文章类型 = 作品”。ACF 的值存在 postmeta 表里,属于数据层,换主题不丢。
步骤 3:做归档页模板
复制主题的 archive.php 为 archive-portfolio.php,把主循环替换成受控的 WP_Query,统一缩略图尺寸避免布局跳动:
$args = array(
'post_type' => 'portfolio',
'posts_per_page' => 12,
'paged' => get_query_var( 'paged' ) ?: 1,
);
if ( is_tax( 'work_type' ) ) {
$args['tax_query'] = array( array(
'taxonomy' => 'work_type',
'field' => 'slug',
'terms' => get_query_var( 'term' ),
) );
}
$q = new WP_Query( $args );
while ( $q->have_posts() ) {
$q->the_post();
echo '<a class="work-card" href="' . get_permalink() . '">';
echo get_the_post_thumbnail( get_the_ID(), 'medium_large', array( 'loading' => 'lazy' ) );
echo '<h3>' . get_the_title() . '</h3>';
echo '<span>' . get_field( 'client_name' ) . ' · ' . get_field( 'project_year' ) . '</span>';
echo '</a>';
}
wp_reset_postdata();
步骤 4:详情页按”结论前置”写
作品详情页不要一上来放大图。建议顺序:一句话项目背景 → 最终成果图(1 张)→ 我的角色 → 过程与难点 → 图集 → 客户评价 → 下一个作品。招聘方或客户平均只花 40 秒看一个作品,结论必须前置。
步骤 5:归档页加类型筛选
用 get_terms() 输出分类链接即可,不需要 JS 框架:
$terms = get_terms( array( 'taxonomy' => 'work_type', 'hide_empty' => true ) );
foreach ( $terms as $t ) {
printf(
'<a class="filter-item" href="%s">%s (%d)</a>',
esc_url( get_term_link( $t ) ),
esc_html( $t->name ),
(int) $t->count
);
}
步骤 6:转化入口放在每个作品底部
不要只放一个总联系页。每个作品详情页底部放一句”有类似项目?聊聊你的需求”+ 一个简短表单。Contact Form 7 或 Fluent Forms 都行,字段控制在 3 个以内。
常见踩坑
- 在
init里反复调用flush_rewrite_rules():这会在每次请求重写数据库,拖慢全站。只在插件启用时调用一次,或手动去后台保存固定链接。 - CPT slug 与页面 slug 冲突:如果已有别名为
work的页面,归档页会被页面抢走,URL 出现work-2。 has_archive设为false却去访问归档 URL:必然 404,且容易被误判成”CPT 没注册成功”。- 图集直接用 gallery 原图:ACF gallery 返回的是附件 ID,必须用
wp_get_attachment_image_src()指定尺寸,直接塞原图会让详情页几 MB。 - 筛选用了 JS 全量加载:作品超过 50 个还一次性渲染全部,移动端会卡。分页或分类链接更稳。
- 案例:某摄影师的换主题事故:作品全部写在主题的 Portfolio 模块里,换主题后 68 个作品只剩标题,图片和描述全丢,因为数据存在主题自有的序列化字段中。重建时改用 CPT UI + ACF,数据落在
posts/postmeta表,此后换过两次主题零损失。 - 详情页没设
rel="next"式的作品导航:访客看完一个作品没有下一个入口,跳出率极高,底部必须加”上一个 / 下一个作品”。
总结
判断作品集站是否搭对了,就问自己三个问题:换主题会不会丢数据?作品能不能按类型筛?每个作品页有没有独立的转化入口?三个都答”是”才算合格。行动清单:先用 register_post_type() 建 portfolio 类型并配 work_type 分类法,再用 ACF 定义字段,然后手写一个受控 WP_Query 的归档模板,最后给详情页补”结论前置”结构和底部表单。如果你想更进一步做成在线名片式的单页,可参考 个人简历站与在线名片,产品化路线则可看 独立开发者产品官网。
