Typecho首页摘要怎么截?4种方法实战对比
这个博客自己就是 Typecho 搭的。首页和分类页要不要输出全文,是 Typecho 站长第一周就会遇到、但很多人三年后都没真的搞清楚的细节问题。默认主题 default、Mirages、Handsome 行为不一致——有的全文输出,有的只输出 more 之前的部分。换主题后首页要么超长拖慢加载,要么摘要被无脑截断把关键信息切没了。
这一篇我把 Typecho 1.2.x / 1.3.0 下首页、分类页、标签页、搜索页文章列表的所有可用截取方法、它们的差异、各自对 SEO 与 LCP 指标的影响、以及实际生产环境踩过的坑全部讲清楚。所有代码我都在 Typecho 1.3.0 + PHP 7.4.33 + Nginx 环境跑过。
先弄清楚 Typecho 的两个核心方法 content 与 excerpt
Typecho 在文章列表页输出正文的核心方法只有两个:$this->content() 与 $this->excerpt()。两者的差别新手 90% 没搞清楚:
- content('阅读剩余部分...'):默认输出全文。只有当文章里写了 more 标签时,才会在该处截断,参数字符串作为"阅读全文"链接的文字。
- excerpt(N, '...'):强制截前 N 个字符,无视有没有 more 标签。N 默认 100,第二个参数是省略号。
关键差异:content 会保留 HTML 标签和图片,excerpt 会把 HTML 全部剥掉变成纯文本。这意味着:
- 想让首页保留排版(图片、加粗、列表):用 content + more 标签。
- 想让首页是纯文本摘要、统一长度:用 excerpt。
这俩方法的源码都在 Typecho 核心 var/Widget/Abstract/Contents.php 里。我自己读过几次源码后发现一个细节:excerpt 内部其实是先调 strip_tags 再 mb_substr,所以你传给它的 N 是"剥完 HTML 后的字符数",不是原始 markdown 字符数。这点很多人写自定义函数时算错。
方案 A:把 content 直接替换成 excerpt
打开当前主题目录的 index.php(一般在 /usr/themes/<主题>/index.php),找到这一行:
<?php $this->content('阅读剩余部分...'); ?>替换成:
<?php $this->excerpt(); ?>这是默认 100 字纯文本摘要。觉得太短就加参数:
<?php $this->excerpt(200, '...'); ?>对中文来说 Typecho 的 excerpt 按 mb_substr 的字符口径算,一个中文字符等于 1,长度直观。改完别忘了 archive.php、category.php、tag.php、search.php 也都要改,否则只改了首页,其它归档页还是全文。我一般用 grep 全局搜 this->content 一次性替换。
这个方案的优缺点很明显:5 分钟搞定,所有列表页字数一致,看起来整齐。但是图片和排版全部丢失,技术博客(贴代码、贴截图)尤其难看,因为 strip_tags 会把代码块的语法高亮 HTML 全部剥光,剩下一堆无格式纯文本。所以这个方案适合纯文字博客或随笔站。
方案 B:用 more 标签精确控制截断点
想保留首页排版又自己决定每篇文章在哪里截断,正确做法是保留 content 不动,写文章时手动插 more 标签。比如:
这是文章引言,吸引读者点开。
第二段继续讲背景,给一些数据。
<!--more-->
这部分只在文章详情页出现,首页看不到。
首页只显示 more 之前的两段,并自动加一个"阅读剩余部分..."的链接。这种方法的好处:
- 排版完全由你控制,图片、列表、代码块、表格都能保留
- 每篇文章可以有不同的摘要长度——长文章截多点,短文章不截
- 对 SEO 友好:首页输出的是真实的文章开头而不是机器截断的半句话
- 提升点击率——可以把"钩子"放在 more 之前,把答案放在 more 之后,制造完读冲动
Typecho 官方文档(docs.typecho.org)默认推荐这种写法。坑是 markdown 文章中的 more 标签必须独占一行,前后都要加空行,否则部分解析器(特别是 1.2.0 之前的版本)不识别。
方案 C:自定义截取函数突破内置限制
内置 excerpt 有两个让人不爽的地方:第一是会剥掉所有 HTML 包括你想保留的图片和加粗,第二是不智能——刚好截到一个英文单词中间也照样切。你想"保留图片但限制段数"或"按句号截断",就得在 functions.php 里自己写。我用了三年的版本:
<?php
function theme_smart_excerpt($content, $maxLen = 200, $suffix = '...') {
// 保留 img、br、strong、em、a,去掉其它标签
$allowed = '<img><br><strong><em><a>';
$text = strip_tags($content, $allowed);
// 多余空白合并
$text = preg_replace('/\s+/', ' ', $text);
// 按字符截断
if (mb_strlen($text, 'UTF-8') > $maxLen) {
$text = mb_substr($text, 0, $maxLen, 'UTF-8') . $suffix;
}
return $text;
}
在 index.php 里这样调用:
<?php echo theme_smart_excerpt($this->content, 200); ?>注意是 $this->content(属性,原始内容),不是 $this->content()(方法,会输出已渲染 HTML)。这一字之差是 Typecho 模板里最常踩的坑之一——少了括号是属性,多了括号是方法。属性返回原始字段值(含 markdown 或 HTML 源码),方法返回经 plugins 钩子和 Markdown 解析后的结果。我自己第一次写主题就踩过这个坑,调了一晚上才发现。
更进一步:按句号或段落截
纯按字符数截会出现"截到半句话"的问题。改良版按句号或段落兜底:
<?php
function theme_sentence_excerpt($content, $maxLen = 200) {
$text = strip_tags($content);
$text = preg_replace('/\s+/', ' ', $text);
if (mb_strlen($text, 'UTF-8') <= $maxLen) return $text;
// 先按字符截
$cut = mb_substr($text, 0, $maxLen, 'UTF-8');
// 找最近的中文标点收尾
$endings = ['。', '!', '?', ';', '.', '!', '?', ';'];
$maxPos = 0;
foreach ($endings as $e) {
$pos = mb_strrpos($cut, $e, 0, 'UTF-8');
if ($pos !== false && $pos > $maxPos) $maxPos = $pos;
}
if ($maxPos > $maxLen * 0.5) {
return mb_substr($cut, 0, $maxPos + 1, 'UTF-8');
}
return $cut . '...';
}
这个版本的逻辑是:先按字符截到 maxLen,然后在截出来的部分里找最近的中文/英文句末标点,如果标点位置超过 maxLen 的一半(避免截太短),就在标点处收尾,否则就用省略号兜底。线上跑了 18 个月没出过半句话的情况。
方案 D:不同页面用不同截取策略
实际项目里我经常希望首页用 200 字摘要、分类页用 150 字、标签页只显示标题不显示摘要、搜索页用关键词高亮的特殊截取。Typecho 的 archive.php 默认会处理所有归档页面,可以这样写分支:
<?php if ($this->is('index')): ?>
<?php $this->excerpt(200, '...'); ?>
<?php elseif ($this->is('category')): ?>
<?php $this->excerpt(150, '...'); ?>
<?php elseif ($this->is('tag')): ?>
<!-- 标签页不输出摘要 -->
<?php elseif ($this->is('search')): ?>
<?php echo theme_search_highlight_excerpt($this->content, $this->keywords, 180); ?>
<?php else: ?>
<?php $this->excerpt(120, '...'); ?>
<?php endif; ?>
$this->is() 支持 index、category、tag、search、archive、single、page、404 等参数,是 Typecho 模板的核心 API。这个分支判断在 var/Widget/Archive.php 里实现,我读过源码确认这是 Typecho 1.x 的稳定 API,不会随版本变。
SEO 视角:摘要长度怎么选才合理
这一节是这篇笔记跟其他"Typecho 摘要教程"最大的差异——摘要不是越长越好也不是越短越好。运营 Typecho 站点 5 年下来我得到几个经验值:
- 首页摘要 150 到 250 字符:让读者判断要不要点击足够,又不会把首页变成"多篇文章拼起来的长页"导致主题分散。我自己测过:从 100 字提到 200 字,首页跳出率降 8%;从 200 字提到 350 字,跳出率反而升 4%。
- 分类页摘要 100 到 180 字符:分类页本身已经有分类导航语义,每条文章是"索引"而不是"内容"。
- 标签页可以更短甚至不显示:标签页主要是聚合作用,输出过多正文反而稀释主题相关性,还可能被搜索引擎判定为低质量聚合页。
- 避免在列表页输出全文:搜索引擎会判定首页/分类页与详情页内容高度重复,详情页排名会被首页"抢走"——这就是经典的 site-internal cannibalization。我自己有篇文章 2024 年 6 月把首页改全文输出,2 周后那篇文章详情页 GSC 展示量降 35%,改回 200 字摘要后 3 周恢复。
如果已经用 content + more 但有的文章忘记加 more 标签,可以在 index.php 里加兜底:
<?php
$content = $this->content;
if (strpos($content, '<!--more-->') === false) {
// 没 more 标签,强制截 200 字
echo theme_smart_excerpt($content, 200);
} else {
$this->content('阅读剩余部分...');
}
?>
性能视角:首屏字节数与 LCP 的关系
很多人不知道首页摘要长度跟 Core Web Vitals 直接相关。我把自己博客在 Chrome DevTools 跑过三次实验:
- 全文输出:首页 HTML 主文档 312KB(gzipped 78KB),LCP 2.4 秒,FCP 1.6 秒
- 200 字 excerpt:首页 HTML 主文档 84KB(gzipped 22KB),LCP 1.3 秒,FCP 0.9 秒
- 200 字 excerpt + 懒加载图片:LCP 0.8 秒,FCP 0.6 秒
三组都在同一台 4M 上行带宽 VPS 上、北京移动 4G 测试。从全文到 200 字摘要 LCP 改善 1.6 秒,这是 Google PageSpeed Insights 里非常显眼的提升,直接把性能等级从橙色推到绿色。对国内 Typecho 站点来说,首页改 excerpt 可能是单次 ROI 最高的性能优化动作。
修改后必须验证的 5 件事
首页源代码长度
浏览器右键查看源代码,搜首页 HTML 体积。改之前可能 200KB,改成摘要后应该降到 50 到 80KB。首屏字节数直接影响 LCP(Largest Contentful Paint)。
分页链接是否正常
首页第二页、第三页(一般是 /page/2/、/page/3/)打开看摘要逻辑是否一致。Typecho 的 pageNav 在某些主题里只在首页第一页出现,分页后摘要可能跳回原始 content() 调用。
RSS 输出
Typecho 的 RSS(/feed/)通常输出全文或前 N 条全文,跟首页摘要无关。但很多 RSS 订阅工具(Feedly、Inoreader)会显示 excerpt,建议同时检查 archive.php 的 RSS 分支。我自己的做法是 RSS 永远输出全文(用户在 RSS 阅读器里读体验最好),网页端首页用 200 字摘要。
图片懒加载是否被 strip_tags 破坏
如果你用了 lazyload 插件给 img 标签加 data-src,excerpt 内部 strip_tags 会把整个 img 剥掉,导致首页图片不显示。如果想保留图片,必须用方案 C 的自定义函数白名单 img。
AMP 页面行为
装了 AMP 插件的话,AMP 页面有独立模板,跟主模板的摘要逻辑无关。AMP 主题里的 amp.php / amp/index.php 也要单独改一遍。
边界情况与高级技巧
处理含代码块的文章摘要
技术博客最容易踩的坑:文章开头几行是代码块,excerpt 一剥变成挤在一起的代码字符串,可读性极差。解决方案:写自定义函数时跳过代码块:
<?php
function theme_skip_code_excerpt($content, $maxLen = 200) {
// 先去掉代码块(含 markdown 的 ``` 与 HTML 的 pre/code)
$text = preg_replace('/```[\s\S]*?```/', '', $content);
$text = preg_replace('/<pre[\s\S]*?<\/pre>/', '', $text);
$text = preg_replace('/<code[\s\S]*?<\/code>/', '', $text);
// 再剥剩余 HTML
$text = strip_tags($text);
$text = preg_replace('/\s+/', ' ', trim($text));
return mb_strlen($text, 'UTF-8') > $maxLen
? mb_substr($text, 0, $maxLen, 'UTF-8') . '...'
: $text;
}
这个版本会把代码块完整剔除再算字数,首页摘要永远是有意义的散文段落。
结合 Open Graph 与 Twitter Card
很多 Typecho 主题的 header.php 里 og:description 用的是 $this->description(用户在文章设置里手填)或者 $this->excerpt(160)。建议统一用同一个自定义函数,这样列表页摘要、社交分享卡片、搜索结果摘要保持一致:
<meta property="og:description" content="<?php echo htmlspecialchars(theme_smart_excerpt($this->content, 160)); ?>">
<meta name="twitter:description" content="<?php echo htmlspecialchars(theme_smart_excerpt($this->content, 160)); ?>">
注意 og:description 推荐 150 到 160 字符(Facebook 和 LinkedIn 截断阈值),twitter:description 推荐 200 字符以内。
缓存 excerpt 结果减少计算开销
如果你的首页流量很大、自定义函数里有正则替换,每次首页请求都会重新计算 excerpt,CPU 浪费明显。可以用 Typecho 内置的 cache 或简单的文件缓存:
<?php
function theme_cached_excerpt($cid, $content, $maxLen = 200) {
$cacheFile = __DIR__ . '/cache/excerpt_' . $cid . '_' . md5($content) . '.txt';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < 86400) {
return file_get_contents($cacheFile);
}
$excerpt = theme_smart_excerpt($content, $maxLen);
@file_put_contents($cacheFile, $excerpt);
return $excerpt;
}
用 cid+content 哈希做 key,文章修改时哈希变了缓存自动失效。这个改造在我自己博客上把首页生成时间从 180ms 降到 38ms。
第三方主题的注意事项
Handsome、Joe、Material、Mirages 这些第三方主题大都做了完善的摘要逻辑,主题设置里就有"首页摘要字数"选项,不要直接改主题代码——更新主题后改的代码会被覆盖。正确做法:
- 先在主题设置里找有没有相关选项(一般在外观-编辑-主题设置)
- 有的话直接调参数,没必要碰代码
- 如果想加自定义函数,写在 site/usr/themes/<主题>/functions.php 里——这个文件主题更新通常不会覆盖(确认一下),更稳妥的做法是建一个 child theme
- 用 hook 而不是直接改模板:Typecho 的 Plugin 系统支持 contentEx 钩子可以全局拦截 content 输出
具体的 hook 写法:
<?php
// 在某个插件或主题 functions.php
Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = function ($plain, $widget, $original) {
return theme_smart_excerpt($widget->content, 200);
};
这种写法一次注册全站生效,所有调用 excerpt 的地方都会走自定义逻辑,比改模板优雅得多。
从 WordPress 迁移过来的差异点
很多人是从 WordPress 转 Typecho,摘要写法的差异要单独提:
- WordPress 是
the_excerpt(),Typecho 是$this->excerpt() - WordPress 默认按词数截(55 个英文单词),Typecho 按字符数截(默认 100)。中文站 Typecho 更直观
- WordPress 的 the_content('Read more') 不像 Typecho 那么干净——WP 会自动加 wpautop 段落处理,Typecho 不会
- WordPress 的 excerpt 钩子叫
excerpt_length、excerpt_more,Typecho 的钩子叫excerptEx - WordPress 文章列表里手动写
<!--more-->用法跟 Typecho 一致,迁移过来不用改
迁移工具(比如 Typecho 自带的 WP 导入插件)通常会保留原文里的 more 标签,但部分会丢失,迁完站后建议跑 grep 全站检查一遍。
总结:三档方案选哪一个
按你的精力和需求选:
- 省力档:
$this->excerpt(200, '...'),全局替换,5 分钟搞定,效果中规中矩,纯文本博客或随笔站合适 - 平衡档:保留 content,写文章手动插 more 标签,每篇定制截断点,效果最好但要求每篇都记得加。技术博客与长文站合适
- 完美档:自定义 theme_smart_excerpt + 兜底逻辑,首页保留图片和加粗、限制字符数、有 more 则按 more 截、没有则按字数截、跳过代码块。运营站合适
我自己用的是完美档,代码就放在 functions.php 里,一次写好用三年。Typecho 的优点之一就是模板系统简单直接,所有控制都在 PHP 文件里没有黑盒,改一行立刻生效——这也是我从 WordPress 转过来后再没换过的核心原因。
常见问题解答
改了 index.php 但首页还是显示全文,是不是没生效?
几种常见原因:第一,你改的不是当前启用主题的 index.php,确认下后台外观里启用的是哪个主题;第二,页面缓存没清,如果装了 TpCache 或 PageCache 插件,去插件设置里清一次缓存;第三,改的位置不对,index.php 里可能有多个 content() 调用(比如循环外的某个特殊位置),全局 grep 一下确保都改了;第四,部分主题用 archive.php 处理首页,这种情况要改 archive.php 而不是 index.php。
excerpt 输出的内容里有奇怪的乱码或半个字符怎么办?
这是 PHP 的 mbstring 扩展没启用导致的。检查 php.ini 里 mbstring 是否开启,并确保 default_charset = UTF-8。Typecho 内部大量依赖 mbstring,没开启会出各种诡异字符问题。宝塔面板装 PHP 时默认会启用 mbstring,源码编译装的可能没启用,要用 phpinfo() 确认。
能不能让首页显示文章封面图加摘要?
可以。Typecho 没有内置特色图概念,但可以读取文章里第一张图:用正则 preg_match('/img[^>]+src=[\"\\']([^\"\\']+)/', $this->content, $m) 提取,没有就用主题默认图。更优雅的做法是用 Typecho 的 fields 功能,给每篇文章手动指定一个 cover 字段,模板里读 $this->fields->cover。后者性能更好,也避免文章内第一张图不是合适封面的问题。
从 WordPress 迁过来的博主,excerpt 用法是不是一样?
概念一样写法不同。WordPress 是 the_excerpt(),Typecho 是 $this->excerpt();WordPress 默认按词数截(55 个英文单词),Typecho 按字符数截(默认 100)。WP 的 excerpt_length 钩子在 Typecho 对应的是 excerptEx 钩子。中文站点 Typecho 的字符数计算更直观也更可控。手动写在文章里的 more 标签两边都通用,迁站不用改。
excerpt 函数对中文字符的截取会不会出现乱码?
1.1 老版本曾经按字节算过,可能截到半个 UTF-8 字节序列导致乱码。1.2 之后改用 mb_substr,按字符算,不会再出乱码。如果你跑的是 1.0 或 1.1 老版本强烈建议升级到 1.2.x 或 1.3.0。1.3.0 还修复了评论 URL XSS 漏洞,安全相关也要升。
首页改成摘要之后社交分享出去图片消失了怎么办?
社交分享走的是 og:image 而不是首页内容,跟摘要无关。og:image 一般在 header.php 里设置。要么用文章自定义字段 cover 指定,要么用 preg_match 从 $this->content 里提第一张图,要么用站点默认 logo 兜底。Facebook Sharing Debugger 工具可以验证 og:image 是否正确解析。
用了 excerpt 之后首页 SEO 描述会自动用摘要吗?
不会。SEO meta description 跟首页文章列表的 excerpt 是两回事。meta description 通常在 header.php 里读取站点设置或文章 description 字段,跟首页布局无关。两者要分开配置:首页 meta description 写"博客整体定位",文章 meta description 写"这篇内容是什么",文章列表的 excerpt 是"让用户判断要不要点开",三者目标不同写法不同。
excerptEx 钩子和直接改模板有什么区别,应该用哪个?
excerptEx 是 Typecho 的全局钩子,注册一次全站生效,所有调用 excerpt 的地方(首页、分类页、RSS、搜索页等)都会走自定义逻辑,逻辑集中维护方便。直接改模板的话每个模板文件都要单独改,容易漏改且第三方主题更新会覆盖你的改动。生产站点强烈建议用 excerptEx 钩子或者写成插件的形式,避免改主题源码。