先给判断:日本访客用手机打开你的站慢,多数时候不是主机不行,而是图片、日文字体和第三方脚本,但这三样要分开算账——页面长时间全白,问题在主机或 CDN 边缘离东京太远;白底和文字先出来、图片一块块往下补,问题在图片和字体;内容都齐了却滑不动、按钮点了半天没反应,问题在脚本抢主线程。按访客看到的顺序把“慢”拆成这三段,先确认是哪一段,再决定改哪里,比一上来就换主机有用得多。
有天晚上,我把一台用了三年的中端安卓机从家里的 Wi-Fi 上摘下来,连上自己的手机热点,限速到 4G 那一档,然后打开自己那个日本站的首页。没开任何工具,就坐在桌前看着它。
第一次:屏幕全白,只有地址栏下面那条进度在慢慢挪。那一段是服务器在回话,专业点叫首字节时间。
第二次:白底、导航、标题出来了,正文位置是一排灰色方块,然后方块被照片一块一块填上,顺序从上往下。那一段是图片和字体在下载。
第三次:内容全在了,我用拇指往下滑,前两屏还行,滑到中间卡了一下,一个横幅挤进来,把下面的文字顶下去,我手指点的地方已经不是我想点的那个链接了。那一段是脚本。
同一个页面,三种慢,三种不同的活儿。我坚持用这台旧手机、用热点、用流量,是因为在我的台式机、在我的宽带、在我登着后台的浏览器里,这三种慢一种都看不见。
白屏那一段:主机和节点
同样配置的机器,源站在哪、CDN 边缘节点在东京还是在别的区域,日本访客的感受完全不一样。反过来也一样:我在上海打开自己的站,跟东京的读者打开它,走的回源路径根本不是一回事,所以我从来不拿“我这边挺快的”当结论。
判断这一段我看两件事。一是白屏时间稳不稳定,二是换一个网络环境(比如换成另一家运营商的热点)白屏时间会不会明显变化。稳定偏长,那是源站或者边缘位置的事,换机房、换节点有用;如果白屏一闪而过,后面才开始慢,那主机不是瓶颈,在这上面继续加钱也买不到访客的体感。
我不太记具体的毫秒数。数字在不同时段、不同运营商下浮动很大,记下来也没有可比性,还容易变成给自己看的成绩单。我看的只有一件事:这段白屏够不够我喝完一口水。
图片和日文字体:访客真正在下载的东西
内容页上体积最大的两个可控项,几乎总是图片和字体。日本站还多一层别人没有的负担:日文字体。
一个完整的日文字体文件动辄几 MB,常用汉字、平假名、片假名加起来字符集本来就大,很多字体还带全角标点和旧字形变体。如果 CSS 里直接 @font-face 引一个完整的 Noto Sans JP 或者某个丸ゴシック,每个第一次来的访客,都要在图片之外再下一份字体。中文站的站长对这个没感觉,因为中文字体大家一般直接用系统字体,很少有人往站里塞。
我的做法是尽量不用网络字体,把字体栈交给系统:-apple-system、“Hiragino Kaku Gothic ProN”、“Yu Gothic”、Meiryo、sans-serif。日本访客的 iPhone 和安卓机上这些字体都在,看着也不违和。如果品牌确实要求某个字体,就用 unicode-range 只切出实际用到的那一段,或者让子集化只保留页面出现的字符——代价是内容一更新子集要重做,这个维护成本得先想清楚再决定。
图片这边我按访客的视觉顺序处理。首屏那张最大的图,通常是文章头图或者商品主图,最优先:转成 AVIF 或者 WebP,配好 srcset,写上宽高。首屏以外的图一律 loading=“lazy”,但首屏那张千万不能 lazy——这是我自己踩过的坑,图省事给全站图片统一加了 lazy,结果首屏大图要等布局算完才开始下载,反而更慢。
还有一个细节我吃过亏:图片的宽高不能用 CSS 或者 JS 去撑。没有显式尺寸,字体会先排上去,等图片下载完再撑开,访客正在读的那一行就被推走了。那种“文字突然跳一下”的感觉,比慢半秒更让人烦。
缓存:你看到的那一次,从来不是访客看到的那一次
缓存改变的是访客实际下载了什么,所以它必须在访客的状态下验证,不能在编辑器里验证。
我见过太多次:后台开着,页面秒开,就以为缓存生效了。其实登录状态本身就会绕过页面缓存,你看到的永远是没缓存的那一版。WordPress 的缓存插件、CDN 的页面缓存,基本都有这条规则。
我的固定动作是:开一个无痕窗口,确认没登录、没带后台 cookie,访问一次,然后用 curl -I 看响应头——看 cache-control,看边缘节点有没有命中标记,看 HTML 本身有没有被缓存。第一次 MISS 很正常,第二次还是 MISS 就说明配置没生效,多半被 cookie、查询参数或者某条排除规则挡掉了。
这里有个容易被忽略的边界:缓存只对重复访客有效。一个日本读者从搜索结果点进来,他是第一次来,没有缓存,图片和字体该下还是全下。缓存能让回访变快,不能让第一次变快。第一次的体感,只能靠减体积、减阻塞去拿。很多人把缓存当万能药,改完一看指标上去了,新访客打开还是那个样子。
内容都出来了却滑不动:脚本
这一段最容易被误判成“主机不行”,因为它发生在页面看起来已经加载完之后。
同意横幅、统计代码、广告位、聊天窗口、社交嵌入,这些在我自己的页面里排不上号,但在访客的手机上,它们是主线程上最重的几块。中端安卓机的 CPU 本来就紧,一个广告脚本的解析和执行,就够让滑动掉帧、点击延迟。
我怎么找:先在一个测试环境里把所有第三方摘掉,用同一台手机、同一个热点打开,记住“顺”是什么手感。然后一个一个加回去,哪个加回来之后开始卡,就是它。这方法笨,但比看瀑布图直观,因为瀑布图告诉你谁大,不告诉你谁在堵主线程。要更准一点,用开发者工具的 Performance 面板录一段滑动,看主线程那条黄线里的长任务落在哪个脚本上。
找到了也不一定能删。广告是收入,统计要看数,同意横幅在日本市场还有合规上的要求。能做的是排顺序:统计脚本延后加载,广告位给固定高度避免它挤动内容,同意横幅用轻量实现而不是塞一整套 CMP SDK,聊天窗口改成点击后再加载。这些改动不会让指标变好看,但访客的手指能感觉到。
那三个指标,其实就是我说的三段
LCP、CLS、INP 跟我前面拆的三段是对得上的:LCP 大致对应主机加首屏图片,CLS 对应布局有没有被撑动,INP 对应脚本有没有把主线程占住。所以我看它们,是拿它们当诊断信号——它告诉我哪一段有问题,而不是当成绩单。
还有一句要说清楚:页面体验是一组信号里的一项,不是排名的开关。指标刷绿了不等于排名会动;反过来,为了刷绿去砍掉真正对读者有用的东西,是亏的。我的标准一直是访客那台手机,指标只是帮我把问题定位到某一段。
有些优化,访客一辈子感觉不到
我把做过的事分两堆。
一堆是访客能感觉到的:首屏大图变小、去掉几 MB 的日文字体、把阻塞渲染的脚本挪走、把白屏从“能喝完一口水”压到“几乎看不见”。
另一堆是访客感觉不到的:CSS 从 40KB 压到 38KB、给三个域名加 preconnect、把一个小图标换成 SVG、把所有 JS 合并成一个包。第二堆不是没意义,但它们排在第一堆后面,而且做完之后你很可能什么都感觉不到。
这也是我这些年最大的一个转变:我不再问“哪家主机好”,改问“我的访客在哪一刻皱眉”。分数涨了、访客没感觉的优化,我做一轮就停,不反复调。那种循环很耗人,而且它消耗的是你对“什么是真的慢”的判断力。
现在每次改完,还是那台旧手机,还是那个热点,还是从无痕窗口进去。看白屏短了没有,看图片补得快不快,看滑到第三屏的时候手指还跟不跟得上。