[{"data":1,"prerenderedAt":432},["ShallowReactive",2],{"blog-spa-rendering-ssr-ssg-prerender":3,"blog-prev-spa-rendering-ssr-ssg-prerender":114,"blog-next-spa-rendering-ssr-ssg-prerender":184},{"id":4,"title":5,"author":6,"body":7,"canonical":98,"category":99,"cover":98,"date":100,"description":101,"draft":102,"extension":103,"keywords":98,"meta":104,"navigation":105,"noindex":102,"ogImage":98,"path":106,"seo":107,"seoDescription":98,"seoTitle":98,"stem":108,"tags":109,"updated":98,"__hash__":113},"blog\u002Fblog\u002Fspa-rendering-ssr-ssg-prerender.md","单页应用做 SEO，服务端渲染和预渲染怎么选","元析智能",{"type":8,"value":9,"toc":89},"minimark",[10,14,18,25,31,37,41,44,47,50,69,72,75,78,86],[11,12,13],"p",{},"能 SSG 就别 SSR，能 SSR 就别预渲染，能预渲染就别硬扛纯客户端渲染。选型只看两件事：内容更新频率、页面数量。",[15,16,17],"h2",{"id":17},"三种方案各自适合谁",[11,19,20,24],{},[21,22,23],"strong",{},"SSG（构建时生成 HTML）","：文档站、营销页、博客、产品功能介绍页。内容不是每分钟变，页面几千到几万个都在射程内。成本最低、速度最快。2025 年 4 月一个开发者文档站 4200 页改 Next.js 的 SSG，初始 HTML 直接带全文，上线两个月收录率 92%，LCP 也从 4.1 秒降到 1.3 秒。",[11,26,27,30],{},[21,28,29],{},"SSR（请求时服务端渲染）","：价格页、搜索结果页、带登录态的个性化页面。需要 Node 服务，运维成本高；缓存配不好，TTFB 会比 SSG 慢一截。适合内容必须实时的场景。",[11,32,33,36],{},[21,34,35],{},"预渲染","：给蜘蛛返回提前渲染好的静态快照，真实用户还是走原 SPA。适合改造成本卡死、只求先被收录的团队。代价是快照会过期，要定时刷新，百度对快照的识别也不如 Google 稳定。",[15,38,40],{"id":39},"_2025-年-7-月的救急改造","2025 年 7 月的救急改造",[11,42,43],{},"一个 SaaS 站 600 个页面，前端是纯 React SPA，初始 HTML 只有挂载点。团队排不出重写排期，先用预渲染顶上：三周内收录从 180 涨到 520。",[11,45,46],{},"问题随之而来。价格页的快照一周才刷一次，中间改价后展示的是旧价格，客服接到一堆投诉。后来把价格页单独改成 SSR，其余页面保留预渲染，才稳定下来。这个组合很常见——高频变化的少数页面走 SSR，长尾页面走静态。",[15,48,49],{"id":49},"判断清单",[51,52,53,57,60,63,66],"ul",{},[54,55,56],"li",{},"内容一周才变一次，页面是列表型 → SSG",[54,58,59],{},"内容每次请求都可能不同 → SSR",[54,61,62],{},"已有 Node 服务、团队有人维护 → SSR 成本可接受",[54,64,65],{},"只求收录、不求实时 → 预渲染",[54,67,68],{},"以上都做不到 → 至少把 title、H1、正文前三段、内链塞进初始 HTML，别全靠 JS",[15,70,71],{"id":71},"成本对照",[11,73,74],{},"我们报过价的三个方案，以 800 个页面的 SaaS 站为例：纯预渲染接入大约 2 到 3 人周，主要成本在快照刷新和监控；SSG 改造 6 到 8 人周，要动路由和数据层；SSR 改造 10 人周以上，还得配 Node 服务、缓存和日志。预算差距在五倍左右，所以选型不能只看\"哪个先进\"。",[15,76,77],{"id":77},"改之前先确认到底缺什么",[11,79,80,81,85],{},"用 ",[82,83,84],"code",{},"curl"," 拉一下页面，看初始 HTML 里有没有正文。有的站其实 SSR 做了，只是 H1 和正文用了懒加载；有的站缺的只是内链。整站重构之前先定位缺口，能省下一大笔预算。",[11,87,88],{},"我们接过一个客户，被外包公司报了 40 万的 SSR 改造，实际问题是模板里把正文渲染在 JS 之后，改模板花了两天。",{"title":90,"searchDepth":91,"depth":91,"links":92},"",2,[93,94,95,96,97],{"id":17,"depth":91,"text":17},{"id":39,"depth":91,"text":40},{"id":49,"depth":91,"text":49},{"id":71,"depth":91,"text":71},{"id":77,"depth":91,"text":77},null,"技术SEO","2025-11-17","三种方案解决同一个问题——让蜘蛛拿到有正文的 HTML，但成本和适用场景差很远。我们用三个客户站的改造结果，给出按站点类型排序的选型顺序和判断清单。",false,"md",{},true,"\u002Fblog\u002Fspa-rendering-ssr-ssg-prerender",{"title":5,"description":101},"blog\u002Fspa-rendering-ssr-ssg-prerender",[110,111,112],"渲染","SSR","SPA","1IQG_zGcoYbbtQtpBcIU-WkwxkRCt5rouU8BYQcjKo0",{"id":115,"title":116,"author":6,"body":117,"canonical":98,"category":172,"cover":98,"date":173,"description":174,"draft":102,"extension":103,"keywords":98,"meta":175,"navigation":105,"noindex":102,"ogImage":98,"path":176,"seo":177,"seoDescription":98,"seoTitle":98,"stem":178,"tags":179,"updated":98,"__hash__":183},"blog\u002Fblog\u002Fai-draft-human-edits.md","AI 写初稿可以，但有三件事必须人来做",{"type":8,"value":118,"toc":166},[119,122,125,128,131,134,138,141,144,147,150,154,157,160,163],[11,120,121],{},"我们从 2025 年 3 月开始在内部用 AI 写初稿，到现在八成文章有它参与，但没有一篇是直接发的。原因很实在：AI 写得出来的内容，竞争对手也写得出来，能被搜索和 AI 搜索引用的东西，必须有它拿不到的信息。",[15,123,124],{"id":124},"必须人做的三件事",[11,126,127],{},"第一是观点。AI 擅长把两种说法都讲一遍，然后说各有优缺点。读者要的不是这个。一篇写外链的文章，得敢说某类链接别碰；一篇写工具的文章，得敢说某个功能不值得买。这个判断只能人来下，因为要承担后果。",[11,129,130],{},"第二是具体操作。改了哪个字段、动了哪条规则、在哪个菜单里点，这些细节 AI 会写得很像，但经常是错的。我们每篇稿子至少有一处真实操作细节，比如 GSC 导出上限 1000 行、robots 里要写完整路径、canonical 要绝对地址。",[11,132,133],{},"第三是一手数据。自己站的 GSC 曝光、日志里的抓取次数、工具跑出来的爬取结果，这些 AI 编不出来。哪怕是“我们站 8 月有 43 个页面卡在已抓取未编入索引”这种小数据，也比一段正确但空泛的论述有价值。",[15,135,137],{"id":136},"ai-真正擅长什么","AI 真正擅长什么",[11,139,140],{},"它擅长结构。给定 5 个要点和 1 个反常识结论，它能很快铺出 1200 字的骨架，标题层级、过渡句、总结段都有。它还擅长改写：同一篇内容改成给老板看的版本、给工程师看的版本，或者翻成英文。",[11,142,143],{},"另外，它在梳理工具知识和术语上很省时间。要写一篇解释 GA4 渠道组的文章，让它先列一遍概念和常见误解，人再核对，比从头查资料快得多。",[15,145,146],{"id":146},"我们的流程",[11,148,149],{},"人先列 5 个要点加 1 个反常识结论，写清每段要用的数字；AI 出 1200 字初稿；人删掉大约 40%，尤其是那些“一方面另一方面”的平衡句；补两处自己的操作细节和一段经历；最后做事实核查，工具版本号、价格、参数名逐个对。",[15,151,153],{"id":152},"我们不给-ai-的三类活","我们不给 AI 的三类活",[11,155,156],{},"第一类，写结论段。让 AI 写“所以这个方法值不值得用”，它给你的永远是“要视情况而定”。这种段落发出去等于没写，读者也不会记住。",[11,158,159],{},"第二类，写带时间锚点的复盘。AI 会编一个“上个月我们给客户做了 X”，编得还挺像。这种句子必须人来写，因为我们写过什么、什么时候做的，只有我们自己知道。",[11,161,162],{},"第三类，写报价和承诺。这类内容涉及法律和交付边界，写错一句就可能被截图当承诺。我们所有涉及价格、周期、效果的段落，都是人写完 + 二次核对。",[11,164,165],{},"我把这个流程贴在共享文档里，新来的写手第一天就照着做。用 AI 写没有独家信息的文章，等于给自己造垃圾页，2025 年的搜索结果对同质化内容已经很不客气了。",{"title":90,"searchDepth":91,"depth":91,"links":167},[168,169,170,171],{"id":124,"depth":91,"text":124},{"id":136,"depth":91,"text":137},{"id":146,"depth":91,"text":146},{"id":152,"depth":91,"text":153},"内容运营","2025-11-16","我们从 2025 年 3 月开始用 AI 写初稿，八成文章有它参与，但没有一篇直接发。观点、具体操作、一手数据这三件事决定了文章值不值得被引用。",{},"\u002Fblog\u002Fai-draft-human-edits",{"title":116,"description":174},"blog\u002Fai-draft-human-edits",[180,181,182],"AI写作","内容生产","编辑流程","ayiHvLX3_uYFrX5LpjK0hQDHqSoAIMrtPSQytzAsqZ4",{"id":185,"title":186,"author":6,"body":187,"canonical":98,"category":99,"cover":98,"date":421,"description":422,"draft":102,"extension":103,"keywords":98,"meta":423,"navigation":105,"noindex":102,"ogImage":98,"path":424,"seo":425,"seoDescription":98,"seoTitle":98,"stem":426,"tags":427,"updated":98,"__hash__":431},"blog\u002Fblog\u002Fcls-image-dimension-fix.md","给图片和嵌入组件写死尺寸，CLS 一周从零点三降到零点零九",{"type":8,"value":188,"toc":414},[189,192,196,199,266,277,280,284,287,376,383,386,389,392,395,398,401,404,407,410],[11,190,191],{},"上个月我们给一个开发者工具站做 CLS 整改，从 0.31 降到 0.09，用了一周。动的是 168 个没写尺寸的图片和 6 个嵌入组件。方法不新鲜，难的是把全站扫干净，一个都别漏。",[15,193,195],{"id":194},"图片给每个-img-补上宽高","图片：给每个 img 补上宽高",[11,197,198],{},"CLS 的根源是浏览器不知道元素最终多大，等内容到了再撑开。宽高属性是成本最低的修法，现代浏览器会拿这两个数自动算宽高比、预留空间。",[200,201,205],"pre",{"className":202,"code":203,"language":204,"meta":90,"style":90},"language-html shiki shiki-themes github-light github-dark","\u003Cimg src=\"dashboard.webp\" width=\"1440\" height=\"810\" alt=\"产品后台截图\" loading=\"lazy\">\n","html",[82,206,207],{"__ignoreMap":90},[208,209,212,216,220,224,227,231,234,236,239,242,244,247,250,252,255,258,260,263],"span",{"class":210,"line":211},"line",1,[208,213,215],{"class":214},"sVt8B","\u003C",[208,217,219],{"class":218},"s9eBZ","img",[208,221,223],{"class":222},"sScJk"," src",[208,225,226],{"class":214},"=",[208,228,230],{"class":229},"sZZnC","\"dashboard.webp\"",[208,232,233],{"class":222}," width",[208,235,226],{"class":214},[208,237,238],{"class":229},"\"1440\"",[208,240,241],{"class":222}," height",[208,243,226],{"class":214},[208,245,246],{"class":229},"\"810\"",[208,248,249],{"class":222}," alt",[208,251,226],{"class":214},[208,253,254],{"class":229},"\"产品后台截图\"",[208,256,257],{"class":222}," loading",[208,259,226],{"class":214},[208,261,262],{"class":229},"\"lazy\"",[208,264,265],{"class":214},">\n",[11,267,268,269,272,273,276],{},"有了这两个属性，图片下载前就能按 16:9 占位。响应式图片再配上 ",[82,270,271],{},"sizes"," 和 ",[82,274,275],{},"srcset","，占位比例依然成立。",[11,278,279],{},"查法：跑一遍全站爬虫，把没有 width 和 height 的 img 列出来。我们这个站 312 张图里有 168 张缺尺寸，主要来自博客正文和客户案例页，编辑器上传时没做校验。",[15,281,283],{"id":282},"嵌入组件iframe-也要占位","嵌入组件：iframe 也要占位",[11,285,286],{},"地图、演示视频、客服 iframe 经常从 0 高度起步，加载完撑到 400 多像素。改法是外层容器写死比例，iframe 填满容器。",[200,288,292],{"className":289,"code":290,"language":291,"meta":90,"style":90},"language-css shiki shiki-themes github-light github-dark",".embed { aspect-ratio: 16 \u002F 9; width: 100%; }\n.embed iframe { width: 100%; height: 100%; border: 0; }\n","css",[82,293,294,336],{"__ignoreMap":90},[208,295,296,299,302,306,309,312,315,318,321,324,326,329,333],{"class":210,"line":211},[208,297,298],{"class":222},".embed",[208,300,301],{"class":214}," { ",[208,303,305],{"class":304},"sj4cs","aspect-ratio",[208,307,308],{"class":214},": ",[208,310,311],{"class":304},"16",[208,313,314],{"class":214}," \u002F ",[208,316,317],{"class":304},"9",[208,319,320],{"class":214},"; ",[208,322,323],{"class":304},"width",[208,325,308],{"class":214},[208,327,328],{"class":304},"100",[208,330,332],{"class":331},"szBVR","%",[208,334,335],{"class":214},"; }\n",[208,337,338,340,343,345,347,349,351,353,355,358,360,362,364,366,369,371,374],{"class":210,"line":91},[208,339,298],{"class":222},[208,341,342],{"class":218}," iframe",[208,344,301],{"class":214},[208,346,323],{"class":304},[208,348,308],{"class":214},[208,350,328],{"class":304},[208,352,332],{"class":331},[208,354,320],{"class":214},[208,356,357],{"class":304},"height",[208,359,308],{"class":214},[208,361,328],{"class":304},[208,363,332],{"class":331},[208,365,320],{"class":214},[208,367,368],{"class":304},"border",[208,370,308],{"class":214},[208,372,373],{"class":304},"0",[208,375,335],{"class":214},[11,377,378,379,382],{},"这个站的定价页嵌了三个客户评价视频，原来每个都从 0 撑到 320 像素，改完位移归零。顺手把 YouTube 的嵌入换成 ",[82,380,381],{},"youtube-nocookie"," 加懒加载，首屏也轻了一点。",[15,384,385],{"id":385},"骨架屏要预留真实高度",[11,387,388],{},"骨架屏高度和真实内容不一致，等于位移换了个形式。我们的做法是拿一次真实数据截图量出高度，骨架屏写死这个值，误差控制在 8 像素以内。这个站的后台截图区差了 120 像素，改完才真正消掉。",[15,390,391],{"id":391},"广告位和推荐位",[11,393,394],{},"有广告或者合作推荐的站，位子必须提前占好。常见做法是给容器一个 min-height，里面没内容时也占着。这个站的博客文末有一条合作推荐，原来从 0 撑到 180 像素，用户读到最后一行时正文被推走，改完加了 min-height 就好了。",[11,396,397],{},"这类位子的另一个问题是内容有时不来，占位就一直空着。我们一般给个 3 秒超时，超时后容器收起，收起动作放在用户看不见的区域，不影响 CLS。",[15,399,400],{"id":400},"验收方式",[11,402,403],{},"改完别只看 Lighthouse。用 Chrome Performance 面板录一次移动端加载，看 Experience 轨道还有没有位移；再等两周看 GSC 的真实数据。这个站实验室数据第一天就到 0.02，真实数据两周后稳定在 0.09，差距来自长尾页面——旧博客里有 40 多张历史图片还在漏。",[11,405,406],{},"我们给客户定的验收标准是两件事同时满足：实验室三次测试都在 0.1 以下，真实数据两周后 P75 在 0.1 以下。只看前者容易自欺，只看后者周期太长，项目会拖。",[11,408,409],{},"最后提醒一句：公众号文章和富文本编辑器粘进来的图片最容易漏。我们的做法是在编辑器上传环节强制写入宽高，从源头堵住，不然三个月后又会冒出来。",[411,412,413],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":90,"searchDepth":91,"depth":91,"links":415},[416,417,418,419,420],{"id":194,"depth":91,"text":195},{"id":282,"depth":91,"text":283},{"id":385,"depth":91,"text":385},{"id":391,"depth":91,"text":391},{"id":400,"depth":91,"text":400},"2025-11-18","一个开发者工具站 CLS 0.31，不是靠玄学优化的：168 个图片补宽高、6 个 iframe 用 aspect-ratio 占位、骨架屏写死真实高度。附查询方法和两段可直接抄的代码。",{},"\u002Fblog\u002Fcls-image-dimension-fix",{"title":186,"description":422},"blog\u002Fcls-image-dimension-fix",[428,429,430],"CLS","图片尺寸","布局偏移","NtIb0Arzyvtl8vRSevA0gvsTFcRnzCleytBU6Dx3vKk",1794088110770]