[{"data":1,"prerenderedAt":1008},["ShallowReactive",2],{"post-\u002Fposts\u002F71970a13":3,"post-nav-\u002Fposts\u002F71970a13":965,"post-related-\u002Fposts\u002F71970a13":984},{"id":4,"title":5,"abbrlink":6,"aisummary":7,"body":8,"category":950,"cover":951,"date":952,"description":953,"draft":954,"extension":955,"featured":954,"meta":956,"navigation":112,"path":957,"pinned":954,"seo":958,"stem":959,"tags":960,"updated":951,"__hash__":964},"posts\u002Fposts\u002Fzero-hardcode.md","一个配置文件能解决多少问题","71970a13","文章探讨了“零硬编码”配置策略的实际应用与收益。作者将可变内容分为四层配置，确保修改一处即可全站同步，提升维护效率。配置文件还充当文档，便于快速了解站点结构。功能开关通过统一配置实现原子性，避免遗漏。主题切换也因配置集中而更高效。尽管存在维护类型定义和过度抽象的风险，但作者认为只要值有多个使用点或未来可能变化，就值得抽离到配置层。最终通过代码扫描验证了硬编码字符串的消除。",{"type":9,"value":10,"toc":940},"minimark",[11,15,18,25,29,32,43,46,164,168,171,219,222,225,229,236,337,348,352,355,358,375,378,412,415,561,567,571,578,676,679,756,763,773,781,784,787,801,814,820,826,832,839,842,849,852,913,916,919,922,930,933,936],[12,13,14],"p",{},"「零硬编码」这句话很容易被当成洁癖。",[12,16,17],{},"毕竟写死一个站名能出什么问题？改的时候全局替换一下不就完了。",[12,19,20,21],{},"我做这个博客的时候，把这条规则当成了硬性架构约束。半年后的结论是：",[22,23,24],"strong",{},"它带来的收益远超预期，而且和代码整洁没什么关系。",[26,27,28],"h2",{"id":28},"四层配置",[12,30,31],{},"我把所有可变内容分成四层：",[33,34,39],"pre",{"className":35,"code":37,"language":38},[36],"language-text","app\u002Fblog.config.ts     站点身份、feed、search\napp\u002Ftheme.config.ts    设计 token（颜色、字号、间距、圆角、动效）\napp\u002Fpages.config.ts    逐路由的业务参数\napp\u002Fdata\u002F*.ts          友链、关于页元数据、系统文案\n","text",[40,41,37],"code",{"__ignoreMap":42},"",[12,44,45],{},"组件里一行硬编码都不许有。",[33,47,51],{"className":48,"code":49,"language":50,"meta":42,"style":42},"language-vue shiki shiki-themes github-light github-dark","\u003C!-- 禁止 -->\n\u003Ch1>喵洛阁\u003C\u002Fh1>\n\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fkemiaosw\">GitHub\u003C\u002Fa>\n\n\u003C!-- 允许 -->\n\u003Ch1>{{ site.name }}\u003C\u002Fh1>\n\u003Ca :href=\"social.url\">{{ social.label }}\u003C\u002Fa>\n","vue",[40,52,53,62,81,107,114,120,134],{"__ignoreMap":42},[54,55,58],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"sJ8bj","\u003C!-- 禁止 -->\n",[54,63,65,69,73,76,78],{"class":56,"line":64},2,[54,66,68],{"class":67},"sVt8B","\u003C",[54,70,72],{"class":71},"s9eBZ","h1",[54,74,75],{"class":67},">喵洛阁\u003C\u002F",[54,77,72],{"class":71},[54,79,80],{"class":67},">\n",[54,82,84,86,89,93,96,100,103,105],{"class":56,"line":83},3,[54,85,68],{"class":67},[54,87,88],{"class":71},"a",[54,90,92],{"class":91},"sScJk"," href",[54,94,95],{"class":67},"=",[54,97,99],{"class":98},"sZZnC","\"https:\u002F\u002Fgithub.com\u002Fkemiaosw\"",[54,101,102],{"class":67},">GitHub\u003C\u002F",[54,104,88],{"class":71},[54,106,80],{"class":67},[54,108,110],{"class":56,"line":109},4,[54,111,113],{"emptyLinePlaceholder":112},true,"\n",[54,115,117],{"class":56,"line":116},5,[54,118,119],{"class":60},"\u003C!-- 允许 -->\n",[54,121,123,125,127,130,132],{"class":56,"line":122},6,[54,124,68],{"class":67},[54,126,72],{"class":71},[54,128,129],{"class":67},">{{ site.name }}\u003C\u002F",[54,131,72],{"class":71},[54,133,80],{"class":67},[54,135,137,139,141,144,147,149,152,155,157,160,162],{"class":56,"line":136},7,[54,138,68],{"class":67},[54,140,88],{"class":71},[54,142,143],{"class":67}," :",[54,145,146],{"class":91},"href",[54,148,95],{"class":67},[54,150,151],{"class":98},"\"",[54,153,154],{"class":67},"social.url",[54,156,151],{"class":98},[54,158,159],{"class":67},">{{ social.label }}\u003C\u002F",[54,161,88],{"class":71},[54,163,80],{"class":67},[26,165,167],{"id":166},"收益一改一处全站同步","收益一：改一处，全站同步",[12,169,170],{},"最直接的收益是这个。举个真实例子：我想把「归档」改成「时间线」。",[33,172,176],{"className":173,"code":174,"language":175,"meta":42,"style":42},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F 改这一行\nnav: [\n  { label: '时间线', to: '\u002Farchive', icon: 'tabler:archive' },\n]\n","ts",[40,177,178,183,191,214],{"__ignoreMap":42},[54,179,180],{"class":56,"line":57},[54,181,182],{"class":60},"\u002F\u002F 改这一行\n",[54,184,185,188],{"class":56,"line":64},[54,186,187],{"class":91},"nav",[54,189,190],{"class":67},": [\n",[54,192,193,196,199,202,205,208,211],{"class":56,"line":83},[54,194,195],{"class":67},"  { label: ",[54,197,198],{"class":98},"'时间线'",[54,200,201],{"class":67},", to: ",[54,203,204],{"class":98},"'\u002Farchive'",[54,206,207],{"class":67},", icon: ",[54,209,210],{"class":98},"'tabler:archive'",[54,212,213],{"class":67}," },\n",[54,215,216],{"class":56,"line":109},[54,217,218],{"class":67},"]\n",[12,220,221],{},"改完，桌面导航、平板汉堡菜单、移动端底部 Tab 栏，三处同时更新。",[12,223,224],{},"如果这些 label 是硬编码在三处的，我至少要改三个文件，而且很可能漏掉移动端（因为它在另一个组件里，平时不怎么看）。",[26,226,228],{"id":227},"收益二配置即文档","收益二：配置即文档",[12,230,231,232,235],{},"打开 ",[40,233,234],{},"blog.config.ts","，你立刻知道这个站有哪些页面、有哪些社交链接、RSS 开没开。",[33,237,239],{"className":173,"code":238,"language":175,"meta":42,"style":42},"export default defineAppConfig({\n  site: {\n    name: '喵洛阁',\n    nav: [\n      { label: '首页', to: '\u002F', icon: 'tabler:home' },\n      { label: '归档', to: '\u002Farchive', icon: 'tabler:archive' },\n      \u002F\u002F ...\n    ],\n  },\n})\n",[40,240,241,256,261,272,277,297,314,319,325,331],{"__ignoreMap":42},[54,242,243,247,250,253],{"class":56,"line":57},[54,244,246],{"class":245},"szBVR","export",[54,248,249],{"class":245}," default",[54,251,252],{"class":91}," defineAppConfig",[54,254,255],{"class":67},"({\n",[54,257,258],{"class":56,"line":64},[54,259,260],{"class":67},"  site: {\n",[54,262,263,266,269],{"class":56,"line":83},[54,264,265],{"class":67},"    name: ",[54,267,268],{"class":98},"'喵洛阁'",[54,270,271],{"class":67},",\n",[54,273,274],{"class":56,"line":109},[54,275,276],{"class":67},"    nav: [\n",[54,278,279,282,285,287,290,292,295],{"class":56,"line":116},[54,280,281],{"class":67},"      { label: ",[54,283,284],{"class":98},"'首页'",[54,286,201],{"class":67},[54,288,289],{"class":98},"'\u002F'",[54,291,207],{"class":67},[54,293,294],{"class":98},"'tabler:home'",[54,296,213],{"class":67},[54,298,299,301,304,306,308,310,312],{"class":56,"line":122},[54,300,281],{"class":67},[54,302,303],{"class":98},"'归档'",[54,305,201],{"class":67},[54,307,204],{"class":98},[54,309,207],{"class":67},[54,311,210],{"class":98},[54,313,213],{"class":67},[54,315,316],{"class":56,"line":136},[54,317,318],{"class":60},"      \u002F\u002F ...\n",[54,320,322],{"class":56,"line":321},8,[54,323,324],{"class":67},"    ],\n",[54,326,328],{"class":56,"line":327},9,[54,329,330],{"class":67},"  },\n",[54,332,334],{"class":56,"line":333},10,[54,335,336],{"class":67},"})\n",[12,338,339,340,343,344,347],{},"这比翻 15 个 ",[40,341,342],{},".vue"," 文件去找 ",[40,345,346],{},"to=\"\u002Farchive\""," 快得多。",[26,349,351],{"id":350},"收益三功能开关变成真的开关","收益三：功能开关变成真的开关",[12,353,354],{},"这是我最没想到的收益。",[12,356,357],{},"提示词的验收标准里有一条：",[359,360,361],"blockquote",{},[12,362,363,366,367,370,371,374],{},[40,364,365],{},"feed"," \u002F ",[40,368,369],{},"search"," 任一 ",[40,372,373],{},"enabled: false"," 后：入口消失、对应路由 404、head 无相关注入",[12,376,377],{},"如果没有配置层，这个需求要改 6 个地方：",[379,380,381,385,395,400,406,409],"ol",{},[382,383,384],"li",{},"删掉 footer 的 RSS 链接",[382,386,387,388,391,392],{},"删掉 ",[40,389,390],{},"\u003Chead>"," 的 ",[40,393,394],{},"\u003Clink rel=\"alternate\">",[382,396,387,397],{},[40,398,399],{},"server\u002Froutes\u002Frss.xml.get.ts",[382,401,402,403],{},"改 ",[40,404,405],{},"nitro.prerender.routes",[382,407,408],{},"删掉搜索按钮",[382,410,411],{},"解绑快捷键",[12,413,414],{},"有了配置层，它们全都读同一个字段：",[33,416,418],{"className":173,"code":417,"language":175,"meta":42,"style":42},"const { feed, search } = useSiteConfig()\n\n\u002F\u002F footer\n\u003CNuxtLink v-if=\"feed.rss.enabled\" :to=\"feed.rss.path\">RSS\u003C\u002FNuxtLink>\n\n\u002F\u002F head\nif (feed.rss.enabled) links.push({ rel: 'alternate', ... })\n\n\u002F\u002F 路由\nif (!feed.rss.enabled) throw createError({ statusCode: 404 })\n",[40,419,420,447,451,456,491,495,500,525,529,534],{"__ignoreMap":42},[54,421,422,425,428,431,434,436,439,441,444],{"class":56,"line":57},[54,423,424],{"class":245},"const",[54,426,427],{"class":67}," { ",[54,429,365],{"class":430},"sj4cs",[54,432,433],{"class":67},", ",[54,435,369],{"class":430},[54,437,438],{"class":67}," } ",[54,440,95],{"class":245},[54,442,443],{"class":91}," useSiteConfig",[54,445,446],{"class":67},"()\n",[54,448,449],{"class":56,"line":64},[54,450,113],{"emptyLinePlaceholder":112},[54,452,453],{"class":56,"line":83},[54,454,455],{"class":60},"\u002F\u002F footer\n",[54,457,458,460,463,466,469,472,474,477,480,483,486,489],{"class":56,"line":109},[54,459,68],{"class":245},[54,461,462],{"class":67},"NuxtLink v",[54,464,465],{"class":245},"-if=",[54,467,468],{"class":98},"\"feed.rss.enabled\"",[54,470,471],{"class":67}," :to",[54,473,95],{"class":245},[54,475,476],{"class":98},"\"feed.rss.path\"",[54,478,479],{"class":245},">",[54,481,482],{"class":430},"RSS",[54,484,485],{"class":245},"\u003C\u002F",[54,487,488],{"class":67},"NuxtLink",[54,490,80],{"class":245},[54,492,493],{"class":56,"line":116},[54,494,113],{"emptyLinePlaceholder":112},[54,496,497],{"class":56,"line":122},[54,498,499],{"class":60},"\u002F\u002F head\n",[54,501,502,505,508,511,514,517,519,522],{"class":56,"line":136},[54,503,504],{"class":245},"if",[54,506,507],{"class":67}," (feed.rss.enabled) links.",[54,509,510],{"class":91},"push",[54,512,513],{"class":67},"({ rel: ",[54,515,516],{"class":98},"'alternate'",[54,518,433],{"class":67},[54,520,521],{"class":245},"...",[54,523,524],{"class":67}," })\n",[54,526,527],{"class":56,"line":321},[54,528,113],{"emptyLinePlaceholder":112},[54,530,531],{"class":56,"line":327},[54,532,533],{"class":60},"\u002F\u002F 路由\n",[54,535,536,538,541,544,547,550,553,556,559],{"class":56,"line":333},[54,537,504],{"class":245},[54,539,540],{"class":67}," (",[54,542,543],{"class":245},"!",[54,545,546],{"class":67},"feed.rss.enabled) ",[54,548,549],{"class":245},"throw",[54,551,552],{"class":91}," createError",[54,554,555],{"class":67},"({ statusCode: ",[54,557,558],{"class":430},"404",[54,560,524],{"class":67},[12,562,563,566],{},[22,564,565],{},"开关的原子性","从\"靠人记得改全\"变成了\"结构上不可能不同步\"。",[26,568,570],{"id":569},"收益四主题切换不用碰组件","收益四：主题切换不用碰组件",[12,572,573,574,577],{},"设计 token 全在 ",[40,575,576],{},"theme.config.ts","：",[33,579,581],{"className":173,"code":580,"language":175,"meta":42,"style":42},"theme: {\n  accent: { light: '#a94f33', dark: '#cc785c' },\n  radius: '0.5rem',\n  contentMaxWidth: '764px',\n  animation: { duration: 200, easing: 'ease-out' },\n}\n",[40,582,583,591,620,632,644,671],{"__ignoreMap":42},[54,584,585,588],{"class":56,"line":57},[54,586,587],{"class":91},"theme",[54,589,590],{"class":67},": {\n",[54,592,593,596,599,602,605,608,610,613,615,618],{"class":56,"line":64},[54,594,595],{"class":91},"  accent",[54,597,598],{"class":67},": { ",[54,600,601],{"class":91},"light",[54,603,604],{"class":67},": ",[54,606,607],{"class":98},"'#a94f33'",[54,609,433],{"class":67},[54,611,612],{"class":91},"dark",[54,614,604],{"class":67},[54,616,617],{"class":98},"'#cc785c'",[54,619,213],{"class":67},[54,621,622,625,627,630],{"class":56,"line":83},[54,623,624],{"class":91},"  radius",[54,626,604],{"class":67},[54,628,629],{"class":98},"'0.5rem'",[54,631,271],{"class":67},[54,633,634,637,639,642],{"class":56,"line":109},[54,635,636],{"class":91},"  contentMaxWidth",[54,638,604],{"class":67},[54,640,641],{"class":98},"'764px'",[54,643,271],{"class":67},[54,645,646,649,651,654,656,659,661,664,666,669],{"class":56,"line":116},[54,647,648],{"class":91},"  animation",[54,650,598],{"class":67},[54,652,653],{"class":91},"duration",[54,655,604],{"class":67},[54,657,658],{"class":430},"200",[54,660,433],{"class":67},[54,662,663],{"class":91},"easing",[54,665,604],{"class":67},[54,667,668],{"class":98},"'ease-out'",[54,670,213],{"class":67},[54,672,673],{"class":56,"line":122},[54,674,675],{"class":67},"}\n",[12,677,678],{},"这些值在构建期被编译成 CSS 变量：",[33,680,684],{"className":681,"code":682,"language":683,"meta":42,"style":42},"language-css shiki shiki-themes github-light github-dark",":root {\n  --accent: #a94f33;\n  --radius: 0.5rem;\n  --content-max-width: 764px;\n  --duration-base: 200ms;\n}\n","css",[40,685,686,694,708,723,738,752],{"__ignoreMap":42},[54,687,688,691],{"class":56,"line":57},[54,689,690],{"class":91},":root",[54,692,693],{"class":67}," {\n",[54,695,696,700,702,705],{"class":56,"line":64},[54,697,699],{"class":698},"s4XuR","  --accent",[54,701,604],{"class":67},[54,703,704],{"class":430},"#a94f33",[54,706,707],{"class":67},";\n",[54,709,710,713,715,718,721],{"class":56,"line":83},[54,711,712],{"class":698},"  --radius",[54,714,604],{"class":67},[54,716,717],{"class":430},"0.5",[54,719,720],{"class":245},"rem",[54,722,707],{"class":67},[54,724,725,728,730,733,736],{"class":56,"line":109},[54,726,727],{"class":698},"  --content-max-width",[54,729,604],{"class":67},[54,731,732],{"class":430},"764",[54,734,735],{"class":245},"px",[54,737,707],{"class":67},[54,739,740,743,745,747,750],{"class":56,"line":116},[54,741,742],{"class":698},"  --duration-base",[54,744,604],{"class":67},[54,746,658],{"class":430},[54,748,749],{"class":245},"ms",[54,751,707],{"class":67},[54,753,754],{"class":56,"line":122},[54,755,675],{"class":67},[12,757,758,759,762],{},"组件里只写 ",[40,760,761],{},"var(--accent)","，不写 hex。",[12,764,765,766,769,770,772],{},"想换强调色？改两行。想换字体？改 ",[40,767,768],{},"fontFamily","。想让动画慢一点？改 ",[40,771,653],{},"。",[12,774,775],{},[22,776,777,778,780],{},"不需要在 30 个 ",[40,779,342],{}," 文件里搜索替换。",[26,782,783],{"id":783},"代价是什么",[12,785,786],{},"诚实地说，这套架构有成本：",[12,788,789,792,793,796,797,800],{},[22,790,791],{},"一、多一层间接。"," 看 ",[40,794,795],{},"{{ site.name }}"," 不如看 ",[40,798,799],{},"喵洛阁"," 直观。新人接手要先去读配置文件。",[12,802,803,806,807,810,811,813],{},[22,804,805],{},"二、类型定义要维护。"," ",[40,808,809],{},"useSiteConfig()"," 里的 interface 得和 ",[40,812,234],{}," 保持同步。",[12,815,816,819],{},[22,817,818],{},"三、过度抽象的风险。"," 如果某个值只在一个组件里用一次，且永远不会变，那抽到配置层就是浪费。",[12,821,822,823],{},"我的判断标准是：",[22,824,825],{},"如果一个值有 ≥ 2 个使用点，或者有「未来可能改」的可能，就抽出来。否则就地写死。",[12,827,828,829,772],{},"比如「回到首页」这个按钮的文案——它在 404 页和错误页都用，所以放进了 ",[40,830,831],{},"app\u002Fdata\u002Fsystem.ts",[12,833,834,835,838],{},"而 ",[40,836,837],{},"aria-hidden=\"true\""," 这种纯技术属性，就地写，不进配置。",[26,840,841],{"id":841},"怎么验证没漏",[12,843,844,845,848],{},"验收标准里有一条：",[40,846,847],{},"grep -r \"硬编码字符串\" app\u002Fcomponents app\u002Fpages"," 命中为 0。",[12,850,851],{},"我实际跑的命令是：",[33,853,857],{"className":854,"code":855,"language":856,"meta":42,"style":42},"language-bash shiki shiki-themes github-light github-dark","# 找中文字符串（排除注释和 CSS）\ngrep -rn '[\\u4e00-\\u9fa5]' app\u002Fcomponents app\u002Fpages \\\n  --include='*.vue' | grep -v '^\\s*\\*' | grep -v '\u003C!--'\n","bash",[40,858,859,864,884],{"__ignoreMap":42},[54,860,861],{"class":56,"line":57},[54,862,863],{"class":60},"# 找中文字符串（排除注释和 CSS）\n",[54,865,866,869,872,875,878,881],{"class":56,"line":64},[54,867,868],{"class":91},"grep",[54,870,871],{"class":430}," -rn",[54,873,874],{"class":98}," '[\\u4e00-\\u9fa5]'",[54,876,877],{"class":98}," app\u002Fcomponents",[54,879,880],{"class":98}," app\u002Fpages",[54,882,883],{"class":430}," \\\n",[54,885,886,889,892,895,898,901,904,906,908,910],{"class":56,"line":83},[54,887,888],{"class":430},"  --include=",[54,890,891],{"class":98},"'*.vue'",[54,893,894],{"class":245}," |",[54,896,897],{"class":91}," grep",[54,899,900],{"class":430}," -v",[54,902,903],{"class":98}," '^\\s*\\*'",[54,905,894],{"class":245},[54,907,897],{"class":91},[54,909,900],{"class":430},[54,911,912],{"class":98}," '\u003C!--'\n",[12,914,915],{},"第一次跑出来 47 处，其中 41 处是真硬编码，6 处是误报（CSS 注释里的说明文字）。",[12,917,918],{},"改完之后是 0。",[26,920,921],{"id":921},"结论",[359,923,924],{},[12,925,926,927,772],{},"零硬编码不是为了\"代码漂亮\"，是为了",[22,928,929],{},"让配置变更具备原子性",[12,931,932],{},"一个改了会漏的地方，早晚会漏。一个结构上不可能漏的地方，永远不漏。",[12,934,935],{},"这就是架构和整洁的区别。",[937,938,939],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#57606A;--shiki-dark:#9AA4B0}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":42,"searchDepth":64,"depth":64,"links":941},[942,943,944,945,946,947,948,949],{"id":28,"depth":64,"text":28},{"id":166,"depth":64,"text":167},{"id":227,"depth":64,"text":228},{"id":350,"depth":64,"text":351},{"id":569,"depth":64,"text":570},{"id":783,"depth":64,"text":783},{"id":841,"depth":64,"text":841},{"id":921,"depth":64,"text":921},"工程",null,"2025-07-10","零硬编码听起来是教条，实践起来是架构约束。这篇讲我怎么用四个配置文件把全站的可变内容抽干净，以及它带来的真实收益。",false,"md",{},"\u002Fposts\u002F71970a13",{"title":5,"description":953},"posts\u002Fzero-hardcode",[961,962,963],"架构","配置","可维护性","magDlTnov3cN_j4NsET9iwx0qNY5RxzO_8YtMWXMHYU",{"prev":966,"next":975},{"path":967,"title":968,"description":969,"date":970,"updated":951,"tags":971,"category":950,"cover":974,"draft":954,"pinned":112,"featured":954},"\u002Fposts\u002F57060fdd","特殊字符与代码块压测：& \u003C > \" ' 全都要","一篇专门用来喂 RSS\u002FAtom 校验器的文章。正文里塞满了 & \u003C > 引号、CDATA 边界、嵌套代码块和原始 HTML 片段。","2025-10-20",[482,972,973],"XML","测试","\u002Fimages\u002Fcovers\u002Fcharset.png",{"path":976,"title":977,"description":978,"date":979,"updated":951,"tags":980,"category":983,"cover":951,"draft":954,"pinned":954,"featured":954},"\u002Fposts\u002F4fd42b50","慢下来之后我才开始写得下去","追求\"高效写作\"的那几年，我一篇文章都没写完。放弃效率之后，反而写了几十篇。","2025-06-18",[981,982],"写作","反思","随笔",[985,992,1000],{"path":986,"title":987,"description":988,"date":989,"updated":951,"tags":990,"category":950,"cover":951,"draft":954,"pinned":954,"featured":954},"\u002Fposts\u002F63afeff4","我删掉了站点的 90% 功能","一个博客需要什么？列表、文章、归档、搜索、订阅。其他东西我全删了，然后发现加载时间从 2.3 秒掉到了 380 毫秒。","2025-10-12",[991,961,982],"性能",{"path":993,"title":994,"description":995,"date":996,"updated":951,"tags":997,"category":950,"cover":951,"draft":954,"pinned":954,"featured":954},"\u002Fposts\u002Fff906e7f","静态博客的搜索为什么不用后端","300KB 的索引、一次懒加载、纯前端查询。对一个几百篇文章的博客来说，这个方案比任何搜索引擎都合适。","2025-09-05",[998,961,999],"搜索","SSG",{"path":1001,"title":1002,"description":1003,"date":1004,"updated":951,"tags":1005,"category":950,"cover":1007,"draft":954,"pinned":954,"featured":112},"\u002Fposts\u002Ff50285d4","测试文章甲：带封面的推荐位文章","这是首页改版验收用的测试文章甲，有封面且标记为 featured，应出现在顶部轮播第一位。","2026-11-01",[973,1006],"首页改版","\u002Fimages\u002Fcovers\u002Fview-transition.png",1794495071248]