前言:为什么要把优化过程写成一份记录
本站(511遇见)近期做了一轮较为完整的 SEO 与前端性能梳理,覆盖了元信息、结构化数据、样式加载、图片加载、主题函数文件清理等多个方面。过程中既有确实生效的改动,也有走了弯路、改出问题又回退的项目。
把全过程如实记录下来,一是给自己留一份可回查的维护档案,二是其中几个坑(尤其是脚本 defer 与 global-styles 注销)在网上很少见到完整说明,希望本文档能给使用 Aldehyde 主题或类似老站的朋友省下一些时间。
说明:本文记录的是本站真实环境的处置方式,服务器为 Windows(XAMPP / phpStudy 类环境),未使用 CDN。如果你的环境不同,部分结论需要重新判断,文中已逐处标注。
一、修订清单总览
| 序号 | 项目 | 处置 | 收益 |
|---|---|---|---|
| 1 | 首页 title / description / 首页简介 | 已改 | 小 |
| 2 | 语言标记统一为 zh-CN | 已改 | 无(仅字面统一) |
| 3 | JSON-LD 结构化数据五项修正 | 已改 | 纠正一处真实错误 |
| 4 | 注销古腾堡(Gutenberg)区块样式 | 已改 | 全站每页省约 12KB |
| 5 | 菜单两处硬伤修正 | 已改 | 语义正确性 |
| 6 | 首屏轮播图加载优化 | 已改 | 改善 LCP |
| 7 | functions.php 整理 + 修复 4 个 bug | 已改 | 可维护性 + 修复无效代码 |
| 8 | 脚本 defer、移除 jquery-migrate | 已放弃 | — |
二、元信息:title、description 与首页简介
1. title 调整
原首页标题把站点名放在最前面,例如"511遇见 - 易语言Lua……"。问题在于:几乎没有人会搜索"511遇见"这个品牌词,标题最靠前的位置是权重最高的位置,放一个没人搜的词属于浪费。
改为关键词前置:
<title>易语言Lua教程_多线程自动化与WordPress建站 - 511遇见</title>
这里有个流传很广的说法是"标题里的下划线不利于分词"。这个说法只在英文语境下成立:英文靠空格和标点断词,red_shoes 会被当成一个连体词。中文分词走的是词典,不依赖标点,所以下划线、竖线在中文标题里差别可以忽略,不必为此反复修改。
2. description 调整
需要明确一点:meta description 不是排名因素,Google 已明确说明它不参与排序,只影响搜索结果页的点击率。原来的版本堆了 8 个主题方向、约 92 字,容易被截断。现调整为约 76 字,保留核心方向并加入"图色识别""源码下载"这类更具体的词。
3. 首页正文
首页正文从约 90 字扩充到约 190 字,仅增加一个段落,不新增任何板块模块。原因很简单:首页顶部已有 7 大分类导航、底部已有教程分类模块,再加一排"板块导语"等于把同一件事讲第三遍,视觉上只会更臃肿。
判断标准可以复用:如果一段内容的链接在导航或底部已经点得到,它就不该再出现在正文里。
三、语言标记统一
改动前源码里存在三个值:<html lang="zh-Hans">、og:locale 为 zh_CN、JSON-LD 的 inLanguage 为 zh-CN。
实际上后两者是同一个东西(大小写不敏感),真正不同的只有 zh-Hans(纯简体,不带地区)与 zh-CN(简体/中国)。两者都完全合法,搜索引擎对简体中文的识别结果一致,此项没有排名收益,只做字面统一。
主题使用 language_attributes() 输出该属性,且 AIOSEO 会通过同一钩子追加 prefix="og: ...",因此不能直接硬编码覆盖,要用过滤器只替换 lang 的值:
/*** 统一全站语言标记为 zh-CN* 只替换 lang="..." 的值,dir、prefix 等属性原样保留* 优先级 999 确保在 AIOSEO 之后执行*/add_filter( 'language_attributes', function ( $output ) {
return preg_replace( '/lang="[^"]*"/', 'lang="zh-CN"', $output );
}, 999 );
注意:优先级必须够高。如果改完发现源码里 prefix="og:" 消失了,说明执行顺序不对,需要调整优先级。
四、JSON-LD 结构化数据五项修正
1. 删除首页单条面包屑
首页的 BreadcrumbList 只有"首页"一项,而页面上并没有可见的面包屑导航。Google 要求结构化数据必须与页面可见内容一致,声明了一个页面上看不到的东西属于无效噪音,删掉最干净。
2. 清理 WebPage 对面包屑的引用
删除 BreadcrumbList 后,如果不同时清掉 WebPage 节点里的 "breadcrumb":{"@id":"...#breadcrumblist"},该引用会指向一个不存在的 @id,富媒体测试会报"引用未定义"。这两步必须配套。
3. 修正 WebSite.name(这是唯一一处真实错误)
AIOSEO 把首页的 SEO 标题当成了网站名,输出为:
"name": "易语言Lua教程_多线程自动化与WordPress建站 - 511遇见"
而 WebSite.name 应该是网站名本身,即"511遇见"。这个字段会影响知识面板与 sitelinks 展示的站点名称,属于填错,不是"可以更好"。
4. 补 SearchAction
这是 sitelinks searchbox(搜索品牌词时结果下方的站内搜索框)的必要条件。注意 urlTemplate 里的 {search_term_string} 必须与 query-input 的 name 完全一致,改变量名会导致搜索框不生效。
5. 补 Organization 的 sameAs
填写社交账号地址,帮助搜索引擎建立实体关联。该字段只接受 URL,微信公众号 ID 这类非网址的值无法填入。
完整代码(仅首页生效):
add_filter( 'aioseo_schema_output', function ( $graphs ) {
if ( ! is_front_page() ) {
return $graphs;
}foreach ( $graphs as $key => $graph ) {
if ( empty( $graph['@type'] ) ) {
continue;
}// 1) 删除首页单条面包屑if ( 'BreadcrumbList' === $graph['@type'] ) {
unset( $graphs[ $key ] );
continue;
}// 2) 清掉 WebPage 对已删面包屑的引用if ( 'WebPage' === $graph['@type'] ) {
unset( $graphs[ $key ]['breadcrumb'] );
continue;
}// 3) + 4) 修正 WebSite:改 name,补 SearchActionif ( 'WebSite' === $graph['@type'] ) {
$graphs[ $key ]['name'] = '511遇见';
$graphs[ $key ]['potentialAction'] = array(
'@type' => 'SearchAction',
'target' => array(
'@type' => 'EntryPoint',
'urlTemplate' => home_url( '/?s={search_term_string}' ),
),
'query-input' => 'required name=search_term_string',
);
}// 5) Organization 补 sameAsif ( 'Organization' === $graph['@type'] ) {
$graphs[ $key ]['sameAs'] = array(
'https://space.bilibili.com/508420984',
);
continue;
}}return array_values( $graphs );
}, 999 );
验证:改完后用 Google 富媒体搜索结果测试工具检测,首页应显示"未检测到任何内容"且无错误无警告。首页本来就不该有富媒体增强,干净即为正确结果。
五、注销古腾堡区块样式(省 12KB)
什么是古腾堡
古腾堡(Gutenberg)是 WordPress 2018 年起主推的区块编辑器。为了渲染按钮、分栏、引用等区块,WP 会自动向每个页面输出一份约 12KB 的样式表。
如果站点的文章全部用经典编辑器或手写 HTML 编写,这份样式表就是纯空转。判断方法(phpMyAdmin 执行):
SELECT COUNT(*) FROM wp_posts
WHERE post_status='publish'
AND post_content LIKE '%<!-- wp:%';
本站返回 0,说明全站无区块内容,可以安全注销。
正确的注销方式
add_action( 'wp_enqueue_scripts', function () {
if ( is_admin() ) {
return;
}// A. 常规移除wp_dequeue_style( 'wp-block-library' );
wp_dequeue_style( 'classic-theme-styles' );
wp_dequeue_style( 'global-styles' );
// B. 摘掉核心挂载(含 wp_footer 上那个)remove_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles' );
remove_action( 'wp_footer', 'wp_enqueue_global_styles', 1 );
// C. 兜底if ( isset( wp_styles()->registered['global-styles'] ) ) {
wp_styles()->add_data( 'global-styles', 'after', '' );
}}, 999 );
踩坑记录:只写 A 段是不够的。global-styles 由核心函数 wp_enqueue_global_styles() 输出,而这个函数同时挂在 wp_enqueue_scripts 和 wp_footer(优先级 1)两个钩子上。我们在 wp_enqueue_scripts(优先级 999)把它摘掉后,页面走到 wp_footer 时它又被加回来了。必须把两个挂载一并移除才能彻底生效。
重要提醒:本站 dashicons.min.css 不能注销,菜单图标插件 menu-icons 依赖它,动了会导致导航图标消失。如果日后改用区块编辑器写文章,必须删除本段代码,否则按钮、分栏、引用块会丢失样式。
六、菜单与内链的两处硬伤
1. "关于遇见"指向归档页
菜单文字是"关于遇见",点进去却是 WordPress 的按月归档列表,访客预期与实际严重不符。处理方式有两个:改菜单文字为"文章归档"(零风险),或真的新建一个关于我们页面。本站选择了前者,因为后者属于内容工作。
2. Ngnix 拼写错误
正确写法是 Nginx。此处只改菜单显示的文字,不要改分类别名(slug)。本站该分类地址为 /front-end/ans,slug 不美观但已被收录,一旦修改会产生 404,需要额外配置 301 重定向。搜索引擎不靠 slug 判断内容,为此改 slug 属于负收益。
同类提醒:已发布文章的 URL 也不要为了"可读性"去改,比如 eyuyan-damo-wgzh.html 这类缩写看着不友好,但改动等于制造一批 404,风险远大于收益。
七、首屏图片加载与 LCP
LCP(Largest Contentful Paint)指"最大那块内容出现在屏幕上的时间",是 Core Web Vitals 三项指标之一,及格线为 2.5 秒。本站首页最大的内容就是轮播第一张图。
本站轮播共 10 张图,处理方式为:首图立即加载并高优先级,其余 9 张滚动到才加载。
<!-- 首图 -->
<img src="/pagephoto/banner/dmyj.jpg" alt="易语言图色自动化教程"
width="960" height="595" loading="eager" fetchpriority="high">
<!-- 其余 9 张 -->
<img src="..." loading="lazy" decoding="async">
所有图片都写了 width 和 height。这一步非常关键,它让浏览器在图片下载完成前就能算出占位比例,避免图片突然加载时把下方内容挤走——这正是 CLS(布局偏移)的主要来源。
关于 preload:本站未采用
常见的优化建议是在 <head> 里加一行 <link rel="preload" as="image">,让浏览器更早发现这张图。但本站已启用 WebP 转换,服务器上实际存在 .jpg 与 .jpg.webp 两种返回情况。
此时 preload 写死 .jpg 就可能出问题:浏览器预加载了一个地址,<img> 实际请求的却是另一个,同一张图下载两遍,非但不快反而多耗带宽。因此本站放弃此项,只保留 loading="eager" 与 fetchpriority="high"——这两个属性跟随标签本身,不管最终加载哪种格式都自动正确。
八、functions.php 整理与 4 个 bug 修复
主题 functions.php 经过多年累积已超过 1000 行,格式与注释混乱。本次做了分区整理(共 27 个区块)与注释补齐,不改动任何一行代码的执行逻辑,并修复了 4 个实际存在的问题。
bug 1:禁用修订版本的写法从未生效
// 原写法(无效)remove_action( 'pre_post_update' , 'wp_save_post_revision' );
// 修复后(生效)add_filter( 'wp_revisions_to_keep', '__return_zero' );
wp_save_post_revision 并未挂在 pre_post_update 上,那行代码从未起过作用,数据库里一直在堆积修订记录。改用官方过滤器后真正生效。本站累计修订记录为 258 条,量很小,历史数据未做清理。
bug 2:移除资源版本号属于负收益
原文件里有移除 CSS / JS 链接 ?ver= 的代码。移除版本号唯一的好处是让 CDN 缓存更久,本站未使用 CDN,这个好处不存在;副作用却是实打实的:去掉版本号后浏览器无法区分新旧文件,改了 main.css 之后老访客仍加载缓存里的旧版,表现为"我明明改了,前台就是不变",排查极其费时。已停用,原代码保留为注释。
bug 3:标签云取色写法错误
// 原写法$color = $colors[ dechex( rand( 0, 7 ) ) ];
// 修复后$color = $colors[ rand( 0, count( $colors ) - 1 ) ];
dechex() 返回的是字符串,拿字符串当数组下标是错误写法,只是 PHP 恰好会把纯数字字符串转成整数才没报错。一旦配色数量超过 10 个,dechex 会返回字母,立刻取不到值。
bug 4:取图函数未判空
// 修复:在循环外调用时 $post 为 null,PHP 8 下会抛 Warningif ( empty( $post->post_content ) ) {
return '';
}
九、踩坑:为什么放弃了脚本 defer
这是本次唯一一次改出问题又回退的操作,值得完整记录。
最初为了减少白屏时间,给 <head> 里的 jQuery 与 custom.js 加了 defer。改完立刻出现三条报错:
Bootstrap's JavaScript requires jQuery (external.js)
jQuery(...).slicknav is not a function (custom.js)
e.indexOf is not a function (jquery.min.js / custom.js)
原因:WordPress 的依赖系统只保证输出顺序,不保证执行方式。本站页脚还挂着 bootstrap、slicknav 等同步脚本,它们没有被加 defer,于是执行顺序变成:
| 脚本 | 状态 | 执行时机 |
|---|---|---|
| jquery.min.js | defer | 等 HTML 解析完 |
| custom.js | defer | 同上,排在 jQuery 后 |
| bootstrap / slicknav(页脚) | 同步 | 解析到就立刻执行 ← 此时 jQuery 还没加载 |
结果就是 jQuery 被推迟了,但用它的脚本没推迟,后者先跑、直接报错。其中 slicknav 是手机端的汉堡菜单,它坏了意味着手机访问时导航可能点不开——这比省下的零点几秒重要得多。
教训:凡是动到运行中 JS 的改动,改完必须打开浏览器控制台确认,而不是只看页面"看起来正常"。菜单、轮播这类交互,不点一下是发现不了损坏的。另外,判断依据如果只来自静态源码、且源码还不完整(比如被截断),结论很可能是错的。
同理,移除 jquery-migrate 也一并放弃。它只省约 10KB,而对一个拥有 10 张轮播大图的页面来说,10KB 可以忽略。
十、最终效果
使用浏览器内置的 Lighthouse(移动端模拟)实测:
| 指标 | 结果 | 及格线 | 判定 |
|---|---|---|---|
| Performance 总分 | 94 | 90–100 为优秀 | 优秀 |
| CLS(布局偏移) | 0 | < 0.1 | 满分 |
| TBT(主线程阻塞) | 0 ms | < 200 ms | 满分 |
| LCP(最大内容绘制) | 2.7 s | < 2.5 s | 略超 |
| FCP(首次内容绘制) | 2.3 s | < 1.8 s | 参考项 |
CLS 与 TBT 两项满分,其中 CLS 为 0 正是得益于所有图片都写了宽高属性。LCP 2.7 秒略微超标,需要说明的是:Lighthouse 默认会模拟 4G 网络与中端手机,属于限速环境,真实访客大概率跑在及格线内。该项的主要瓶颈是轮播的 10 张大图,要进一步优化得从压缩图片入手,不是再改几行代码能解决的。
另外确认了域名跳转为单跳 301:无论输入 http://511yj.com 还是 https://511yj.com,都一步直达 https://www.511yj.com,这是最优状态。
十一、关于 canonical 尾斜杠的说明
本站 canonical 与 og:url 均不带尾斜杠,而站内 logo、Organization.url 带斜杠。这类不一致会被搜索引擎归一化处理,不会产生重复内容问题,不需要专门处理。WordPress 后台"设置 → 常规"里的站点地址也无法通过加斜杠来统一(保存时会自动去除),因此保持现状即可。
十二、明确放弃的项目
为了便于日后回查,把评估后决定不做的项目一并列出:
| 项目 | 放弃原因 |
|---|---|
| 脚本 defer | 会导致手机端菜单失效(已实测) |
| 移除 jquery-migrate | 仅省 10KB,风险大于收益 |
| LCP 图片 preload | WebP 环境下可能重复下载 |
| WebPage 改为 CollectionPage | Google 无对应富媒体结果,改了无可见变化 |
| 给列表加 ItemList | 四个 Tab 中有三个默认隐藏,声明了看不见的内容反而违反一致性要求 |
| 改分类 slug、改文章 URL | 会产生 404,风险远大于"可读性"收益 |
| 清理内联 CSS | 核心自动内联是 WP 的既定优化行为,不是缺陷 |
| 防 iframe 改用 CSP | frame-ancestors 无法通过 meta 标签设置;且 CSP 会误伤微信内置浏览器等合法场景,导致白屏 |
| og:image:alt、插件指纹清理 | 收益可忽略 |
十三、小结
这一轮梳理下来,一个很实际的体会是:SEO 技术层的改动收益递减得非常快,而风险并不递减。
真正有确定收益的,集中在少数几项:修正填错的结构化数据字段、注销确实在空转的样式、修正菜单语义错误、给图片补齐宽高。这些做完之后,继续往下挖出来的项目,价值往往低于所花费的时间,甚至可能改出真问题(如本次的 defer)。
技术层做完后,影响更大的其实是另外两个层面:教程内容本身的质量与数量,以及外部链接与品牌。那不是改代码能解决的。优化做到一定程度就该停手,把时间花在写下一篇教程上。
后续观察:改动是否真的带来变化,需要等 28 天后在 Google Search Console 里对比"展示次数"与"点击率",以及"核心网页指标"中真实访客的数据。源码层面的判断替代不了真实数据。