[{"data":1,"prerenderedAt":6905},["ShallowReactive",2],{"site-settings":3,"writing-\u002Fwriting\u002F2026-07-26-quilibra-complete-technical-handbook":103,"writing-navigation":3592},{"site":4,"home":19,"writing":41,"services":49,"about":84},{"id":5,"title":6,"description":7,"extension":8,"footer":9,"location":13,"meta":14,"name":6,"stem":15,"theme":16,"__hash__":18},"site\u002Fsettings\u002Fsite.yml","Quilibra","这是我自己部署的小主页，用于记录想法并提供一些服务的入口。","yml",{"icp":10,"policeRecord":11,"credit":12},"皖ICP备2026023522号-1","皖公网安备34012102000966号","DESIGNED & BUILT BY GPT-5.6 IN CODEX","China · UTC+8",{},"settings\u002Fsite",{"defaultAccent":17},"#000000","jtRrbdN47gDyGs_u2ubGZhFFByRvoiraZTjokx9SF0A",{"id":20,"dock":21,"extension":8,"headline":25,"introduction":28,"kicker":29,"latestWriting":30,"meta":36,"modules":37,"stem":38,"systemsLabel":39,"__hash__":40},"homeSettings\u002Fsettings\u002Fhome.yml",{"writing":22,"services":23,"about":24},"文章","服务","关于",{"prefix":26,"emphasis":27},"你好，这里是","Equilibra","这是我自己部署的个人主页，用于记录我的一些事情并维护我的一些服务。","个人主页 \u002F 持续施工中",{"enabled":31,"title":32,"linkLabel":33,"emptyLabel":34,"limit":35},true,"最近写下","全部文章","暂无文章",3,{},{"services":31,"rgbAssembly":31},"settings\u002Fhome","SYSTEMS","mLQiZfULI1ARi0lqmzbNtwFTadrRDtSbuzkF2UyyPzU",{"id":42,"emptyLabel":43,"extension":8,"intro":44,"meta":45,"seoDescription":46,"stem":47,"__hash__":48},"writingSettings\u002Fsettings\u002Fwriting.yml","没有找到匹配的文章。","随笔、学习、交易，以及一些观察。",{},"随笔、学习、交易与观察。","settings\u002Fwriting","YEMHVxKRvAlcMP_kI5F46TdJR19p9ErI69cG5BbP_j8",{"id":50,"accessNotes":51,"accessTitle":54,"extension":8,"intro":55,"items":56,"meta":80,"seoDescription":81,"stem":82,"__hash__":83},"servicesSettings\u002Fsettings\u002Fservices.yml",[52,53],"公开入口由 VPS 上的 Nginx 提供 HTTPS 与反向代理。","家中服务通过私网连接，不直接暴露 。","访问说明","统一入口，分别运行。尚未开放的服务会在域名和登录保护完成后上线。",[57,63,69,75],{"name":58,"description":59,"href":60,"state":61,"icon":62,"showOnHome":31},"Code","浏览器里的开发环境","https:\u002F\u002Fcode.quilibra.cn","online","code",{"name":64,"description":65,"href":66,"state":67,"icon":68,"showOnHome":31},"Photos","照片备份和浏览","https:\u002F\u002Fphotos.example.com","soon","image",{"name":70,"description":71,"href":72,"state":73,"icon":74,"showOnHome":31},"NAS","文件与存储管理","https:\u002F\u002Fnas.example.com","private","hard-drive",{"name":76,"description":77,"href":78,"state":61,"icon":79,"showOnHome":31},"Status","服务可用性与故障记录","#","activity",{},"开发环境、相册、NAS 和运行状态。","settings\u002Fservices","yLhlQNGotM4SYxIDWlMMScw4erJoBdsGRFf_uffFabk",{"id":85,"contactCopy":86,"contactLabel":87,"currentFocus":88,"extension":8,"focusLabel":89,"introduction":90,"meta":91,"seoDescription":92,"stem":93,"topicsLabel":94,"writingTopics":95,"__hash__":102},"aboutSettings\u002Fsettings\u002Fabout.yml","不是，你联系我干嘛？","联系","正在上班当牛马。","正在做的事","这是我自己部署的个人主页，用于记录我的想法并维护我的一些服务。",{},"一些基本信息，以及这个网站目前用来做什么。","settings\u002Fabout","关注的主题",[96,97,98,99,100,101],"看盘看线","捣鼓电脑","折腾NAS","踢踢野球","看看科普","玩币练胆","ZVRx6CC1-GDQUGPl4C4XUfgy30ovFv6LdaXNegQjWAo",{"id":104,"title":105,"body":106,"category":3574,"date":3575,"description":3576,"extension":3577,"hidden":3578,"meta":3579,"navigation":31,"path":3580,"publishedAt":3581,"seo":3582,"slug":3583,"stem":3584,"tags":3585,"updated":3590,"__hash__":3591},"writing\u002Fwriting\u002F2026-07-26-quilibra-complete-technical-handbook.md","Quilibra 个人主页完整技术手册：从页面架构到内容发布与生产部署",{"type":107,"value":108,"toc":3502},"minimark",[109,113,116,137,142,145,148,158,174,189,192,196,199,221,224,227,231,234,334,337,343,349,363,367,370,376,379,413,428,432,438,472,490,501,507,553,560,564,567,631,650,657,692,705,709,715,747,765,807,810,821,825,828,851,870,889,892,896,899,927,930,934,941,999,1010,1013,1040,1043,1049,1055,1059,1070,1076,1083,1093,1116,1120,1130,1133,1153,1156,1196,1202,1206,1217,1220,1223,1237,1258,1264,1288,1299,1303,1314,1317,1344,1355,1358,1373,1377,1380,1396,1399,1405,1419,1426,1444,1448,1458,1465,1468,1482,1496,1503,1507,1518,1524,1532,1538,1545,1549,1552,1638,1641,1647,1674,1697,1710,1720,1724,1733,1736,1758,1761,1769,1775,1781,1788,1795,1809,1813,1820,1823,1846,1865,1868,1872,1879,1885,1894,1907,1911,1914,1921,1924,1930,1933,1939,1942,1948,1954,1966,1998,2005,2011,2036,2039,2044,2053,2058,2070,2090,2094,2100,2106,2119,2136,2145,2164,2167,2173,2177,2213,2278,2284,2287,2293,2304,2315,2319,2370,2381,2387,2391,2394,2400,2403,2445,2465,2476,2487,2491,2494,2498,2504,2535,2539,2545,2579,2583,2589,2627,2630,2633,2637,2640,2657,2664,2667,2670,2711,2716,2720,2723,2730,2747,2750,2754,2758,2767,2770,2779,2782,2791,2794,2808,2811,2824,2827,2834,2837,2851,2854,2860,2864,2868,2887,2890,2893,2920,2925,2929,3009,3018,3021,3111,3114,3117,3145,3156,3160,3164,3187,3190,3206,3210,3221,3224,3232,3235,3241,3245,3255,3259,3268,3278,3282,3288,3292,3295,3301,3304,3310,3320,3324,3327,3330,3333,3336,3339,3345,3348,3352,3355,3409,3412,3432,3435,3438,3498],[110,111,112],"p",{},"这是一份给自己看的项目说明书。它主要以 2026 年 7 月 27 日的实际代码和 VPS 配置为准，从浏览器里看到的页面一直追到内容文件、Git 分支、构建产物、静态服务器和版本化 release。以后忘了某段代码为什么存在、主页颜色为什么能保存、CMS 保存为什么只出现在测试站，或者 3000、3100、3101 三个端口分别在做什么，可以从这里重新建立完整认识。",[110,114,115],{},"本文记录的是一个具体版本的实现，不是一套永远不变的规范。最可靠的事实来源始终是仓库中的代码；本文的作用，是把分散在 Vue、CSS、YAML、脚本和运维文件里的设计意图串起来。",[117,118,119],"blockquote",{},[110,120,121,122,125,126,129,130,133,134,136],{},"2026 年 8 月 3 日更新：网站已经移除长期 ",[62,123,124],{},"staging"," 分支、远程测试站和 ",[62,127,128],{},"promote"," 流程，改为 CMS 直接提交 ",[62,131,132],{},"master","、代码在本地功能分支验证后推送 ",[62,135,132],{},"。下文关于双环境发布的内容保留为历史架构记录，当前操作以仓库 README 和运维手册为准。",[138,139,141],"h2",{"id":140},"一先用一句话理解整个网站","一、先用一句话理解整个网站",[110,143,144],{},"Quilibra（名字也是AI起的捏） 是一个由 Nuxt 生成的静态个人网站：页面结构写在 Vue 中，主页设置写在 YAML 中，文章写在 Markdown 中；发布时把这些源文件编译成普通 HTML、CSS、JavaScript 和图片，再由一个很小的 Node 静态服务器提供给浏览器。",[110,146,147],{},"它的静态生成核心是：",[149,150,156],"pre",{"className":151,"code":153,"language":154,"meta":155},[152],"language-text","Vue \u002F CSS \u002F YAML \u002F Markdown\n           |\n           | Nuxt generate\n           v\n      .output\u002Fpublic\n           |\n           | 复制到带时间戳的 release\n           v\n  releases\u002F\u003Crelease-id>\n           |\n           | 原子切换 current 符号链接\n           v\n     current -> 当前 release\n           |\n           | Node 静态服务器 :3000\n           v\n        浏览器 \u002F 反向代理\n","text","",[62,157,153],{"__ignoreMap":155},[110,159,160,161,163,164,167,168,163,170,173],{},"现在这套链路有两份互不覆盖的实例：",[62,162,132],{}," 构建到 ",[62,165,166],{},"~\u002F.local\u002Fshare\u002Fpersonal-site\u002Fcurrent"," 并由 3000 提供正式站，",[62,169,124],{},[62,171,172],{},"~\u002F.local\u002Fshare\u002Fpersonal-site-staging\u002Fcurrent"," 并由 3100 提供测试站。两边都使用相同的生成和原子切换逻辑，但仓库、成功状态、release 目录和服务端口完全独立。",[110,175,176,177],{},"这里最重要的边界是：",[178,179,180,181,184,185,188],"strong",{},"Git 分支中的源码是长期事实，",[62,182,183],{},".output\u002Fpublic"," 是可重新生成的临时产物，各环境 ",[62,186,187],{},"current"," 指向的 release 才是该环境当前真正提供的版本。",[110,190,191],{},"因此，修改 Markdown 或 YAML 只改变源码；只有重新生成并切换 release，线上页面才会改变。网站没有生产数据库，也没有在访问文章时临时渲染 Markdown。",[138,193,195],{"id":194},"二为什么选择静态优先","二、为什么选择静态优先",[110,197,198],{},"个人主页的读取远多于写入，而且文章、简介和服务入口都不需要按访客实时计算。静态生成正好适合这种负载：",[200,201,202,206,209,212,215,218],"ul",{},[203,204,205],"li",{},"访问时不查询数据库，也不运行 Vue 服务端渲染逻辑；",[203,207,208],{},"HTML 已经在发布阶段生成，首屏可以直接返回完整内容；",[203,210,211],{},"生产服务只需要读取文件，故障面比常驻 Nuxt 服务小；",[203,213,214],{},"每个 release 都是一份独立、完整、可回滚的目录；",[203,216,217],{},"Markdown 和 YAML 都在 Git 中，内容、配置和代码可以一起审查与恢复；",[203,219,220],{},"CMS 只是源码编辑器，不会成为网站运行时依赖。",[110,222,223],{},"代价也很明确：每次内容变化都要重新生成站点；文章越多，生成时间越长；需要登录、评论、实时状态或用户数据时，必须接入独立服务，而不能假装静态文件可以承担动态业务。",[110,225,226],{},"当前项目主动接受这个取舍，因为稳定、透明和容易备份比“保存后数据库立刻生效”更重要。",[138,228,230],{"id":229},"三技术栈与各自职责","三、技术栈与各自职责",[110,232,233],{},"当前主要依赖如下：",[235,236,237,250],"table",{},[238,239,240],"thead",{},[241,242,243,247],"tr",{},[244,245,246],"th",{},"技术",[244,248,249],{},"当前职责",[251,252,253,262,270,278,286,294,302,310,318,326],"tbody",{},[241,254,255,259],{},[256,257,258],"td",{},"Nuxt 4",[256,260,261],{},"应用框架、路由、预渲染、页面头信息和构建产物",[241,263,264,267],{},[256,265,266],{},"Vue 3",[256,268,269],{},"组件、响应式状态、生命周期和浏览器交互",[241,271,272,275],{},[256,273,274],{},"TypeScript",[256,276,277],{},"页面、组件、配置、内容脚本和测试的静态类型检查",[241,279,280,283],{},[256,281,282],{},"Nuxt Content 3",[256,284,285],{},"读取 YAML\u002FMarkdown、建立集合、查询文章并渲染正文",[241,287,288,291],{},[256,289,290],{},"Zod",[256,292,293],{},"约束站点设置与文章字段",[241,295,296,299],{},[256,297,298],{},"Lucide Vue",[256,300,301],{},"返回、搜索、服务、调色等界面图标",[241,303,304,307],{},[256,305,306],{},"原生 CSS",[256,308,309],{},"全部视觉、响应式布局、悬浮反馈和字标动画",[241,311,312,315],{},[256,313,314],{},"Sveltia CMS",[256,316,317],{},"在浏览器中编辑仓库里的 YAML、Markdown 和上传资源",[241,319,320,323],{},[256,321,322],{},"Playwright",[256,324,325],{},"真实浏览器中的交互、布局、响应式和回归测试",[241,327,328,331],{},[256,329,330],{},"Node.js",[256,332,333],{},"内容工具、同步与发布脚本、生产和测试静态服务器",[110,335,336],{},"没有引入 Tailwind、组件库、动画框架、运行时数据库或外部字体。页面使用系统无衬线字体和系统等宽字体回退，这减少了字体请求，也避免了字体加载完成时重新排版。",[110,338,339,342],{},[62,340,341],{},"package.json"," 中最常用的入口是：",[149,344,347],{"className":345,"code":346,"language":154,"meta":155},[152],"npm run dev              本地临时 Nuxt 开发服务\nnpm run generate         生成静态站点\nnpm run typecheck        Nuxt\u002FVue TypeScript 检查\nnpm run content:validate 校验全部文章与站点设置\nnpm run test:content     内容校验器与受限 writer 的单元测试\nnpm run test:release     release 工具的单元测试\nnpm run test:e2e         Playwright 浏览器测试\nnpm run publish          发布未提交的受管内容\nnpm run deploy           部署已经提交且工作区干净的代码版本\nnpm run sync             拉取指定 GitHub 分支并自动检查、构建和部署\nnpm run promote          把测试站确认过的 CMS 内容安全发布到生产\nnpm run rollback         切换回指定历史 release\n",[62,348,346],{"__ignoreMap":155},[110,350,351,354,355,358,359,362],{},[62,352,353],{},"build"," 与 ",[62,356,357],{},"preview"," 仍是 Nuxt 的常规命令，但当前生产链路以 ",[62,360,361],{},"generate"," 和自有静态服务器为准。",[138,364,366],{"id":365},"四目录结构与所有权边界","四、目录结构与所有权边界",[110,368,369],{},"理解这个项目，先要知道“改什么就去哪里”。",[149,371,374],{"className":372,"code":373,"language":154,"meta":155},[152],"personal-site\u002F\n├── app\u002F\n│   ├── app.vue                         全站外壳、主题初始化、动态 favicon\n│   ├── assets\u002Fcss\u002Fmain.css             全站视觉与响应式规则\n│   ├── components\u002F\n│   │   ├── ArtisticWordmark.vue        可编辑艺术字与重播控制\n│   │   ├── RgbAssembly.vue             RGB 滑杆、预设与重置\n│   │   ├── ServiceIcon.vue             服务图标名称到 Lucide 的映射\n│   │   └── ServiceStatusDot.vue        服务状态点\n│   ├── composables\u002F\n│   │   ├── useAccentTheme.ts           主题色状态、混色、对比色与持久化\n│   │   ├── useSiteSettings.ts          并行读取并组合五份站点设置\n│   │   └── useWordmarkAnimation.ts     跨组件字标重播信号\n│   ├── config\u002F                         文章分类、服务状态和图标枚举\n│   ├── pages\u002F                          首页、文章、服务、关于页面\n│   └── error.vue                       错误页面\n├── content\u002F\n│   ├── settings\u002F                       全站与四个页面的独立 YAML 配置\n│   └── writing\u002F*.md                    正式文章\n├── public\u002F\n│   ├── admin\u002F                          Sveltia CMS 入口与字段配置\n│   ├── brand\u002F                          字标位图资源\n│   ├── uploads\u002F                        文章上传资源\n│   └── favicon.svg                     静态 favicon 回退\n├── scripts\u002F\n│   ├── content\u002Fvalidate.ts             内容结构校验\n│   ├── content\u002Fwrite.ts                受限文章写入、更新与删除\n│   ├── release.ts                      测试、生成、提交、发布和回滚\n│   ├── promote.ts                      staging 内容安全合并到 master\n│   ├── sync.ts                         GitHub 拉取、代理、构建与部署\n│   ├── webhook.ts                      GitHub 签名校验、事件队列与部署触发\n│   └── static-server.mjs               生产与测试静态文件服务器\n├── tests\u002F                              单元、发布和浏览器测试\n├── ops\u002F\n│   ├── openclaw\u002F...\u002FSKILL.md           OpenClaw 文章发布 Skill\n│   ├── nginx\u002F                           正式域名、测试域名和默认拒绝站点\n│   └── systemd\u002F                         静态、Webhook 与同步服务\n├── content.config.ts                   Nuxt Content 集合\n├── content.schema.ts                   站点设置 Schema\n├── nuxt.config.ts                      Nuxt 与静态预渲染配置\n└── playwright.config.ts                E2E 临时服务与浏览器配置\n",[62,375,373],{"__ignoreMap":155},[110,377,378],{},"可以把这些文件分成四层：",[380,381,382,388,394,403],"ol",{},[203,383,384,387],{},[62,385,386],{},"content\u002F"," 是可频繁修改的数据层。",[203,389,390,393],{},[62,391,392],{},"app\u002F"," 是页面表现和交互层。",[203,395,396,354,399,402],{},[62,397,398],{},"scripts\u002F",[62,400,401],{},"ops\u002F"," 是发布和运行层。",[203,404,405,408,409,412],{},[62,406,407],{},".output\u002F","、",[62,410,411],{},".nuxt*"," 是构建缓存或产物，不是手工维护的源码。",[110,414,415,416,419,420,423,424,427],{},"CMS 默认只管理 ",[62,417,418],{},"content\u002Fsettings\u002F"," 中五份固定 YAML、",[62,421,422],{},"content\u002Fwriting\u002F"," 和 ",[62,425,426],{},"public\u002Fuploads\u002F","。Vue、CSS、CMS 配置、脚本或测试发生变化时，应当视为代码变更，走完整代码部署流程；设置目录中的未知文件也不会自动进入内容发布白名单。",[138,429,431],{"id":430},"五nuxt-配置与构建数据库","五、Nuxt 配置与构建数据库",[110,433,434,437],{},[62,435,436],{},"nuxt.config.ts"," 做了几件关键的事：",[200,439,440,447,453,456,459,466,469],{},[203,441,442,443,446],{},"加载 ",[62,444,445],{},"@nuxt\u002Fcontent","；",[203,448,449,450,446],{},"全局引入 ",[62,451,452],{},"app\u002Fassets\u002Fcss\u002Fmain.css",[203,454,455],{},"关闭开发工具；",[203,457,458],{},"设置中文页面语言和移动端 viewport；",[203,460,461,462,465],{},"使用 ",[62,463,464],{},"out-in"," 页面切换过渡；",[203,467,468],{},"允许 Nitro 爬取内部链接并预渲染，遇到错误时直接让构建失败；",[203,470,471],{},"把 Nuxt Content 的本地数据库放到与构建目录对应的位置。",[110,473,474,475,423,478,481,482,485,486,489],{},"Nuxt Content 3 在构建过程中会建立 SQLite 内容索引。开发环境默认使用 ",[62,476,477],{},".nuxt",[62,479,480],{},".data\u002Fcontent\u002Fcontents.sqlite","；release 构建传入 ",[62,483,484],{},"NUXT_BUILD_DIR=.nuxt-release","，E2E 使用 ",[62,487,488],{},".nuxt-e2e","。把三种构建目录分开，是为了避免开发服务器、正式生成和 Playwright 同时读写同一个内容缓存。",[110,491,492,493,496,497,500],{},"这里的 SQLite 是",[178,494,495],{},"构建时索引","，不是生产文章数据库。它帮助 Nuxt Content 查询 Markdown；静态页面生成完成后，生产服务器只负责输出构建结果。遇到 ",[62,498,499],{},"no such table: _content_writing"," 时，通常是本地 Nuxt Content 缓存被并发构建或中断破坏，不代表文章源文件丢失。",[110,502,503,506],{},[62,504,505],{},"content.config.ts"," 定义了六个集合：",[200,508,509,531],{},[203,510,511,408,514,408,517,408,520,408,523,526,527,530],{},[62,512,513],{},"site",[62,515,516],{},"homeSettings",[62,518,519],{},"writingSettings",[62,521,522],{},"servicesSettings",[62,524,525],{},"aboutSettings","：五个 ",[62,528,529],{},"data"," 集合，各自读取同名职责的 YAML；",[203,532,533,536,537,540,541,544,545,548,549,552],{},[62,534,535],{},"writing","：",[62,538,539],{},"page"," 类型，读取 ",[62,542,543],{},"writing\u002F**\u002F*.md","，因此每篇文章会拥有可路由的 ",[62,546,547],{},"path"," 和可供 ",[62,550,551],{},"ContentRenderer"," 使用的正文。",[110,554,555,556,559],{},"E2E 测试通过 ",[62,557,558],{},"NUXT_CONTENT_CWD=tests\u002Ffixtures\u002Fcontent"," 把文章集合切换到固定 fixture，测试不会依赖真实文章的标题和数量，也不会改动生产内容。",[138,561,563],{"id":562},"六站点设置五份-yaml-如何保持页面边界","六、站点设置：五份 YAML 如何保持页面边界",[110,565,566],{},"站点设置按后台入口拆成五份文件：",[235,568,569,579],{},[238,570,571],{},[241,572,573,576],{},[244,574,575],{},"文件",[244,577,578],{},"管理内容",[251,580,581,591,601,611,621],{},[241,582,583,588],{},[256,584,585],{},[62,586,587],{},"site.yml",[256,589,590],{},"站点名称、浏览器标题、SEO、位置、默认主题色、备案和署名",[241,592,593,598],{},[256,594,595],{},[62,596,597],{},"home.yml",[256,599,600],{},"首页介绍、标题、最近文章、服务矩阵、RGB 模块和底部入口",[241,602,603,608],{},[256,604,605],{},[62,606,607],{},"writing.yml",[256,609,610],{},"文章列表页介绍、SEO 和空状态",[241,612,613,618],{},[256,614,615],{},[62,616,617],{},"services.yml",[256,619,620],{},"服务列表、状态、图标、主页显示和访问说明",[241,622,623,628],{},[256,624,625],{},[62,626,627],{},"about.yml",[256,629,630],{},"关于页介绍、当前状态、关注主题和联系文案",[110,632,633,634,637,638,641,642,645,646,649],{},"拆分不只是 CMS 菜单分组。如果多个表单只声明同一 YAML 的部分字段，保存其中一个表单可能覆盖另一个表单未声明的字段；因此每个入口必须拥有独立源文件。",[62,635,636],{},"useSiteSettings()"," 使用固定异步数据键 ",[62,639,640],{},"site-settings","，通过 ",[62,643,644],{},"Promise.all"," 并行查询五个 data collection，再返回结构化的 ",[62,647,648],{},"site\u002Fhome\u002Fwriting\u002Fservices\u002Fabout"," 对象。任何一份缺失都会立即抛出 500，而不是让页面带着部分设置运行。",[110,651,652,653,656],{},"这些配置不是任意 YAML。",[62,654,655],{},"content.schema.ts"," 为每个文件提供独立严格 Schema，例如：",[200,658,659,665,668,671,680,683,689],{},[203,660,661,662,446],{},"默认强调色必须是 ",[62,663,664],{},"#RRGGBB",[203,666,667],{},"最新文章数量只能是 1 到 3；",[203,669,670],{},"服务数量只能是 1 到 12；",[203,672,673,674,408,676,408,678,446],{},"服务状态只能是 ",[62,675,61],{},[62,677,73],{},[62,679,67],{},[203,681,682],{},"图标只能使用代码中已有的枚举；",[203,684,685,686,688],{},"服务链接只能是 ",[62,687,78],{}," 或完整的 HTTPS URL；",[203,690,691],{},"对象使用严格模式，拼错字段不会被静默忽略。",[110,693,694,697,698,700,701,704],{},[62,695,696],{},"public\u002Fadmin\u002Fconfig.yml"," 的表单选项、",[62,699,655],{}," 的运行时约束、",[62,702,703],{},"app\u002Fconfig\u002Fsite.ts"," 的 TypeScript 枚举应该同步修改。只改 CMS 下拉框，不改 Schema，保存后会校验失败；只改 Schema，不改组件映射，界面可能没有正确图标。",[138,706,708],{"id":707},"七全站外壳与水合边界","七、全站外壳与水合边界",[110,710,711,714],{},[62,712,713],{},"app\u002Fapp.vue"," 是每个路由外面的共同外壳。它负责：",[200,716,717,720,723,726,732,735,741,744],{},[203,718,719],{},"在页面加载时取得站点设置；",[203,721,722],{},"判断当前是否为主页；",[203,724,725],{},"初始化和恢复主题色；",[203,727,728,729,446],{},"把主题 CSS 变量挂到 ",[62,730,731],{},".site-shell",[203,733,734],{},"为非主页显示“返回”按钮与主题预设切换按钮；",[203,736,737,738,446],{},"设置标题模板、站点描述、Open Graph 信息和 ",[62,739,740],{},"theme-color",[203,742,743],{},"根据当前强调色实时生成 data URL favicon；",[203,745,746],{},"显示 Nuxt 页面加载进度条。",[110,748,749,752,753,756,757,760,761,764],{},[62,750,751],{},"hydrated"," 初始为 ",[62,754,755],{},"false","，在 ",[62,758,759],{},"onMounted"," 完成主题恢复和本地监听注册后变成 ",[62,762,763],{},"true","，最终反映为：",[149,766,770],{"className":767,"code":768,"language":769,"meta":155,"style":155},"language-html shiki shiki-themes github-light github-dark","\u003Cdiv class=\"site-shell\" data-hydrated=\"true\">\n","html",[62,771,772],{"__ignoreMap":155},[773,774,777,781,785,789,792,796,799,801,804],"span",{"class":775,"line":776},"line",1,[773,778,780],{"class":779},"sVt8B","\u003C",[773,782,784],{"class":783},"s9eBZ","div",[773,786,788],{"class":787},"sScJk"," class",[773,790,791],{"class":779},"=",[773,793,795],{"class":794},"sZZnC","\"site-shell\"",[773,797,798],{"class":787}," data-hydrated",[773,800,791],{"class":779},[773,802,803],{"class":794},"\"true\"",[773,805,806],{"class":779},">\n",[110,808,809],{},"这个属性既是测试可观察的“客户端已经接管页面”标志，也是字标动画的播放闸门。CSS 动画先建立，但在页面完成水合前保持暂停，防止服务端 HTML 刚显示就消耗掉一段动画，等 JavaScript 接管时只剩最终帧。",[110,811,812,813,816,817,820],{},"非主页的全局返回入口固定回到 ",[62,814,815],{},"\u002F","，文本只有“返回”，图标和 ",[62,818,819],{},"aria-label"," 补充了方向与完整语义。它与文章正文中的“返回文章”属于不同层级：前者返回网站主页，后者返回文章列表。",[138,822,824],{"id":823},"八主页是一张单屏工作台","八、主页是一张单屏工作台",[110,826,827],{},"主页不是按多个营销区块向下滚动，而是一张固定在一个视口高度内的工作台。核心区域包括：",[200,829,830,833,836,839,842,845,848],{},[203,831,832],{},"顶部站点标识、上海时区时钟、指针坐标和主题按钮；",[203,834,835],{},"左上介绍、普通标题前缀与 Quilibra 字标；",[203,837,838],{},"右上服务矩阵；",[203,840,841],{},"左下最近三篇文章；",[203,843,844],{},"右下 RGB 调色器；",[203,846,847],{},"底部中央文章、服务、关于三个入口；",[203,849,850],{},"两侧备案与署名。",[110,852,853,854,857,858,861,862,865,866,869],{},"首页在 ",[62,855,856],{},"setup"," 阶段并行取得站点设置和最近文章。文章查询按 ",[62,859,860],{},"date DESC"," 排序，再按 YAML 中的 ",[62,863,864],{},"limit"," 截取。服务矩阵先筛选 ",[62,867,868],{},"showOnHome: true","，按 YAML 顺序最多取四个；服务页仍然展示完整列表。",[110,871,872,873,876,877,880,881,884,885,888],{},"顶部时钟通过 ",[62,874,875],{},"Intl.DateTimeFormat"," 固定使用 ",[62,878,879],{},"Asia\u002FShanghai","，每 30 秒更新一次。指针坐标不是每次 ",[62,882,883],{},"pointermove"," 都直接改 DOM：事件只保存最新坐标，然后在下一帧 ",[62,886,887],{},"requestAnimationFrame"," 中统一计算。元素边界在挂载与窗口缩放时缓存，触摸指针被忽略。这样可以减少高频事件中的布局读取和响应式更新。",[110,890,891],{},"工作台没有为了装饰引入 canvas 或 WebGL。网格、面板、条纹、阴影和入场效果全部由 CSS 完成，浏览器可以直接合成绝大多数变换。",[138,893,895],{"id":894},"九响应式布局为何不只是按比例缩小","九、响应式布局为何不只是“按比例缩小”",[110,897,898],{},"主页有大量绝对定位元素，因此响应式的目标不是把桌面页面机械缩成手机版，而是维护清晰的空间约束：",[200,900,901,908,911,914,917,924],{},[203,902,903,904,907],{},"桌面端通过 ",[62,905,906],{},"clamp()","、视口高度和稳定的面板高度控制上下关系；",[203,909,910],{},"最近文章与 RGB 面板在宽桌面上对齐顶部和底部；",[203,912,913],{},"小屏笔记本缩小间距和模块尺寸，仍避免重叠；",[203,915,916],{},"768 像素附近的紧凑布局隐藏无法舒适操作的 RGB 面板；",[203,918,919,920,923],{},"手机端重新排列主页元素，并使用 ",[62,921,922],{},"overflow: clip"," 防止装饰或过渡产生横向滚动；",[203,925,926],{},"固定格式组件使用明确的 grid 列、最小宽度和高度，标题悬浮时不会重新计算可用宽度。",[110,928,929],{},"Playwright 会在 768×1024、1024×768、1440×900 和 3440×1440 上读取真实元素几何信息，验证介绍不压住服务矩阵、上方面板不压住底部导航、RGB 与最近文章对齐、页面没有横向溢出。这里测试的是关系，而不是容易因一个像素改动就失效的整页截图。",[138,931,933],{"id":932},"十主题色从一个-rgb-值扩展成整套界面","十、主题色从一个 RGB 值扩展成整套界面",[110,935,936,937,940],{},"主题逻辑集中在 ",[62,938,939],{},"useAccentTheme.ts","。唯一的核心状态是：",[149,942,946],{"className":943,"code":944,"language":945,"meta":155,"style":155},"language-ts shiki shiki-themes github-light github-dark","interface RgbColor {\n  r: number\n  g: number\n  b: number\n}\n","ts",[62,947,948,960,974,983,993],{"__ignoreMap":155},[773,949,950,954,957],{"class":775,"line":776},[773,951,953],{"class":952},"szBVR","interface",[773,955,956],{"class":787}," RgbColor",[773,958,959],{"class":779}," {\n",[773,961,963,967,970],{"class":775,"line":962},2,[773,964,966],{"class":965},"s4XuR","  r",[773,968,969],{"class":952},":",[773,971,973],{"class":972},"sj4cs"," number\n",[773,975,976,979,981],{"class":775,"line":35},[773,977,978],{"class":965},"  g",[773,980,969],{"class":952},[773,982,973],{"class":972},[773,984,986,989,991],{"class":775,"line":985},4,[773,987,988],{"class":965},"  b",[773,990,969],{"class":952},[773,992,973],{"class":972},[773,994,996],{"class":775,"line":995},5,[773,997,998],{"class":779},"}\n",[110,1000,1001,1002,1005,1006,1009],{},"三个内置预设是红、绿、蓝。站点设置里的 ",[62,1003,1004],{},"theme.defaultAccent"," 可以是任意合法十六进制颜色，并不要求与预设一致。当前颜色存放在 Nuxt ",[62,1007,1008],{},"useState"," 中，因此同一客户端的页面切换不会创建互相冲突的主题实例。",[110,1011,1012],{},"主题计算分为几步：",[380,1014,1015,1018,1028,1031,1037],{},[203,1016,1017],{},"把每个通道四舍五入并限制在 0 到 255。",[203,1019,1020,1021,1024,1025,1027],{},"生成 CSS ",[62,1022,1023],{},"rgb(r g b)"," 与大写 ",[62,1026,664],{},"。",[203,1029,1030],{},"把强调色分别与白色、画布色和黑色混合，得到 soft、faint、canvas、grid、strong 等衍生色。",[203,1032,1033,1034,1027],{},"计算 sRGB 相对亮度，对比黑白文字的对比度，自动选择更清晰的 ",[62,1035,1036],{},"--accent-ink",[203,1038,1039],{},"把结果以 CSS 变量挂到站点外壳，按钮、悬浮底色、网格、字标和 favicon 共同消费这些变量。",[110,1041,1042],{},"主题变量的意义大致如下：",[149,1044,1047],{"className":1045,"code":1046,"language":154,"meta":155},[152],"--accent          原始强调色\n--accent-ink      强调色背景上的黑或白文字\n--accent-soft     大面积悬浮反馈\n--accent-faint    更浅的面板背景\n--accent-canvas   轻微带主题倾向的页面画布\n--accent-grid     网格与线条色\n--accent-strong   向黑色混合后的强调色\n",[62,1048,1046],{"__ignoreMap":155},[110,1050,1051,1052,1054],{},"这样做比在 CSS 中到处硬编码“红色按钮、浅红背景、深红边框”更可靠。换成极亮或极暗的自定义颜色时，文字对比度仍会自动调整。E2E 会直接把颜色调到纯白和纯黑，验证 ",[62,1053,1036],{}," 分别变成深色和白色。",[138,1056,1058],{"id":1057},"十一主题持久化与旧黄色迁移","十一、主题持久化与旧黄色迁移",[110,1060,1061,1062,1065,1066,1069],{},"客户端挂载时，",[62,1063,1064],{},"restoreSavedAccent()"," 从 ",[62,1067,1068],{},"localStorage"," 读取：",[149,1071,1074],{"className":1072,"code":1073,"language":154,"meta":155},[152],"site-accent-rgb   完整 RGB 对象\nsite-accent       red \u002F green \u002F blue \u002F custom 等名称\n",[62,1075,1073],{"__ignoreMap":155},[110,1077,1078,1079,1082],{},"恢复后，深度监听 ",[62,1080,1081],{},"accentRgb","，后续每次变化都会重新保存。由于服务端无法读取浏览器存储，静态 HTML 首先使用 YAML 中的默认颜色；客户端挂载后再恢复访客上次的选择。",[110,1084,1085,1086,354,1089,1092],{},"旧版本曾使用黄色默认主题。为了避免已经访问过网站的浏览器永远被旧缓存锁在黄色，恢复逻辑专门识别 ",[62,1087,1088],{},"{255,255,2}",[62,1090,1091],{},"yellow"," 的组合，删除这两个旧值并回到当前 YAML 默认色。这是一次小型数据迁移：不是清空所有人的自定义配色，只清除能够明确识别的旧默认值。",[110,1094,1095,1096,1099,1100,1103,1104,1107,1108,1111,1112,1115],{},"动态 favicon 也跟随主题和站点名称。",[62,1097,1098],{},"app.vue"," 在内存中生成一个包含主题背景色与 ",[62,1101,1102],{},"site.name"," 首字符的 SVG，再编码为 ",[62,1105,1106],{},"data:image\u002Fsvg+xml","。因此 CMS 修改站点名称并重新构建后，标签页字母会同步改变；图标底色、字色和 ",[62,1109,1110],{},"meta[name=theme-color]"," 则继续与 RGB 面板同步。",[62,1113,1114],{},"public\u002Ffavicon.svg"," 只作为静态回退。",[138,1117,1119],{"id":1118},"十二rgb-调色器的交互细节","十二、RGB 调色器的交互细节",[110,1121,1122,1125,1126,1129],{},[62,1123,1124],{},"RgbAssembly.vue"," 不是独立维护另一份颜色，它调用同一个 ",[62,1127,1128],{},"useAccentTheme()","，因此滑杆、预设按钮、页面按钮和 favicon 始终共享状态。",[110,1131,1132],{},"三个 range input 分别控制 R、G、B：",[200,1134,1135,1141,1144,1147,1150],{},[203,1136,1137,1140],{},[62,1138,1139],{},"input"," 事件实时更新颜色，拖动时页面连续渐变；",[203,1142,1143],{},"每条轨道通过 CSS 变量计算已填充百分比；",[203,1145,1146],{},"输出区显示十六进制、十进制通道值，并采用自动对比文字；",[203,1148,1149],{},"点击 RED、GREEN、BLUE 立即加载预设；",[203,1151,1152],{},"重置按钮恢复 YAML 中的默认颜色，而不是写死某个预设。",[110,1154,1155],{},"字标动画不能在滑杆的每一个像素变化时重播，否则会不断重建动画并显得卡顿。组件因此区分“预览变化”和“提交变化”：",[200,1157,1158,1165,1171,1181,1188],{},[203,1159,1160,1161,1164],{},"指针按下后标记 ",[62,1162,1163],{},"pointerActive","，并捕获当前 pointer；",[203,1166,1167,1168,1170],{},"拖动中的 ",[62,1169,1139],{}," 只改变颜色；",[203,1172,1173,1176,1177,1180],{},[62,1174,1175],{},"pointerup"," 或 ",[62,1178,1179],{},"pointercancel"," 才请求重播一次字标；",[203,1182,1183,1184,1187],{},"键盘调整 range 时由 ",[62,1185,1186],{},"change"," 请求重播；",[203,1189,1190,1191,423,1193,1195],{},"一个零延时抑制标志防止同一次鼠标提交同时触发 ",[62,1192,1175],{},[62,1194,1186],{},"，造成双播。",[110,1197,1198,1199],{},"预设与重置属于离散操作，所以点击时立即换色并重播。这个模型可以概括为：",[178,1200,1201],{},"连续操作实时预览、松手提交动画；离散操作立即提交。",[138,1203,1205],{"id":1204},"十三quilibra-字标是可编辑的艺术字","十三、Quilibra 字标是可编辑的艺术字",[110,1207,1208,1209,1212,1213,1216],{},"当前字标不再依赖固定图片，而是由 ",[62,1210,1211],{},"content\u002Fsettings\u002Fhome.yml"," 中的 ",[62,1214,1215],{},"headline.emphasis"," 生成真实文本。CMS 的“首页 \u002F 艺术字文字”可以直接修改内容，Schema 将长度限制为 20 个字符；组件还会按照中英文字符宽度自动缩小过长文字，避免挤出首屏。",[110,1218,1219],{},"字形使用随构建产物一起发布的 Cormorant Garamond 600 Italic。页面只引入拉丁字符子集，既保留高对比衬线斜体的艺术感，也不依赖 Google Fonts 等站外服务；无法覆盖的字符会回退到本机衬线字体。",[110,1221,1222],{},"动画仍然采用“RGB 套色从偏移到合拢”，没有模拟笔尖逐笔书写。字标由四层相同文本组成：",[200,1224,1225,1228,1231,1234],{},[203,1226,1227],{},"红层从左下附近偏移进入；",[203,1229,1230],{},"绿层从上方偏移进入；",[203,1232,1233],{},"蓝层从右下附近偏移进入；",[203,1235,1236],{},"最终层使用当前主题色并带极轻的阴影。",[110,1238,1239,1240,1243,1244,408,1247,423,1250,1253,1254,1257],{},"前三层使用 ",[62,1241,1242],{},"mix-blend-mode: multiply","、动态模糊、透明度和不同位移。动画前段保留可见错版，中段快速靠近，后段降低彩色层透明度并让最终主题层聚焦。四层都只改变 ",[62,1245,1246],{},"opacity",[62,1248,1249],{},"transform",[62,1251,1252],{},"filter","，播放时临时声明 ",[62,1255,1256],{},"will-change","，结束后不长期占用合成资源。",[110,1259,1260,1263],{},[62,1261,1262],{},"ArtisticWordmark.vue"," 的加载控制解决了“首次打开没有动画”和字体中途替换的问题：",[380,1265,1266,1272,1279,1282,1285],{},[203,1267,1268,1271],{},[62,1269,1270],{},"@fontsource\u002Fcormorant-garamond"," 把字体文件交给 Vite 打包并生成带哈希的静态资源。",[203,1273,1274,1275,1278],{},"组件调用 ",[62,1276,1277],{},"document.fonts.load()","，等待艺术字体真正可用于绘制。",[203,1280,1281],{},"只有字体就绪、组件仍存活、页面可见且请求仍是最新时才开始播放。",[203,1283,1284],{},"字体加载失败时继续使用衬线回退字体，不阻断内容显示。",[203,1286,1287],{},"开启减少动态效果时直接展示最终状态。",[110,1289,1290,1291,1294,1295,1298],{},"每次重播都会增加 ",[62,1292,1293],{},"animationRun","，并把它放进四层的 Vue ",[62,1296,1297],{},"key","。这会让浏览器得到新的动画元素，可靠地从第 0 帧开始，而不是依赖移除 class 后强制读取布局。",[138,1300,1302],{"id":1301},"十四字标在什么时候重播","十四、字标在什么时候重播",[110,1304,1305,1306,1309,1310,1313],{},"全局 ",[62,1307,1308],{},"useWordmarkAnimation()"," 只维护一个数字信号。任何组件调用 ",[62,1311,1312],{},"requestReplay()","，数字加一；字标组件 watch 到变化后重播。这个很小的事件通道避免 RGB 组件直接引用字标 DOM。",[110,1315,1316],{},"当前重播场景包括：",[200,1318,1319,1322,1325,1328,1331,1334,1337],{},[203,1320,1321],{},"首次进入主页并且艺术字体加载完成；",[203,1323,1324],{},"点击主题预设或重置；",[203,1326,1327],{},"RGB 滑杆松手或键盘提交；",[203,1329,1330],{},"从文章、服务、关于页面返回主页，主页组件重新挂载；",[203,1332,1333],{},"浏览器标签页从隐藏变为可见；",[203,1335,1336],{},"窗口重新获得焦点；",[203,1338,1339,1340,1343],{},"浏览器的 ",[62,1341,1342],{},"pageshow","，包括可能来自往返缓存的恢复。",[110,1345,1346,408,1349,423,1352,1354],{},[62,1347,1348],{},"visibilitychange",[62,1350,1351],{},"focus",[62,1353,1342],{}," 可能在同一次切回中连续到达。组件使用一个 animation frame 合并激活请求，并设置 200ms 的最小间隔，防止一次切回连续播两三遍。页面变为隐藏时会取消待播放帧、使旧异步请求失效并结束当前播放。",[110,1356,1357],{},"这里还有两个无障碍与容错原则：",[200,1359,1360,1366],{},[203,1361,1362,1365],{},[62,1363,1364],{},"prefers-reduced-motion: reduce"," 时不播放套色，直接显示最终字标；",[203,1367,1368,1369,1372],{},"可读文字仍存在于 ",[62,1370,1371],{},"sr-only"," 中，视觉 mask 不承担标题语义。",[138,1374,1376],{"id":1375},"十五服务矩阵图标与状态点","十五、服务矩阵、图标与状态点",[110,1378,1379],{},"每个服务在 YAML 中有六个字段：名称、说明、链接、状态、图标、是否显示在主页。",[110,1381,1382,1385,1386,354,1388,1391,1392,1395],{},[62,1383,1384],{},"ServiceIcon.vue"," 把业务名称映射到 Lucide 图标。NAS 使用独立的 ",[62,1387,74],{},[62,1389,1390],{},"HardDrive","，不会复用影音播放图标。未知值虽然有 ",[62,1393,1394],{},"Server"," 回退，但正常内容会在 Schema 阶段被拒绝，因此回退只是组件级防御。",[110,1397,1398],{},"状态共有三种：",[149,1400,1403],{"className":1401,"code":1402,"language":154,"meta":155},[152],"online   在线\nprivate  仅限本人\nsoon     准备中\n",[62,1404,1402],{"__ignoreMap":155},[110,1406,1407,1410,1411,1414,1415,1418],{},[62,1408,1409],{},"ServiceStatusDot.vue"," 把状态写入 ",[62,1412,1413],{},"data-state","，CSS 用属性选择器决定颜色，",[62,1416,1417],{},"title"," 提供文字说明。主页和服务页都复用这个组件，因此 CMS 修改 YAML 状态后，重新构建的两个页面会同步变化，不需要分别维护配色。",[110,1420,1421,1422,1425],{},"状态点表达的是",[178,1423,1424],{},"人工配置的展示状态","，不是实时健康检查。它不会主动请求 Code、Photos、NAS 或 Status，也不会自动从监控系统同步。如果未来需要实时状态，应由独立状态 API 提供可信结果，再决定是在客户端请求还是构建前抓取；不能仅把绿色小点误解为自动监控。",[110,1427,1428,1429,1431,1432,1435,1436,1439,1440,1443],{},"链接为 ",[62,1430,78],{}," 时，主页服务节点转到 ",[62,1433,1434],{},"\u002Fservices","，服务页显示为不可点击行；完整 HTTPS 链接则在新标签页打开，并带 ",[62,1437,1438],{},"noopener noreferrer","。Schema 禁止 ",[62,1441,1442],{},"javascript:"," 和非 HTTPS 外部地址，避免 CMS 字段直接变成危险链接。",[138,1445,1447],{"id":1446},"十六文章列表筛选与搜索","十六、文章列表、筛选与搜索",[110,1449,1450,1453,1454,1457],{},[62,1451,1452],{},"\u002Fwriting"," 在构建阶段只查询 ",[62,1455,1456],{},"hidden = false"," 的文章，并按日期倒序排列。首页最近文章使用同样条件，浏览器端再对已经加载的公开数组做筛选，不需要为每个关键词请求服务器。",[110,1459,1460,1461,1464],{},"分类来自共享常量：随笔、学习、交易、观察。列表额外添加“全部”。搜索会把标题、摘要和标签拼成小写字符串，再做简单的 ",[62,1462,1463],{},"includes"," 匹配。这适合当前文章规模，特点是实现透明、中文可用、没有索引服务；但它不是分词搜索，也不会搜索正文。",[110,1466,1467],{},"文章行显示日期、标题、摘要、分类和箭头。悬浮一篇时：",[200,1469,1470,1473,1476,1479],{},[203,1471,1472],{},"只给当前行增加浅主题色背景；",[203,1474,1475],{},"日期与正文整体平移 8px；",[203,1477,1478],{},"箭头向右上轻移；",[203,1480,1481],{},"其他文章保持完全不变。",[110,1483,1484,1485,408,1488,1491,1492,1495],{},"标题容器的 grid 列不会在 hover 时改变，所以长标题不会因为悬浮突然获得更小宽度而换行。主页“最近写下”的标题使用 ",[62,1486,1487],{},"minmax(0,1fr)",[62,1489,1490],{},"overflow:hidden","、省略号和 ",[62,1493,1494],{},"white-space:nowrap","，确保固定高度面板不被长标题撑开。",[110,1497,1498,1499,1502],{},"搜索结果区使用 ",[62,1500,1501],{},"aria-live=\"polite\"","，筛选后的篇数和空状态可以被辅助技术感知。",[138,1504,1506],{"id":1505},"十七文章详情与相邻导航","十七、文章详情与相邻导航",[110,1508,1509,1510,1513,1514,1517],{},"动态路由文件是 ",[62,1511,1512],{},"app\u002Fpages\u002Fwriting\u002F[...slug].vue","。它使用当前 ",[62,1515,1516],{},"route.path"," 查询对应文章；找不到时立即抛出带中文说明的 404。",[110,1519,1520,1521,1523],{},"详情查询本身也要求 ",[62,1522,1456],{},"，所以隐藏文章不只是从列表消失，直接访问原 URL 也会得到真正的 404。详情页同时取得按日期倒序排列的全部公开文章，用当前文章的数组位置计算：",[200,1525,1526,1529],{},[203,1527,1528],{},"数组中下一项是时间上更旧的“上一篇”；",[203,1530,1531],{},"数组中上一项是时间上更新的“下一篇”。",[110,1533,1534,1535,1537],{},"页面展示分类、发布日期、可选的更新日期、标题、摘要、标签与正文。正文由 Nuxt Content 的 ",[62,1536,551],{}," 生成。SEO 标题使用全站模板变成“文章标题 · Quilibra”，描述来自 frontmatter，Open Graph 类型设为 article。",[110,1539,1540,1541,1544],{},"正文样式集中在 ",[62,1542,1543],{},".article-prose","：限制阅读宽度、提高行高、分级处理标题、列表、引用、代码、链接和图片。文章页面可以自由使用 Markdown，不需要为每篇文章编写 Vue 模板。",[138,1546,1548],{"id":1547},"十八文章-frontmatter-合约","十八、文章 frontmatter 合约",[110,1550,1551],{},"一篇新文章最小结构如下：",[149,1553,1557],{"className":1554,"code":1555,"language":1556,"meta":155,"style":155},"language-yaml shiki shiki-themes github-light github-dark","---\ntitle: 清楚的文章标题\ndescription: 一句话说明文章实际记录了什么。\ndate: 2026-07-26\nhidden: false\ncategory: 学习\ntags:\n  - Nuxt\n---\n","yaml",[62,1558,1559,1564,1574,1584,1594,1604,1615,1624,1633],{"__ignoreMap":155},[773,1560,1561],{"class":775,"line":776},[773,1562,1563],{"class":787},"---\n",[773,1565,1566,1568,1571],{"class":775,"line":962},[773,1567,1417],{"class":783},[773,1569,1570],{"class":779},": ",[773,1572,1573],{"class":794},"清楚的文章标题\n",[773,1575,1576,1579,1581],{"class":775,"line":35},[773,1577,1578],{"class":783},"description",[773,1580,1570],{"class":779},[773,1582,1583],{"class":794},"一句话说明文章实际记录了什么。\n",[773,1585,1586,1589,1591],{"class":775,"line":985},[773,1587,1588],{"class":783},"date",[773,1590,1570],{"class":779},[773,1592,1593],{"class":972},"2026-07-26\n",[773,1595,1596,1599,1601],{"class":775,"line":995},[773,1597,1598],{"class":783},"hidden",[773,1600,1570],{"class":779},[773,1602,1603],{"class":972},"false\n",[773,1605,1607,1610,1612],{"class":775,"line":1606},6,[773,1608,1609],{"class":783},"category",[773,1611,1570],{"class":779},[773,1613,1614],{"class":794},"学习\n",[773,1616,1618,1621],{"class":775,"line":1617},7,[773,1619,1620],{"class":783},"tags",[773,1622,1623],{"class":779},":\n",[773,1625,1627,1630],{"class":775,"line":1626},8,[773,1628,1629],{"class":779},"  - ",[773,1631,1632],{"class":794},"Nuxt\n",[773,1634,1636],{"class":775,"line":1635},9,[773,1637,1563],{"class":787},[110,1639,1640],{},"文件名必须是：",[149,1642,1645],{"className":1643,"code":1644,"language":154,"meta":155},[152],"YYYY-MM-DD-english-kebab.md\n",[62,1646,1644],{"__ignoreMap":155},[110,1648,1649,1650,408,1652,408,1654,408,1656,408,1659,408,1662,408,1664,408,1666,1027,1668,1670,1671,1673],{},"允许字段只有 ",[62,1651,1417],{},[62,1653,1578],{},[62,1655,1588],{},[62,1657,1658],{},"updated",[62,1660,1661],{},"slug",[62,1663,1598],{},[62,1665,1609],{},[62,1667,1620],{},[62,1669,1598],{}," 只能是布尔值，省略时按 ",[62,1672,755],{}," 处理。发布新文章时：",[200,1675,1676,1681,1687,1692],{},[203,1677,1678,1680],{},[62,1679,1588],{}," 必须是中国时区当天；",[203,1682,1683,1684,1686],{},"文件名日期必须与 ",[62,1685,1588],{}," 一致；",[203,1688,1689,1690,446],{},"不能包含 ",[62,1691,1658],{},[203,1693,1694,1696],{},[62,1695,1661],{}," 如果存在，必须与文件名后半段一致。",[110,1698,1699,1700,1702,1703,1705,1706,1709],{},"修订文章时保留原文件名和 ",[62,1701,1588],{},"，把 ",[62,1704,1658],{}," 设置为中国时区当天。writer 默认保留现有隐藏状态，只有修订内容明确给出 ",[62,1707,1708],{},"hidden: false"," 才会恢复。正文结构是自由的，但不能为空。若使用二级标题“来源与延伸阅读”，该节必须至少有一个 HTTP 或 HTTPS 链接。",[110,1711,1712,1715,1716,1719],{},[62,1713,1714],{},"scripts\u002Fcontent\u002Fvalidate.ts"," 使用 ",[62,1717,1718],{},"gray-matter"," 和 YAML 解析 frontmatter，再用 Markdown AST 检查正文与来源节。使用结构化解析而不是正则扫描全文，可以区分标题、链接与普通文本，并给出确定的错误。",[138,1721,1723],{"id":1722},"十九sveltia-cms-到底是什么","十九、Sveltia CMS 到底是什么",[110,1725,1726,1729,1730,1732],{},[62,1727,1728],{},"\u002Fadmin\u002Findex.html"," 只加载 Sveltia CMS 的前端脚本，",[62,1731,696],{}," 描述后台字段。它不是另一个网站后端，也不存文章副本。",[110,1734,1735],{},"后台能维护：",[200,1737,1738,1741,1744,1747,1750,1753],{},[203,1739,1740],{},"站点、首页与栏目文案；",[203,1742,1743],{},"默认强调色和主页模块开关；",[203,1745,1746],{},"服务的增删、顺序、URL、状态、图标和主页显示；",[203,1748,1749],{},"文章的新建、搜索、筛选、修改和删除；",[203,1751,1752],{},"文章的隐藏和恢复；",[203,1754,1755,1757],{},[62,1756,426],{}," 中的文章资源。",[110,1759,1760],{},"CMS 有两种工作方式：",[380,1762,1763,1766],{},[203,1764,1765],{},"本地仓库模式：Chromium 通过 File System Access API 直接编辑本机选择的仓库。",[203,1767,1768],{},"GitHub 模式：用户经 GitHub 登录后，CMS 根据配置向私有仓库提交内容。",[110,1770,1771,1772,1774],{},"无论哪种模式，“保存”都只意味着源文件或远程仓库出现改动。它不会直接覆盖某个静态目录。远程 CMS 当前固定写入私人仓库的 ",[62,1773,124],{}," 分支，完整链路是：",[149,1776,1779],{"className":1777,"code":1778,"language":154,"meta":155},[152],"dev.quilibra.cn\u002Fadmin\n        |\n        | GitHub OAuth 保存\n        v\nGitHub staging\n        |\n        | GitHub push Webhook，HMAC-SHA256 签名\n        v\nVPS 事件队列 -> staging 同步 -> 快速或完整构建 -> staging release -> 127.0.0.1:3100\n        |\n        | 人工确认测试页面后 npm run promote\n        v\nGitHub master\n        |\n        | GitHub push Webhook，HMAC-SHA256 签名\n        v\nVPS 事件队列 -> master 同步 -> 快速或完整构建 -> production release -> 127.0.0.1:3000\n",[62,1780,1778],{"__ignoreMap":155},[110,1782,1783,1784,1787],{},"因此，在 CMS 打开“隐藏文章”并保存，只会先让测试站列表和原 URL 发生变化，正式站保持不动。确认后执行 ",[62,1785,1786],{},"npm run promote","，生产才接收同一个内容状态。这是分支、构建目录、release 和进程都真实隔离的预发布环境，不是给同一份生产内容换了一个域名。",[110,1789,1790,1791,1794],{},"GitHub OAuth 只控制“谁能通过 CMS 读写私人仓库”。本项目不使用 GitHub Actions 部署，也不开放公网 SSH；GitHub 只向现有 HTTPS 精确路径发送带签名的 push 通知，VPS 收到后再通过 Sing-box HTTP 代理主动拉取对应分支。Webhook secret 只在 VPS 和 GitHub 仓库设置中保存。私人仓库存放网站源码、文章、公开图片和不含密钥的配置；OAuth secret、访问令牌、Webhook secret、部署私钥、OpenClaw 会话、cookie 和 ",[62,1792,1793],{},".env"," 不进入仓库。",[110,1796,1797,1798,1801,1802,1805,1806,1808],{},"远程后台统一通过 ",[62,1799,1800],{},"https:\u002F\u002Fdev.quilibra.cn\u002Fadmin\u002Findex.html"," 使用。Nginx 在 TLS 之外再加一层 HTTP Basic Auth，并发送 ",[62,1803,1804],{},"noindex","；GitHub OAuth 负责仓库权限，Basic Auth 负责挡住后台入口，两者职责不同。后台路径和 ",[62,1807,1804],{}," 本身都不是访问控制。",[138,1810,1812],{"id":1811},"二十受限-writer-为什么存在","二十、受限 writer 为什么存在",[110,1814,1815,1816,1819],{},"普通编辑器可以修改仓库中的任何文件，但自动化发布工具不应该拥有同样宽的写入自由。",[62,1817,1818],{},"scripts\u002Fcontent\u002Fwrite.ts"," 把 OpenClaw 的写入范围压缩为三个明确操作：create、update、delete。",[110,1821,1822],{},"新建流程只接受草稿目录中的一个普通 Markdown 文件名和一个无日期 slug。writer 会检查：",[200,1824,1825,1828,1831,1834,1837,1840,1843],{},[203,1826,1827],{},"草稿名不能包含目录或路径穿越；",[203,1829,1830],{},"草稿必须是普通文件，不能是符号链接；",[203,1832,1833],{},"草稿不超过 1 MiB；",[203,1835,1836],{},"slug 必须是小写英文、数字和连字符；",[203,1838,1839],{},"frontmatter 与正文必须通过完整校验；",[203,1841,1842],{},"日期必须是中国时区当天；",[203,1844,1845],{},"目标文章不能已经存在。",[110,1847,1848,1849,1852,1853,1856,1857,1860,1861,1864],{},"写入时先以 ",[62,1850,1851],{},"wx"," 模式建立权限为 ",[62,1854,1855],{},"0640"," 的随机临时文件并 ",[62,1858,1859],{},"fsync","，再用硬链接创建最终文件，利用文件系统的 ",[62,1862,1863],{},"EEXIST"," 保证不会覆盖已有新文章。更新时先验证现有文件和发布日期，再用 rename 原子替换。删除只接受精确的完整 dated slug，并拒绝目录、符号链接和不存在的目标。",[110,1866,1867],{},"这不是为了让日常写作变复杂，而是为了让“从聊天自动发布”仍有可审计的硬边界。Skill 中的文字规则可能被模型误解，writer 的路径、日期和文件类型检查则由操作系统与代码强制执行。",[138,1869,1871],{"id":1870},"二十一openclaw-发布-skill-的实际状态机","二十一、OpenClaw 发布 Skill 的实际状态机",[110,1873,1874,1875,1878],{},"仓库只维护一个 ",[62,1876,1877],{},"personal-site-publisher"," Skill。它先读取文章合约，再按以下过程工作：",[149,1880,1883],{"className":1881,"code":1882,"language":154,"meta":155},[152],"链接或素材\n   |\n   v\n确定标题、摘要、日期、slug、分类、标签\n   |\n   v\n在私聊中返回完整草稿，不写仓库\n   |\n   +-- 修改：... --> 修改内存草稿\n   +-- 放弃 ------> 清除内存草稿\n   +-- 发布 ------> 保存到受限草稿目录\n                         |\n                         v\n                   content:write\n                         |\n                         v\n                  npm run publish\n                         |\n                         v\n              返回文章 URL、commit、release\n",[62,1884,1882],{"__ignoreMap":155},[110,1886,1887,1890,1891,1027],{},[62,1888,1889],{},"直接发布"," 只跳过人工预览，不跳过 writer、内容校验和静态生成。更新文章必须保留 dated URL；删除必须先展示目标信息，再收到完全匹配的 ",[62,1892,1893],{},"确认删除 \u003Cdated-slug>",[110,1895,1896,1897,408,1900,1176,1903,1906],{},"发布 Skill 还规定：只接受所有者私聊中的发布命令；把网页、对话和搜索结果视为不可信内容；不把 API key、cookie、账号信息、私人对话细节和精确私人资产写入文章；不能用任意编辑器、",[62,1898,1899],{},"rm",[62,1901,1902],{},"git add",[62,1904,1905],{},"git commit"," 绕过 writer 和 release 脚本。",[138,1908,1910],{"id":1909},"二十二本地发布自动同步与环境晋级","二十二、本地发布、自动同步与环境晋级",[110,1912,1913],{},"当前有五个相关入口，名字接近但权限完全不同。",[1915,1916,1918],"h3",{"id":1917},"npm-run-publish",[62,1919,1920],{},"npm run publish",[110,1922,1923],{},"用于受信本地流程中尚未提交的内容改动，例如 OpenClaw Skill。它只允许以下路径：",[149,1925,1928],{"className":1926,"code":1927,"language":154,"meta":155},[152],"content\u002Fsettings\u002Fsite.yml\ncontent\u002Fsettings\u002Fhome.yml\ncontent\u002Fsettings\u002Fwriting.yml\ncontent\u002Fsettings\u002Fservices.yml\ncontent\u002Fsettings\u002Fabout.yml\ncontent\u002Fwriting\u002F**\npublic\u002Fuploads\u002F**\n",[62,1929,1927],{"__ignoreMap":155},[110,1931,1932],{},"它验证内容、生成静态站、自动创建本地 Git commit、复制 release 并切换本地默认生产目录。若工作区混入 Vue、CSS、脚本、CMS 配置或文档改动，会拒绝执行。它是受限本地发布入口，不是远程 CMS 的预发布流程。",[1915,1934,1936],{"id":1935},"npm-run-deploy",[62,1937,1938],{},"npm run deploy",[110,1940,1941],{},"用于已经提交的代码版本。它要求工作区完全干净，不创建 commit，并依次运行：",[149,1943,1946],{"className":1944,"code":1945,"language":154,"meta":155},[152],"类型检查\n内容与 writer 单元测试\nrelease 单元测试\n静态生成\n浏览器 E2E\n创建 release\n原子切换 current\n清理过旧 release\n",[62,1947,1945],{"__ignoreMap":155},[1915,1949,1951],{"id":1950},"npm-run-sync",[62,1952,1953],{},"npm run sync",[110,1955,1956,1959,1960,1962,1963,1965],{},[62,1957,1958],{},"scripts\u002Fsync.ts"," 是两个 systemd oneshot 服务共用的自动同步器。环境变量决定它跟踪 ",[62,1961,132],{}," 还是 ",[62,1964,124],{},"、使用哪一份独立仓库、把 release 写到哪个数据根目录。每轮会：",[380,1967,1968,1971,1974,1984,1989,1995],{},[203,1969,1970],{},"从 Sing-box 配置读取带认证的本机 HTTP 代理；",[203,1972,1973],{},"重试 GitHub fetch，只接受能够快进的远端历史；",[203,1975,1976,1977,1980,1981,446],{},"根据 ",[62,1978,1979],{},"package-lock.json"," 哈希决定是否运行 ",[62,1982,1983],{},"npm ci",[203,1985,1986,1987,446],{},"比较上次已部署提交与目标提交：只有受管内容变化时执行快速内容部署，否则执行完整 ",[62,1988,1938],{},[203,1990,1991,1992,446],{},"只有部署成功才原子写入 ",[62,1993,1994],{},"deployed-commit",[203,1996,1997],{},"失败时保留旧 release，由持久化 Webhook 事件延迟重试。",[110,1999,2000,2001,2004],{},"纯文章、站点设置和上传资源变化仍会执行内容校验与静态生成，但跳过未变化代码的类型检查、单元测试和 E2E。生产和测试同步服务使用同一个 ",[62,2002,2003],{},"flock"," 文件串行执行，防止 2 GB 内存的 VPS 同时跑两份 Nuxt 构建和 Playwright。",[1915,2006,2008],{"id":2007},"npm-run-webhook",[62,2009,2010],{},"npm run webhook",[110,2012,2013,2016,2017,2020,2021,2024,2025,2028,2029,2032,2033,1027],{},[62,2014,2015],{},"scripts\u002Fwebhook.ts"," 只监听 ",[62,2018,2019],{},"127.0.0.1:3110","，Nginx 把 ",[62,2022,2023],{},"https:\u002F\u002Fquilibra.cn\u002F_hooks\u002Fgithub"," 的精确 POST 路由转给它。接收器先验证 HMAC-SHA256 签名、仓库名、push 类型和 ",[62,2026,2027],{},"staging\u002Fmaster"," 分支，再把事件以权限 ",[62,2030,2031],{},"0600"," 写入用户状态目录并返回 ",[62,2034,2035],{},"202",[110,2037,2038],{},"每个 GitHub delivery 都有独立队列文件。若构建期间出现第二次 push，第一轮完成后会再启动一轮同步；进程重启时也会恢复未完成事件。失败使用延迟重试，不需要周期性轮询 GitHub。Webhook 请求只能映射到两个固定 systemd unit，不能携带或执行任意命令。",[1915,2040,2042],{"id":2041},"npm-run-promote",[62,2043,1786],{},[110,2045,2046,2047,2049,2050,2052],{},"这是远程 CMS 从测试进入生产的唯一显式关口。它要求本地 ",[62,2048,132],{}," 干净且等于最新远端，确认远端 ",[62,2051,124],{}," 的精确提交已经成功部署到测试站，并拒绝 staging 中的 Vue、CSS、脚本、文档或 CMS 配置改动。允许晋级的只有：",[149,2054,2056],{"className":2055,"code":1927,"language":154,"meta":155},[152],[62,2057,1927],{"__ignoreMap":155},[110,2059,2060,2061,2064,2065,423,2067,2069],{},"检查通过后，脚本在临时 Git worktree 中把 staging 合并到最新 master，再用一次 ",[62,2062,2063],{},"git push --atomic"," 让远端 ",[62,2066,132],{},[62,2068,124],{}," 同时指向同一个合并提交。若任一引用不能更新，两边都不更新。GitHub 随后发送 master Webhook 并触发正式部署。",[110,2071,2072,2073,2076,2077,2079,2080,2083,2084,2086,2087,2089],{},"因此，修改本文这样的文章有两种合法路径：本地受信流程可用 ",[62,2074,2075],{},"publish","；远程 CMS 则保存到 staging、检查测试站、再运行 ",[62,2078,128],{},"。修改 Vue\u002FCSS 并已经提交后使用 ",[62,2081,2082],{},"deploy"," 或由 master 同步器部署。不要用 ",[62,2085,2075],{}," 偷渡代码，也不要让 ",[62,2088,2082],{}," 带着未提交文件上线。",[138,2091,2093],{"id":2092},"二十三静态-release-如何做到切换时不中断","二十三、静态 release 如何做到切换时不中断",[110,2095,2096,2099],{},[62,2097,2098],{},"scripts\u002Frelease.ts"," 用 UTC 时间生成 release 名称，例如：",[149,2101,2104],{"className":2102,"code":2103,"language":154,"meta":155},[152],"20260726T144914-134Z\n",[62,2105,2103],{"__ignoreMap":155},[110,2107,2108,2109,2111,2112,2115,2116,2118],{},"生成结束后，把 ",[62,2110,183],{}," 递归复制到 ",[62,2113,2114],{},"releases\u002F\u003Cid>","。激活时不是先删 ",[62,2117,187],{}," 再新建，而是：",[380,2120,2121,2124,2127],{},[203,2122,2123],{},"创建一个带进程号的临时符号链接；",[203,2125,2126],{},"让临时链接指向新 release；",[203,2128,2129,2130,2133,2134,1027],{},"用 ",[62,2131,2132],{},"rename"," 把临时链接原子替换成 ",[62,2135,187],{},[110,2137,2138,2139,2141,2142,2144],{},"对访问者来说，某个请求要么读取旧 release，要么读取新 release，不会遇到 ",[62,2140,187],{}," 暂时不存在的中间状态。静态 Node 进程也不需要重启，因为它每次请求都从 ",[62,2143,187],{}," 路径解析文件。",[110,2146,2147,2150,2151,2154,2155,2158,2159,423,2162,1027],{},[62,2148,2149],{},"PERSONAL_SITE_DATA_ROOT"," 决定 release 根目录。生产不设置时使用 ",[62,2152,2153],{},"~\u002F.local\u002Fshare\u002Fpersonal-site","；测试同步服务显式设置为 ",[62,2156,2157],{},"~\u002F.local\u002Fshare\u002Fpersonal-site-staging","。因此相同的 release 代码可以复用，但两个环境不会碰到对方的 ",[62,2160,2161],{},"releases\u002F",[62,2163,187],{},[110,2165,2166],{},"脚本默认保留最近五个 release，并额外保证当前激活版本不被删掉。由于当前版本有可能是手工回滚到的较旧目录，保留“当前版本”比单纯保留时间最新的五个更重要。",[110,2168,2169,2170,2172],{},"回滚只调用同一个原子激活函数，把 ",[62,2171,187],{}," 指回已存在的 release。它不修改 Git、不重新构建、不删除较新的版本，也不重启生产服务。",[138,2174,2176],{"id":2175},"二十四静态服务器nginx-与-https","二十四、静态服务器、Nginx 与 HTTPS",[110,2178,2179,2182,2183,408,2186,408,2189,423,2192,2195,2196,2198,2199,408,2202,408,2205,2208,2209,2212],{},[62,2180,2181],{},"scripts\u002Fstatic-server.mjs"," 使用 Node 自带的 ",[62,2184,2185],{},"http",[62,2187,2188],{},"fs",[62,2190,2191],{},"stream",[62,2193,2194],{},"zlib","。监听地址、端口和 ",[62,2197,187],{}," 路径分别由 ",[62,2200,2201],{},"PERSONAL_SITE_HOST",[62,2203,2204],{},"PERSONAL_SITE_PORT",[62,2206,2207],{},"PERSONAL_SITE_ROOT"," 控制。两个 systemd unit 都把 host 设为 ",[62,2210,2211],{},"127.0.0.1","，所以 3000 和 3100 不直接接受公网连接。它没有框架中间件，职责非常窄：",[200,2214,2215,2218,2231,2237,2244,2247,2250,2253,2256,2259,2266,2272,2275],{},[203,2216,2217],{},"只接受 GET 和 HEAD，其他方法返回 405；",[203,2219,2220,2221,354,2224,2227,2228,2230],{},"解码 URL 后用 ",[62,2222,2223],{},"resolve",[62,2225,2226],{},"relative"," 检查目标仍在 ",[62,2229,187],{}," 内，阻止目录穿越；",[203,2232,2233,2234,446],{},"请求目录时返回该目录的 ",[62,2235,2236],{},"index.html",[203,2238,2239,2240,2243],{},"找不到文件时返回预生成的 ",[62,2241,2242],{},"404.html"," 和 404 状态；",[203,2245,2246],{},"根据扩展名发送正确的 Content-Type；",[203,2248,2249],{},"对大于等于 1 KiB 的文本资源协商 Brotli 或 gzip；",[203,2251,2252],{},"Brotli 使用质量 4，gzip 使用级别 6，在 CPU 与体积间取中间值；",[203,2254,2255],{},"使用读取流和 pipeline，不把大文件一次性读进内存；",[203,2257,2258],{},"根据文件大小和修改时间生成弱 ETag；",[203,2260,2261,2262,2265],{},"命中 ",[62,2263,2264],{},"If-None-Match"," 时返回 304；",[203,2267,2268,2271],{},[62,2269,2270],{},"_nuxt\u002F"," 哈希资源缓存一年并标记 immutable；",[203,2273,2274],{},"HTML、图片等非哈希路径使用 no-cache，确保 release 切换后会重新验证；",[203,2276,2277],{},"收到 SIGINT 或 SIGTERM 时停止接受新连接并正常退出。",[110,2279,2280,2281,2283],{},"如果 ",[62,2282,187],{}," 不可用，服务器返回 503，而不是把文件系统错误暴露给访问者。若响应流已经开始后出错，则销毁连接，避免发送一半文件后再拼接错误文本。",[110,2285,2286],{},"它不会处理 TLS、OAuth 或反向代理。公网入口由 Nginx 负责：",[149,2288,2291],{"className":2289,"code":2290,"language":154,"meta":155},[152],"quilibra.cn \u002F www.quilibra.cn -> HTTPS -> 127.0.0.1:3000\ndev.quilibra.cn               -> HTTPS + Basic Auth -> 127.0.0.1:3100\n公网 IP或未知 Host             -> 默认站点拒绝\n",[62,2292,2290],{"__ignoreMap":155},[110,2294,2295,2296,2299,2300,2303],{},"根域名和 ",[62,2297,2298],{},"www"," 共用正式站，证书由 Certbot 申请并续期；测试子域名使用自己的证书和密码文件。Nginx 把真实 Host、客户端地址和转发协议传给上游，测试站额外发送 ",[62,2301,2302],{},"X-Robots-Tag: noindex, nofollow, noarchive","。腾讯云安全组只需要开放 80 和 443，3000、3100 不对公网放行。",[110,2305,2306,2307,2310,2311,2314],{},"页脚备案号来自全站设置 ",[62,2308,2309],{},"content\u002Fsettings\u002Fsite.yml","，当前 ICP 号链接到 ",[62,2312,2313],{},"https:\u002F\u002Fbeian.miit.gov.cn\u002F","。备案展示、DNS、TLS 证书和反向代理是四个不同层次：备案号不会自动配置 HTTPS，证书也不会自动创建 DNS 记录。",[138,2316,2318],{"id":2317},"二十五300031003101-分别是什么","二十五、3000、3100、3101 分别是什么",[235,2320,2321,2334],{},[238,2322,2323],{},[241,2324,2325,2328,2331],{},[244,2326,2327],{},"端口",[244,2329,2330],{},"用途",[244,2332,2333],{},"生命周期",[251,2335,2336,2347,2359],{},[241,2337,2338,2341,2344],{},[256,2339,2340],{},"3000",[256,2342,2343],{},"当前生产静态 release",[256,2345,2346],{},"systemd 长期运行",[241,2348,2349,2352,2357],{},[256,2350,2351],{},"3100",[256,2353,2354,2356],{},[62,2355,124],{}," 当前静态 release",[256,2358,2346],{},[241,2360,2361,2364,2367],{},[256,2362,2363],{},"3101",[256,2365,2366],{},"Playwright 隔离测试服务",[256,2368,2369],{},"测试自动启动并结束",[110,2371,2372,2373,2376,2377,2380],{},"3100 过去曾运行当前工作区的 Nuxt Dev，容易受首次编译、热更新和 Nuxt Content 缓存损坏影响。现在它与 3000 一样只运行静态服务器，从独立 staging release 读取文件，空闲常驻约几十 MB，并由 ",[62,2374,2375],{},"https:\u002F\u002Fdev.quilibra.cn"," 提供远程预览。需要真正的热更新时，在开发机或 VPS 的另一个空闲端口临时运行 ",[62,2378,2379],{},"npm run dev","，不要覆盖常驻 3100。",[110,2382,2383,2384,2386],{},"3101 使用独立内容 fixture 和 ",[62,2385,488],{},"，不能拿它当日常预览端口。生产 3000、预发布 3100 和测试 3101 可以同时存在，因为 release、构建目录和端口都隔离。",[138,2388,2390],{"id":2389},"二十六systemd-用户服务","二十六、systemd 用户服务",[110,2392,2393],{},"当前有五个相关 unit：",[149,2395,2398],{"className":2396,"code":2397,"language":154,"meta":155},[152],"personal-site.service                 3000 生产静态服务\npersonal-site-dev.service             3100 staging 静态服务\npersonal-site-webhook.service         3110 签名 Webhook 接收与事件排队\npersonal-site-sync.service            master 单次同步与部署\npersonal-site-staging-sync.service    staging 单次同步与部署\n",[62,2399,2397],{"__ignoreMap":155},[110,2401,2402],{},"两个静态服务的关键设置包括：",[200,2404,2405,2411,2417,2423,2429,2438],{},[203,2406,2407,2410],{},[62,2408,2409],{},"Type=simple","：Node 进程本身就是主进程；",[203,2412,2413,2416],{},[62,2414,2415],{},"After\u002FWants=network-online.target","：网络就绪后启动；",[203,2418,2419,2422],{},[62,2420,2421],{},"Restart=on-failure"," 与 5 秒延迟：异常退出后自动恢复；",[203,2424,2425,2428],{},[62,2426,2427],{},"KillSignal=SIGINT","：与静态服务器的优雅关闭逻辑对应；",[203,2430,2431,408,2434,2437],{},[62,2432,2433],{},"NoNewPrivileges=true",[62,2435,2436],{},"PrivateTmp=true","：缩小运行时权限；",[203,2439,2440,2441,2444],{},"安装到用户级 ",[62,2442,2443],{},"default.target","，不要求整个服务以 root 运行。",[110,2446,2447,2448,2450,2451,2454,2455,354,2458,2460,2461,2464],{},"Webhook 服务是 ",[62,2449,2409],{}," 常驻轻量进程，只监听回环地址；两个同步服务仍是 ",[62,2452,2453],{},"Type=oneshot","，仅在收到对应分支的 push 后运行。生产和测试分别拥有独立 ",[62,2456,2457],{},"sync-repo",[62,2459,1994],{},"，但用同一个 ",[62,2462,2463],{},"\u002Fhome\u002Fuser\u002F.local\u002Fstate\u002Fpersonal-site-deploy.lock"," 串行构建。仓库没有周期性 GitHub timer；有事件时才 fetch，依赖锁变化时才安装依赖，纯内容提交使用快速构建。",[110,2466,2467,2468,2471,2472,2475],{},"unit 当前把 Node 可执行文件写成 NVM 下的明确路径。升级 Node 或切换 NVM 版本后，shell 中的 ",[62,2469,2470],{},"node"," 可能已经变化，但 systemd 仍引用旧路径，这是需要主动检查的运维陷阱。修改 unit 后必须 ",[62,2473,2474],{},"daemon-reload","，而普通文章或页面 release 不需要重启静态服务。",[110,2477,2478,2479,2482,2483,2486],{},"用户级服务要在 VPS 重启且用户未登录时自动启动，需要为运行网站的 Linux 用户启用 linger。管理它时应使用相同用户的 ",[62,2480,2481],{},"systemctl --user","，不应使用 ",[62,2484,2485],{},"sudo systemctl --user"," 去连接 root 的用户总线。",[138,2488,2490],{"id":2489},"二十七测试体系究竟覆盖什么","二十七、测试体系究竟覆盖什么",[110,2492,2493],{},"测试按风险边界分成三组。",[1915,2495,2497],{"id":2496},"内容和-writer-单元测试","内容和 writer 单元测试",[110,2499,2500,2503],{},[62,2501,2502],{},"test:content"," 检查：",[200,2505,2506,2509,2512,2515,2518,2523,2526,2529,2532],{},[203,2507,2508],{},"合法文章和四种分类可以通过；",[203,2510,2511],{},"非法日期、分类、文件名、重复标签和空正文会失败；",[203,2513,2514],{},"来源节必须包含公共 HTTP(S) 链接；",[203,2516,2517],{},"CMS 可选 slug 必须与文件名一致；",[203,2519,2520,2522],{},[62,2521,1598],{}," 只能是布尔值，writer 更新时默认保留已有隐藏状态；",[203,2524,2525],{},"站点 YAML 满足 Schema；",[203,2527,2528],{},"writer 拒绝覆盖、穿越、符号链接和错误修订日期；",[203,2530,2531],{},"更新保留发布日期；",[203,2533,2534],{},"删除只接受精确存在的 dated slug。",[1915,2536,2538],{"id":2537},"release-单元测试","release 单元测试",[110,2540,2541,2544],{},[62,2542,2543],{},"test:release"," 在临时目录中检查：",[200,2546,2547,2552,2555,2558,2561,2564,2567,2570,2573,2576],{},[203,2548,2549,2551],{},[62,2550,187],{}," 能原子切换；",[203,2553,2554],{},"清理历史版本时保留激活 release；",[203,2556,2557],{},"release 名称不能路径穿越；",[203,2559,2560],{},"受管内容路径不会扩大到应用代码；",[203,2562,2563],{},"自动同步失败后不写成功状态，后续重试仍会部署；",[203,2565,2566],{},"Webhook 拒绝无效签名、其他仓库和其他分支；",[203,2568,2569],{},"构建期间到达的第二个 push 会保留并追加同步；",[203,2571,2572],{},"纯内容提交走快速构建，代码提交走完整构建；",[203,2574,2575],{},"staging 未成功部署时不能晋级，晋级后 master 与 staging 原子对齐；",[203,2577,2578],{},"子命令失败时错误信息保留退出码。",[1915,2580,2582],{"id":2581},"playwright-浏览器测试","Playwright 浏览器测试",[110,2584,2585,2588],{},[62,2586,2587],{},"test:e2e"," 在 Chromium 中检查：",[200,2590,2591,2594,2597,2600,2603,2606,2609,2612,2615,2618,2621,2624],{},[203,2592,2593],{},"首页成功水合且没有控制台错误；",[203,2595,2596],{},"动态 favicon、theme-color 和 RGB 输出同步；",[203,2598,2599],{},"自定义主题在纯黑纯白下仍可读；",[203,2601,2602],{},"旧黄色缓存会迁移；",[203,2604,2605],{},"mask 没加载完时字标不偷跑，加载后正常播放；",[203,2607,2608],{},"标签页激活、窗口 focus、换色、滑杆松手和返回主页会重播；",[203,2610,2611],{},"拖动期间不会反复播放；",[203,2613,2614],{},"服务状态、NAS 图标、最近文章、文章详情和更新日期正确；",[203,2616,2617],{},"隐藏文章不出现在公开查询中，原 URL 返回 404；",[203,2619,2620],{},"文章悬浮只影响当前行，长标题宽度不跳变；",[203,2622,2623],{},"手机页面无横向溢出，RGB 在小屏隐藏；",[203,2625,2626],{},"多种桌面尺寸下关键模块互不重叠。",[110,2628,2629],{},"Playwright 配置允许一次重试，等待时间 10 秒，临时 Nuxt 服务启动最长等待 120 秒。响应式几何测试启用 reduced motion，避免入场动画中的瞬时位置影响布局判断。",[110,2631,2632],{},"测试不是越多越好。这里保留的是曾经真实出错、或者一旦回归就很难靠类型系统发现的行为；字体文件数量、每个 CSS 颜色和每一段正文不需要分别写测试。",[138,2634,2636],{"id":2635},"二十八当前性能画像与做过的优化","二十八、当前性能画像与做过的优化",[110,2638,2639],{},"在当前版本的一次本机测量中：",[200,2641,2642,2645,2648,2651,2654],{},[203,2643,2644],{},"主 CSS 约 31.1 KB，gzip 后约 6.9 KB；",[203,2646,2647],{},"外部或本地字体文件由 98 个减少到 0；",[203,2649,2650],{},"首页一次传输约 252 KB；",[203,2652,2653],{},"模拟 4G 和 4 倍 CPU 降速时，LCP 约 0.75 秒，完整 load 约 1.37 秒；",[203,2655,2656],{},"CLS 为 0。",[110,2658,2659,2660,2663],{},"VPS 空闲状态下，生产与测试两个 Node 静态服务合计约 50 MB；Webhook 接收器也是单一轻量 Node 进程，不执行构建。整机仍有约 1.1 GB 可用内存。完整 Nuxt + Playwright 部署的峰值接近 1 GB，但两个同步器通过共享锁串行执行；内容快速构建不启动 Playwright。生产、测试和开发三份仓库合计约 1.5 GB，主要空间来自各自的 ",[62,2661,2662],{},"node_modules","；这是用磁盘换取工作区、缓存和发布故障互不污染。",[110,2665,2666],{},"这些数字是环境相关快照，不是永久承诺。浏览器缓存、网络、CPU、文章数量和依赖升级都会改变结果。",[110,2668,2669],{},"当前主要性能措施包括：",[200,2671,2672,2675,2678,2681,2688,2693,2696,2699,2702,2705,2708],{},[203,2673,2674],{},"静态预渲染，HTML 首次响应包含页面内容；",[203,2676,2677],{},"移除大批字体资源，改用系统字体；",[203,2679,2680],{},"只预加载首屏真正依赖的 30 KB 字标 mask；",[203,2682,2683,2684,2687],{},"字标等待 ",[62,2685,2686],{},"decode()","，避免资源未准备好时动画丢失；",[203,2689,2690,2692],{},[62,2691,887],{}," 合并高频指针更新；",[203,2694,2695],{},"缓存元素边界，避免每次 pointermove 读取布局；",[203,2697,2698],{},"连续调色只改颜色，松手才重播动画；",[203,2700,2701],{},"用 transform\u002Fopacity 处理大多数动效；",[203,2703,2704],{},"生产服务器流式响应并支持 Brotli、gzip、ETag 和长期哈希缓存；",[203,2706,2707],{},"固定组件尺寸，避免动态文本造成布局偏移；",[203,2709,2710],{},"分离本地开发、生产同步、测试同步和 E2E 的工作目录与 Nuxt Content 数据库。",[110,2712,2713,2715],{},[62,2714,183],{}," 整体约 5.4 MB，其中包括 Nuxt Content 的客户端 SQLite\u002FWASM 相关资源；这不等于每个访客首屏都会下载整个目录。分析性能时应看浏览器实际请求和传输体积，不应把产物目录大小直接当作首页流量。",[138,2717,2719],{"id":2718},"二十九为什么首次交互偶尔会过一会儿才正常","二十九、为什么首次交互偶尔会“过一会儿才正常”",[110,2721,2722],{},"这个问题曾同时表现为字标不播放和 RGB 滑杆暂时失灵，根因方向不是滑杆 CSS 本身，而是客户端水合尚未完成或 Nuxt Dev 仍在首次编译。静态 HTML 可以先显示，但 Vue 事件只有在 JavaScript 加载、执行并成功水合后才生效。",[110,2724,2725,2726,2729],{},"判断方法是观察 ",[62,2727,2728],{},".site-shell[data-hydrated=\"true\"]","。如果页面已经显示但该属性迟迟没有变成 true，应检查：",[200,2731,2732,2735,2738,2741,2744],{},[203,2733,2734],{},"浏览器控制台是否有 JavaScript 错误；",[203,2736,2737],{},"本地 Nuxt Dev 是否还在首次编译；",[203,2739,2740],{},"本地内容 SQLite 缓存是否损坏；",[203,2742,2743],{},"大型依赖或资源是否阻塞客户端入口；",[203,2745,2746],{},"当前访问的是临时 Nuxt Dev，还是已经生成的 3000\u002F3100 静态站。",[110,2748,2749],{},"目前通过移除字体、减轻入口资源、预加载 mask、分离内容数据库和明确动画水合闸门改善了这个过程。远程测试站也已经改成静态 release，不再边访问边编译，因此它与正式站具有相同的水合特征。",[138,2751,2753],{"id":2752},"三十常见修改应该改哪里","三十、常见修改应该改哪里",[1915,2755,2757],{"id":2756},"修改个人介绍栏目文案或服务","修改个人介绍、栏目文案或服务",[110,2759,2760,2761,2763,2764,2766],{},"优先通过 CMS 中对应的页面入口，或编辑 ",[62,2762,418],{}," 下对应 YAML；服务列表位于 ",[62,2765,617],{},"。若新增服务状态或图标类型，还要同步修改 Schema、配置枚举、CMS 选项、组件映射、CSS 和测试。",[1915,2768,2769],{"id":2769},"修改默认颜色",[110,2771,2772,2773,2775,2776,2778],{},"修改 ",[62,2774,1004],{},"。它可以是任意 ",[62,2777,664],{},"。已有访客若保存过自定义颜色，会继续看到自己的选择；要做全体迁移，需要像旧黄色迁移一样明确识别并处理旧值，不能无条件清空 localStorage。",[1915,2780,2781],{"id":2781},"修改红绿蓝预设",[110,2783,2772,2784,2786,2787,2790],{},[62,2785,939],{}," 的 ",[62,2788,2789],{},"accentPresets","。RGB 面板和非主页调色按钮都复用它。修改后应测试纯黑纯白对比、localStorage 恢复和字标重播。",[1915,2792,2793],{"id":2793},"修改艺术字",[110,2795,2796,2797,2786,2799,2801,2802,2804,2805,1027],{},"在 CMS 中打开“站点设置 \u002F 首页”，修改“艺术字文字”即可。它最终写入 ",[62,2798,597],{},[62,2800,1215],{},"，保存到测试分支后由 Webhook 自动重新构建测试站。字形由 ",[62,2803,436],{}," 引入，字体栈、字号和四层颜色位于 ",[62,2806,2807],{},"main.css",[1915,2809,2810],{"id":2810},"修改字标动画",[110,2812,2813,2814,2816,2817,2820,2821,2823],{},"播放条件在 ",[62,2815,1262],{},"，跨组件触发在 ",[62,2818,2819],{},"useWordmarkAnimation.ts","，视觉关键帧在 ",[62,2822,2807],{},"。不要把三者混成一个大组件：字体状态、事件状态和视觉时间线是不同责任。",[1915,2825,2826],{"id":2826},"修改文章分类",[110,2828,2829,2830,2833],{},"至少同步 ",[62,2831,2832],{},"app\u002Fconfig\u002Farticles.ts","、Nuxt Content Schema、CMS 选项、校验器测试与界面测试。文章中的旧分类是否迁移，需要单独决定。",[1915,2835,2836],{"id":2836},"修改文章样式",[110,2838,2839,2840,2843,2844,2846,2847,2850],{},"文章列表看 ",[62,2841,2842],{},".article-row","，正文看 ",[62,2845,1543],{},"，详情头部看 ",[62,2848,2849],{},".article-hero","。长标题、手机宽度、hover 前后几何和其他文章 opacity 是必须复查的回归点。",[1915,2852,2853],{"id":2853},"修改生产或测试服务",[110,2855,2856,2857,2859],{},"静态响应逻辑改 ",[62,2858,2181],{},"；监听端口、release 根目录、Node 路径和重启策略改对应 systemd unit。同步分支、代理、独立仓库和部署锁改两个 sync unit。只有静态服务器或 unit 变化需要重启\u002F重载，普通 release 切换不需要。",[138,2861,2863],{"id":2862},"三十一日常发布配方","三十一、日常发布配方",[1915,2865,2867],{"id":2866},"通过远程-cms-修改内容","通过远程 CMS 修改内容",[380,2869,2870,2876,2879,2884],{},[203,2871,2872,2873,2875],{},"打开 ",[62,2874,1800],{}," 并保存。",[203,2877,2878],{},"等待 Webhook 触发 staging 构建，检查测试站主页、列表和具体 URL。",[203,2880,2881,2882,1027],{},"在干净且最新的 master 工作区运行 ",[62,2883,1786],{},[203,2885,2886],{},"等待 master Webhook 触发生产构建，再检查正式站。",[110,2888,2889],{},"保存、预览、晋级和正式部署是四个明确阶段。隐藏文章也遵循同一流程。",[1915,2891,2892],{"id":2892},"本地受信流程直接发布内容",[149,2894,2898],{"className":2895,"code":2896,"language":2897,"meta":155,"style":155},"language-bash shiki shiki-themes github-light github-dark","npm run content:validate\nnpm run publish\n","bash",[62,2899,2900,2911],{"__ignoreMap":155},[773,2901,2902,2905,2908],{"class":775,"line":776},[773,2903,2904],{"class":787},"npm",[773,2906,2907],{"class":794}," run",[773,2909,2910],{"class":794}," content:validate\n",[773,2912,2913,2915,2917],{"class":775,"line":962},[773,2914,2904],{"class":787},[773,2916,2907],{"class":794},[773,2918,2919],{"class":794}," publish\n",[110,2921,2922,2924],{},[62,2923,2075],{}," 成功后会输出 commit、release 和 current 路径。它用于 OpenClaw 等本地受限流程，会直接切换默认生产 release；是否推送 GitHub 是另一项操作。普通远程 CMS 编辑不要使用这条路径绕过 staging。",[1915,2926,2928],{"id":2927},"发布-vuecss脚本或配置代码","发布 Vue、CSS、脚本或配置代码",[149,2930,2932],{"className":2895,"code":2931,"language":2897,"meta":155,"style":155},"npm run typecheck\nnpm run test:content\nnpm run test:release\nnpm run test:e2e\nnpm run generate\ngit diff --check\n# 明确检查并提交本次文件\ngit push origin master\n",[62,2933,2934,2943,2952,2961,2970,2979,2990,2996],{"__ignoreMap":155},[773,2935,2936,2938,2940],{"class":775,"line":776},[773,2937,2904],{"class":787},[773,2939,2907],{"class":794},[773,2941,2942],{"class":794}," typecheck\n",[773,2944,2945,2947,2949],{"class":775,"line":962},[773,2946,2904],{"class":787},[773,2948,2907],{"class":794},[773,2950,2951],{"class":794}," test:content\n",[773,2953,2954,2956,2958],{"class":775,"line":35},[773,2955,2904],{"class":787},[773,2957,2907],{"class":794},[773,2959,2960],{"class":794}," test:release\n",[773,2962,2963,2965,2967],{"class":775,"line":985},[773,2964,2904],{"class":787},[773,2966,2907],{"class":794},[773,2968,2969],{"class":794}," test:e2e\n",[773,2971,2972,2974,2976],{"class":775,"line":995},[773,2973,2904],{"class":787},[773,2975,2907],{"class":794},[773,2977,2978],{"class":794}," generate\n",[773,2980,2981,2984,2987],{"class":775,"line":1606},[773,2982,2983],{"class":787},"git",[773,2985,2986],{"class":794}," diff",[773,2988,2989],{"class":972}," --check\n",[773,2991,2992],{"class":775,"line":1617},[773,2993,2995],{"class":2994},"sJ8bj","# 明确检查并提交本次文件\n",[773,2997,2998,3000,3003,3006],{"class":775,"line":1626},[773,2999,2983],{"class":787},[773,3001,3002],{"class":794}," push",[773,3004,3005],{"class":794}," origin",[773,3007,3008],{"class":794}," master\n",[110,3010,3011,3012,3014,3015,3017],{},"master Webhook 会触发同步器再次执行完整 ",[62,3013,1938],{},"，前面的命令适合在提交前尽早发现问题。需要在 VPS 立即部署当前干净提交时也可以手工运行 ",[62,3016,1938],{},"。代码版本只进入 master 时，测试站可能暂时仍运行上一份代码；下一次内容晋级会把最新 master 与 staging 合并并重新对齐两边。",[1915,3019,3020],{"id":3020},"查看当前生产状态",[149,3022,3024],{"className":2895,"code":3023,"language":2897,"meta":155,"style":155},"systemctl --user is-active personal-site.service\nsystemctl --user is-active personal-site-dev.service\nreadlink -f ~\u002F.local\u002Fshare\u002Fpersonal-site\u002Fcurrent\nreadlink -f ~\u002F.local\u002Fshare\u002Fpersonal-site-staging\u002Fcurrent\ncurl -fsS -o \u002Fdev\u002Fnull -w '%{http_code} %{content_type}\\n' http:\u002F\u002F127.0.0.1:3000\u002F\ncurl -fsS -o \u002Fdev\u002Fnull -w '%{http_code} %{content_type}\\n' http:\u002F\u002F127.0.0.1:3100\u002F\n",[62,3025,3026,3040,3051,3062,3071,3094],{"__ignoreMap":155},[773,3027,3028,3031,3034,3037],{"class":775,"line":776},[773,3029,3030],{"class":787},"systemctl",[773,3032,3033],{"class":972}," --user",[773,3035,3036],{"class":794}," is-active",[773,3038,3039],{"class":794}," personal-site.service\n",[773,3041,3042,3044,3046,3048],{"class":775,"line":962},[773,3043,3030],{"class":787},[773,3045,3033],{"class":972},[773,3047,3036],{"class":794},[773,3049,3050],{"class":794}," personal-site-dev.service\n",[773,3052,3053,3056,3059],{"class":775,"line":35},[773,3054,3055],{"class":787},"readlink",[773,3057,3058],{"class":972}," -f",[773,3060,3061],{"class":794}," ~\u002F.local\u002Fshare\u002Fpersonal-site\u002Fcurrent\n",[773,3063,3064,3066,3068],{"class":775,"line":985},[773,3065,3055],{"class":787},[773,3067,3058],{"class":972},[773,3069,3070],{"class":794}," ~\u002F.local\u002Fshare\u002Fpersonal-site-staging\u002Fcurrent\n",[773,3072,3073,3076,3079,3082,3085,3088,3091],{"class":775,"line":995},[773,3074,3075],{"class":787},"curl",[773,3077,3078],{"class":972}," -fsS",[773,3080,3081],{"class":972}," -o",[773,3083,3084],{"class":794}," \u002Fdev\u002Fnull",[773,3086,3087],{"class":972}," -w",[773,3089,3090],{"class":794}," '%{http_code} %{content_type}\\n'",[773,3092,3093],{"class":794}," http:\u002F\u002F127.0.0.1:3000\u002F\n",[773,3095,3096,3098,3100,3102,3104,3106,3108],{"class":775,"line":1606},[773,3097,3075],{"class":787},[773,3099,3078],{"class":972},[773,3101,3081],{"class":972},[773,3103,3084],{"class":794},[773,3105,3087],{"class":972},[773,3107,3090],{"class":794},[773,3109,3110],{"class":794}," http:\u002F\u002F127.0.0.1:3100\u002F\n",[1915,3112,3113],{"id":3113},"回滚",[110,3115,3116],{},"先列出确实存在的 release，再执行：",[149,3118,3120],{"className":2895,"code":3119,"language":2897,"meta":155,"style":155},"npm run rollback -- \u003Cexact-release-id>\n",[62,3121,3122],{"__ignoreMap":155},[773,3123,3124,3126,3128,3131,3134,3137,3140,3143],{"class":775,"line":776},[773,3125,2904],{"class":787},[773,3127,2907],{"class":794},[773,3129,3130],{"class":794}," rollback",[773,3132,3133],{"class":972}," --",[773,3135,3136],{"class":952}," \u003C",[773,3138,3139],{"class":794},"exact-release-i",[773,3141,3142],{"class":779},"d",[773,3144,806],{"class":952},[110,3146,3147,3148,3150,3151,3153,3154,1027],{},"回滚后再次检查 ",[62,3149,187],{}," 和 3000 返回值。不要手动删除 ",[62,3152,187],{},"，也不要把它指向仓库的 ",[62,3155,183],{},[138,3157,3159],{"id":3158},"三十二故障排查顺序","三十二、故障排查顺序",[1915,3161,3163],{"id":3162},"cms-保存后测试站看不到","CMS 保存后测试站看不到",[110,3165,3166,3167,3169,3170,3172,3173,408,3176,3179,3180,3183,3184,3186],{},"先确认提交是否进入 GitHub ",[62,3168,124],{},"，并在 GitHub Recent Deliveries 中确认 Webhook 返回 ",[62,3171,2035],{},"；再检查 ",[62,3174,3175],{},"personal-site-webhook.service",[62,3177,3178],{},"personal-site-staging-sync.service"," 日志、",[62,3181,3182],{},"~\u002F.local\u002Fstate\u002Fpersonal-site-staging-sync\u002Fdeployed-commit"," 和测试 ",[62,3185,187],{},"。构建失败不会覆盖旧预览；事件文件会保留并延迟重试，也可以修复后手工启动同步服务。",[1915,3188,3189],{"id":3189},"测试站正确但正式站没变化",[110,3191,3192,3193,3195,3196,3198,3199,3202,3203,3205],{},"确认是否实际运行过 ",[62,3194,1786],{},"、远端 master 是否已更新以及 master Webhook 是否返回 ",[62,3197,2035],{},"，再检查 ",[62,3200,3201],{},"personal-site-sync.service"," 和生产 ",[62,3204,1994],{},"。CMS 保存只更新测试站是正常设计，不是同步故障。",[1915,3207,3209],{"id":3208},"rgb-不能拖按钮不能点动画不播放","RGB 不能拖、按钮不能点、动画不播放",[110,3211,3212,3213,3216,3217,3220],{},"先看 ",[62,3214,3215],{},"data-hydrated","，再看控制台错误和网络请求。若只发生在临时 Nuxt Dev 的第一次编译，等待完成后重载；若持续发生，停止那个临时开发进程，运行 ",[62,3218,3219],{},"npx nuxt cleanup"," 后重启开发端口。常驻 3000 和 3100 都是静态服务器，不使用这份开发缓存。",[1915,3222,3223],{"id":3223},"切回标签页没有动画",[110,3225,3226,3227,408,3229,3231],{},"检查浏览器是否启用 reduced motion、mask 图片是否加载失败，以及 ",[62,3228,1348],{},[62,3230,1351],{}," 是否触发。一次切回只播一次是正常防抖，不应去掉 200ms 合并后让三个事件连续触发。",[1915,3233,3234],{"id":3234},"换色先闪回旧颜色",[110,3236,3237,3238,3240],{},"检查静态默认色、Nuxt ",[62,3239,1008],{}," 初始值和 localStorage 恢复顺序。服务端 HTML无法知道客户端保存颜色，完全避免首帧差异需要在页面渲染前执行极小的内联主题恢复脚本；当前实现选择保持静态生成简单，在水合时恢复。",[1915,3242,3244],{"id":3243},"服务状态点不随-cms-变化","服务状态点不随 CMS 变化",[110,3246,3247,3248,3251,3252,3254],{},"确认 YAML 中 ",[62,3249,3250],{},"state"," 已保存、值属于三个枚举，并检查正在看的环境：CMS 保存后测试站应先变化，正式站要等 promote。若 DOM 属性已变而颜色没变，检查 CSS 状态选择器；若属性没变，检查对应环境的 ",[62,3253,187],{}," 是否仍是旧 release。",[1915,3256,3258],{"id":3257},"_3000-无法访问","3000 无法访问",[110,3260,3261,3262,3264,3265,3267],{},"检查用户级 systemd 状态、日志、Node 固定路径、端口占用和 ",[62,3263,187],{}," 链接。服务器进程存在但 ",[62,3266,187],{}," 失效时会返回 503。不要在未确认进程归属前直接杀端口进程。",[110,3269,3270,3271,3274,3275,3277],{},"3100 使用相同排查方法，但服务是 ",[62,3272,3273],{},"personal-site-dev.service","，数据根目录是 ",[62,3276,2157],{},"。正式站正常而测试站异常时，不要重启 3000。",[1915,3279,3281],{"id":3280},"管理页面提示只允许-https","管理页面提示只允许 HTTPS",[110,3283,3284,3285,3287],{},"这通常来自浏览器安全上下文要求，而不是 Git remote 配错。Git remote 只决定仓库同步地址，不会给 HTTP 页面增加 HTTPS。远程后台应使用已经配置可信证书和 Basic Auth 的 ",[62,3286,1800],{},"；本地仓库模式则从 localhost 打开。",[138,3289,3291],{"id":3290},"三十三安全与备份边界","三十三、安全与备份边界",[110,3293,3294],{},"至少要区分三类数据：",[149,3296,3299],{"className":3297,"code":3298,"language":154,"meta":155},[152],"Git 仓库       源码、文章、站点设置、公开静态资源\nOpenClaw 目录  Skill、草稿、会话和本地配置\nrelease 目录   生产与测试当前及历史静态构建结果\n",[62,3300,3298],{"__ignoreMap":155},[110,3302,3303],{},"Git 提交是版本历史，不是异地备份。私人远程仓库可以备份源码与文章，但 OpenClaw 配置和 release 数据需要单独备份到受控存储。",[110,3305,3306,3307,3309],{},"永远不要提交或写入公开文章的内容包括：API key、OAuth secret、部署私钥、QQ token、cookie、扫码状态、",[62,3308,1793],{},"、私人服务密码、内部地址清单和不适合公开的资产信息。",[110,3311,3312,3313,3316,3317,3319],{},"CMS 的 ",[62,3314,3315],{},"\u002Fadmin\u002F"," 路径和 ",[62,3318,1804],{}," 只能减少搜索引擎收录，不能阻止陌生人访问。当前远程后台同时依靠可信 HTTPS、Nginx Basic Auth 和 GitHub OAuth；生产\u002F测试上游只监听回环地址，公网 IP 与未知 Host 由默认 Nginx 站点拒绝。真正的保护来自这些边界和最小权限凭据，而不是隐藏 URL。",[138,3321,3323],{"id":3322},"三十四维护这套代码时应坚持的原则","三十四、维护这套代码时应坚持的原则",[110,3325,3326],{},"第一，内容、表现和发布边界分开。能在 YAML 解决的文案不要硬编码进 Vue；能由共享组件表达的状态不要在两个页面复制；生产切换不要混进页面代码。",[110,3328,3329],{},"第二，把浏览器状态当成显式状态。主题色、资源是否解码、页面是否可见、指针是否仍按下、水合是否完成，都应有清楚的变量和生命周期，而不是靠延时猜测。",[110,3331,3332],{},"第三，动画必须有静态最终状态。资源失败、JavaScript 失败或用户减少动态效果时，标题仍要可读，布局仍要成立。",[110,3334,3335],{},"第四，自动化写入必须比人工编辑权限更窄。Skill 负责工作流，writer 负责硬边界，validator 负责内容合约，release 负责上线，四者不能互相冒充。",[110,3337,3338],{},"第五，生产部署应该是“生成一个完整新版本，然后切换”，而不是在访问者正在读取的目录里逐个覆盖文件。原子 release 是这个小网站最值得长期保留的工程设计之一。",[110,3340,3341,3342,3344],{},"第六，测试站和正式站必须由分支、仓库、release 与进程共同隔离。域名不同不等于环境隔离，CMS 保存也不等于生产发布；",[62,3343,128],{}," 是有校验的明确边界。",[110,3346,3347],{},"第七，测试真实的关系和历史 bug。主题对比、滑杆松手、标签页重播、长标题 hover、隐藏文章 404、服务状态同步和多视口不重叠，都来自实际使用中的问题，比为了提高数字而堆大量脆弱测试更有价值。",[138,3349,3351],{"id":3350},"三十五重新上手时的最短阅读路径","三十五、重新上手时的最短阅读路径",[110,3353,3354],{},"如果未来很久没有维护这个项目，按下面顺序读，可以最快恢复上下文：",[380,3356,3357,3363,3371,3380,3386,3391,3403,3406],{},[203,3358,3359,3360,3362],{},"看 ",[62,3361,418],{}," 的五份 YAML，知道全站和各页面当前展示什么。",[203,3364,3359,3365,423,3368,3370],{},[62,3366,3367],{},"app\u002Fpages\u002Findex.vue",[62,3369,2807],{}," 的主页部分，理解首屏结构。",[203,3372,3359,3373,408,3375,408,3377,3379],{},[62,3374,939],{},[62,3376,1124],{},[62,3378,1262],{},"，理解最复杂的交互。",[203,3381,3382,3383,3385],{},"看两个 writing 页面和 ",[62,3384,505],{},"，理解文章查询与渲染。",[203,3387,3359,3388,3390],{},[62,3389,655],{},"、validator 和 writer，理解内容边界。",[203,3392,3359,3393,408,3396,408,3399,3402],{},[62,3394,3395],{},"release.ts",[62,3397,3398],{},"sync.ts",[62,3400,3401],{},"promote.ts"," 和 systemd unit，理解双环境发布链路。",[203,3404,3405],{},"看 Nginx 模板与运维文档，理解域名、HTTPS、Basic Auth 和回环上游。",[203,3407,3408],{},"最后看测试，把“哪些行为不能再坏”快速过一遍。",[110,3410,3411],{},"只要能重新回答下面五个问题，就已经掌握了这个项目：",[200,3413,3414,3417,3420,3423,3426,3429],{},[203,3415,3416],{},"当前页面内容的唯一来源在哪里？",[203,3418,3419],{},"浏览器中的主题色如何从 RGB 状态传到 CSS、字标和 favicon？",[203,3421,3422],{},"为什么 CMS 保存后只改变测试站？",[203,3424,3425],{},"publish、deploy、sync 和 promote 的权限、检查与 Git 行为有什么不同？",[203,3427,3428],{},"一个新 release 如何在不重启 3000 的情况下接管生产请求？",[203,3430,3431],{},"3000 与 3100 为什么不会覆盖彼此的仓库和 current？",[110,3433,3434],{},"这些问题就是 Quilibra 当前技术架构的骨架。",[138,3436,3437],{"id":3437},"来源与延伸阅读",[200,3439,3440,3449,3456,3463,3470,3477,3484,3491],{},[203,3441,3442],{},[3443,3444,3448],"a",{"href":3445,"rel":3446},"https:\u002F\u002Fnuxt.com\u002Fdocs",[3447],"nofollow","Nuxt 文档",[203,3450,3451],{},[3443,3452,3455],{"href":3453,"rel":3454},"https:\u002F\u002Fcontent.nuxt.com\u002F",[3447],"Nuxt Content 文档",[203,3457,3458],{},[3443,3459,3462],{"href":3460,"rel":3461},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fextras\u002Fcomposition-api-faq.html",[3447],"Vue Composition API",[203,3464,3465],{},[3443,3466,3469],{"href":3467,"rel":3468},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Fintro",[3447],"Playwright 文档",[203,3471,3472],{},[3443,3473,3476],{"href":3474,"rel":3475},"https:\u002F\u002Fgithub.com\u002Fsveltia\u002Fsveltia-cms",[3447],"Sveltia CMS 文档",[203,3478,3479],{},[3443,3480,3483],{"href":3481,"rel":3482},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002Fmask",[3447],"MDN：CSS mask",[203,3485,3486],{},[3443,3487,3490],{"href":3488,"rel":3489},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FPage_Visibility_API",[3447],"MDN：Page Visibility API",[203,3492,3493],{},[3443,3494,3497],{"href":3495,"rel":3496},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FCaching",[3447],"MDN：HTTP 缓存",[3499,3500,3501],"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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":155,"searchDepth":962,"depth":962,"links":3503},[3504,3505,3506,3507,3508,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3532,3533,3534,3535,3536,3541,3542,3543,3553,3560,3570,3571,3572,3573],{"id":140,"depth":962,"text":141},{"id":194,"depth":962,"text":195},{"id":229,"depth":962,"text":230},{"id":365,"depth":962,"text":366},{"id":430,"depth":962,"text":431},{"id":562,"depth":962,"text":563},{"id":707,"depth":962,"text":708},{"id":823,"depth":962,"text":824},{"id":894,"depth":962,"text":895},{"id":932,"depth":962,"text":933},{"id":1057,"depth":962,"text":1058},{"id":1118,"depth":962,"text":1119},{"id":1204,"depth":962,"text":1205},{"id":1301,"depth":962,"text":1302},{"id":1375,"depth":962,"text":1376},{"id":1446,"depth":962,"text":1447},{"id":1505,"depth":962,"text":1506},{"id":1547,"depth":962,"text":1548},{"id":1722,"depth":962,"text":1723},{"id":1811,"depth":962,"text":1812},{"id":1870,"depth":962,"text":1871},{"id":1909,"depth":962,"text":1910,"children":3526},[3527,3528,3529,3530,3531],{"id":1917,"depth":35,"text":1920},{"id":1935,"depth":35,"text":1938},{"id":1950,"depth":35,"text":1953},{"id":2007,"depth":35,"text":2010},{"id":2041,"depth":35,"text":1786},{"id":2092,"depth":962,"text":2093},{"id":2175,"depth":962,"text":2176},{"id":2317,"depth":962,"text":2318},{"id":2389,"depth":962,"text":2390},{"id":2489,"depth":962,"text":2490,"children":3537},[3538,3539,3540],{"id":2496,"depth":35,"text":2497},{"id":2537,"depth":35,"text":2538},{"id":2581,"depth":35,"text":2582},{"id":2635,"depth":962,"text":2636},{"id":2718,"depth":962,"text":2719},{"id":2752,"depth":962,"text":2753,"children":3544},[3545,3546,3547,3548,3549,3550,3551,3552],{"id":2756,"depth":35,"text":2757},{"id":2769,"depth":35,"text":2769},{"id":2781,"depth":35,"text":2781},{"id":2793,"depth":35,"text":2793},{"id":2810,"depth":35,"text":2810},{"id":2826,"depth":35,"text":2826},{"id":2836,"depth":35,"text":2836},{"id":2853,"depth":35,"text":2853},{"id":2862,"depth":962,"text":2863,"children":3554},[3555,3556,3557,3558,3559],{"id":2866,"depth":35,"text":2867},{"id":2892,"depth":35,"text":2892},{"id":2927,"depth":35,"text":2928},{"id":3020,"depth":35,"text":3020},{"id":3113,"depth":35,"text":3113},{"id":3158,"depth":962,"text":3159,"children":3561},[3562,3563,3564,3565,3566,3567,3568,3569],{"id":3162,"depth":35,"text":3163},{"id":3189,"depth":35,"text":3189},{"id":3208,"depth":35,"text":3209},{"id":3223,"depth":35,"text":3223},{"id":3234,"depth":35,"text":3234},{"id":3243,"depth":35,"text":3244},{"id":3257,"depth":35,"text":3258},{"id":3280,"depth":35,"text":3281},{"id":3290,"depth":962,"text":3291},{"id":3322,"depth":962,"text":3323},{"id":3350,"depth":962,"text":3351},{"id":3437,"depth":962,"text":3437},"学习","2026-07-26","记录 Quilibra 个人主页 2026 年 7 月版本的技术选型、页面架构、内容系统、主题动画、双环境发布、HTTPS、测试体系和维护方法。","md",false,{},"\u002Fwriting\u002F2026-07-26-quilibra-complete-technical-handbook","2026-07-26T23:46:23+08:00",{"title":105,"description":3576},"quilibra-complete-technical-handbook","writing\u002F2026-07-26-quilibra-complete-technical-handbook",[3586,3587,274,3588,322,314,3589],"Nuxt","Vue","Nuxt Content","静态部署","2026-08-03","ILFf8HTh9BFuZakheSV0lJmjpNenfU1fBRtcsKT1umE",[3593,4045,6365,6531,6725],{"id":3594,"title":3595,"body":3596,"category":3574,"date":4026,"description":4027,"extension":3577,"hidden":3578,"meta":4028,"navigation":31,"path":4029,"publishedAt":4030,"seo":4031,"slug":4032,"stem":4033,"tags":4034,"updated":4043,"__hash__":4044},"writing\u002Fwriting\u002F2026-07-29-etf-premium-discount-cost-of-carry.md","从ETF到商品期货：申赎机制、折溢价与持有成本模型",{"type":107,"value":3597,"toc":4005},[3598,3602,3605,3608,3622,3625,3628,3631,3685,3688,3691,3705,3715,3718,3732,3735,3739,3742,3749,3752,3775,3778,3781,3801,3804,3815,3818,3821,3825,3828,3831,3842,3845,3871,3877,3880,3885,3888,3892,3943,3946,3966,3969,3983,3986,3992,3995,4002],[138,3599,3601],{"id":3600},"etf与lof申赎机制决定折溢价","ETF与LOF：申赎机制决定折溢价",[1915,3603,3604],{"id":3604},"底层机制差异",[110,3606,3607],{},"ETF和LOF虽然都是场内可交易的开放式基金，但申赎机制截然不同：",[200,3609,3610,3616],{},[203,3611,3612,3615],{},[178,3613,3614],{},"ETF：实物申赎，T+0瞬时套利。"," 机构投资者用一篮子股票换取基金份额，最小申赎单位通常为50万或100万份，门槛在百万级别。每日盘前基金公司公布PCF（Portfolio Composition File）清单，列明当日申赎对应的一篮子股票明细。",[203,3617,3618,3621],{},[178,3619,3620],{},"LOF：现金申赎，跨系统转托管需T+2日。"," 门槛低（1份起），普通投资者即可参与，但套利存在时滞和净值波动风险。",[110,3623,3624],{},"这个差异是两者折溢价收敛速度天差地别的根本原因。",[1915,3626,3627],{"id":3627},"现金替代的四种标志",[110,3629,3630],{},"ETF实物申赎中，并非每只成分股都能完美交收。PCF清单对每只股票标注现金替代标志：",[235,3632,3633,3643],{},[238,3634,3635],{},[241,3636,3637,3640],{},[244,3638,3639],{},"标志",[244,3641,3642],{},"含义",[251,3644,3645,3655,3665,3675],{},[241,3646,3647,3652],{},[256,3648,3649],{},[178,3650,3651],{},"禁止",[256,3653,3654],{},"必须用股票实物交收",[241,3656,3657,3662],{},[256,3658,3659],{},[178,3660,3661],{},"允许",[256,3663,3664],{},"申购可现金替代，赎回必须给股票，多退少补",[241,3666,3667,3672],{},[256,3668,3669],{},[178,3670,3671],{},"必须",[256,3673,3674],{},"申赎均必须用现金，针对停牌或待剔除股票",[241,3676,3677,3682],{},[256,3678,3679],{},[178,3680,3681],{},"退补",[256,3683,3684],{},"必须现金替代，按基金公司实际买卖成本多退少补",[1915,3686,3687],{"id":3687},"折溢价的形成",[110,3689,3690],{},"场内基金有两个价格：",[200,3692,3693,3699],{},[203,3694,3695,3698],{},[178,3696,3697],{},"IOPV（参考净值）","：实时计算的基金份额参考价值",[203,3700,3701,3704],{},[178,3702,3703],{},"市价","：二级市场交易价格",[110,3706,3707,3708,3711,3712,1027],{},"当市价 > IOPV，即为",[178,3709,3710],{},"溢价","；反之则为",[178,3713,3714],{},"折价",[110,3716,3717],{},"套利机制是折溢价收敛的驱动力：",[200,3719,3720,3726],{},[203,3721,3722,3725],{},[178,3723,3724],{},"溢价套利","：T日买入一篮子股票 → 申购ETF份额 → 卖出ETF，全流程T+0完成",[203,3727,3728,3731],{},[178,3729,3730],{},"折价套利","：T日买入ETF → 赎回为一篮子股票 → 卖出股票，同样T+0",[110,3733,3734],{},"正因为ETF套利链条畅通，折溢价通常控制在1%以内。而LOF因T+2转托管时滞，套利者需承担两天净值波动风险，折溢价常达5%以上。",[138,3736,3738],{"id":3737},"跨境etf全现金替代与qdii额度约束","跨境ETF：全现金替代与QDII额度约束",[1915,3740,3741],{"id":3741},"一个常见误区",[110,3743,3744,3745,3748],{},"\"我有了一篮子股票，如何申购跨境ETF？\"——这个问题本身存在误区。跨境ETF的成分股在境外，境内投资者无法持有和交收这些股票，因此",[178,3746,3747],{},"跨境ETF不能实物申购，只能全现金替代","。投资者交付现金，由基金公司代为买入境外成分股。",[1915,3750,3751],{"id":3751},"现金申购流程",[380,3753,3754,3757,3760,3763,3766,3769,3772],{},[203,3755,3756],{},"确认券商具备跨境ETF申赎资格",[203,3758,3759],{},"T日盘前查阅PCF清单（现金替代标志全为\"退补\"）",[203,3761,3762],{},"核对当日QDII额度与开放状态（可能限购，极端情况单日仅100元）",[203,3764,3765],{},"计算所需现金（现金替代款 + 预估现金差额），最小单位50万份",[203,3767,3768],{},"T日交易时段通过券商端提交委托",[203,3770,3771],{},"T+1日日终份额确认，T+2日可用",[203,3773,3774],{},"基金公司代买完成后，进行现金差额多退少补",[1915,3776,3777],{"id":3777},"三层结构与溢价固化",[110,3779,3780],{},"跨境ETF的运作涉及三层主体：",[200,3782,3783,3789,3795],{},[203,3784,3785,3788],{},[178,3786,3787],{},"外汇局","：审批QDII额度（总量约1708亿美元）",[203,3790,3791,3794],{},[178,3792,3793],{},"基金公司","：在额度内换汇，代投资者买入境外成分股",[203,3796,3797,3800],{},[178,3798,3799],{},"境内投资者","：只能通过现金提交申购申请",[110,3802,3803],{},"当额度充裕时，套利机制正常运行。但当额度耗尽时：",[380,3805,3806,3809,3812],{},[203,3807,3808],{},"基金公司暂停或限额申购（单日低至10元）",[203,3810,3811],{},"一级市场申购通道堵死，套利者无法创造新份额",[203,3813,3814],{},"二级市场资金只能抢存量份额，溢价飙升至10%-50%+",[110,3816,3817],{},"此外，现金申赎 + 代买模式下，跨时区汇率波动和境外开盘价差导致退补款不可预知，套利收益无法锁定，进一步抑制套利者进场意愿，固化高溢价。",[110,3819,3820],{},"典型场景：QDII ETF因外汇额度受限，一级市场申购通道关闭，套利链条断裂，溢价长期高达10%-40%。",[138,3822,3824],{"id":3823},"商品期货持有成本模型与期限结构","商品期货：持有成本模型与期限结构",[1915,3826,3827],{"id":3827},"持有成本定价公式",[110,3829,3830],{},"商品期货的理论价格由持有成本模型决定：",[149,3832,3836],{"className":3833,"code":3834,"language":3835,"meta":155,"style":155},"language-plain shiki shiki-themes github-light github-dark","F = S · e^((r + u − y) · T)\n","plain",[62,3837,3838],{"__ignoreMap":155},[773,3839,3840],{"class":775,"line":776},[773,3841,3834],{},[110,3843,3844],{},"其中：",[200,3846,3847,3853,3859,3865],{},[203,3848,3849,3852],{},[178,3850,3851],{},"S","：现货价格",[203,3854,3855,3858],{},[178,3856,3857],{},"r","：无风险利率（资金占用成本）",[203,3860,3861,3864],{},[178,3862,3863],{},"u","：仓储保险成本率",[203,3866,3867,3870],{},[178,3868,3869],{},"y","：便利收益率（convenience yield）",[110,3872,3873,3876],{},[178,3874,3875],{},"便利收益","是持有实物库存的隐性收益——当供应紧张时，手上有货意味着生产不断线、客户不流失，这种\"随时可用\"的价值就是便利收益。它与库存水平成反比：库存越低，便利收益越高。",[1915,3878,3879],{"id":3879},"基差及其收敛",[110,3881,3882],{},[178,3883,3884],{},"基差 = 现货价格(S) − 期货价格(F)",[110,3886,3887],{},"随着交割日临近，持仓成本（r+u−y）T逐渐衰减趋零，期货价格向现货价格收敛，基差必然趋近于零。否则将触发无风险套利：买低卖高，交割平仓。",[1915,3889,3891],{"id":3890},"期限结构contango与backwardation","期限结构：Contango与Backwardation",[235,3893,3894,3909],{},[238,3895,3896],{},[241,3897,3898,3901,3904,3907],{},[244,3899,3900],{},"结构",[244,3902,3903],{},"形态",[244,3905,3906],{},"成因",[244,3908,3642],{},[251,3910,3911,3927],{},[241,3912,3913,3918,3921,3924],{},[256,3914,3915],{},[178,3916,3917],{},"Contango",[256,3919,3920],{},"S \u003C F近 \u003C F远",[256,3922,3923],{},"r+u > y，库存充足",[256,3925,3926],{},"常态结构，持有成本主导",[241,3928,3929,3934,3937,3940],{},[256,3930,3931],{},[178,3932,3933],{},"Backwardation",[256,3935,3936],{},"S > F近 > F远",[256,3938,3939],{},"r+u \u003C y，库存紧张",[256,3941,3942],{},"供应短缺，现货溢价",[1915,3944,3945],{"id":3945},"品种实例",[200,3947,3948,3954,3960],{},[203,3949,3950,3953],{},[178,3951,3952],{},"黄金","：投资性商品，便利收益y≈0，恒为Contango结构",[203,3955,3956,3959],{},[178,3957,3958],{},"原油","：消费性商品，便利收益波动剧烈，常在Contango与Backwardation间切换。紧缺时深度Backwardation，过剩时转Contango",[203,3961,3962,3965],{},[178,3963,3964],{},"农产品","：受季节性收获周期影响，仓储成本u与便利收益y交替主导曲线形态",[1915,3967,3968],{"id":3968},"套利操作",[200,3970,3971,3977],{},[203,3972,3973,3976],{},[178,3974,3975],{},"正向套利（Cash and Carry）","：当F实际 > F理论时，借钱买入现货 + 卖出期货，锁定差价",[203,3978,3979,3982],{},[178,3980,3981],{},"反向套利（Reverse Cash and Carry）","：当F实际 \u003C F理论时，卖空现货 + 买入期货。但对消费性商品，卖空现货难以操作",[1915,3984,3985],{"id":3985},"远期与期货的凸性调整",[110,3987,3988,3989,1027],{},"期货合约每日盯市（mark-to-market），而远期合约到期才结算。当标的资产价格与利率正相关时，期货价格略高于远期价格，这一差异称为",[178,3990,3991],{},"凸性调整",[138,3993,3994],{"id":3994},"串联",[110,3996,3997,3998,4001],{},"三个主题看似分散，实则共享一条逻辑主线：",[178,3999,4000],{},"机制决定效率，效率决定价格形态","。ETF的T+0实物申赎使折溢价瞬间收敛；跨境ETF的额度瓶颈使套利链条断裂、溢价固化；商品期货的持有成本模型则将仓储、利率、库存预期统一为一个数学框架，解释了曲线形态的成因。理解机制本身，比记住某个具体结论更重要。",[3499,4003,4004],{},"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);}",{"title":155,"searchDepth":962,"depth":962,"links":4006},[4007,4012,4017,4025],{"id":3600,"depth":962,"text":3601,"children":4008},[4009,4010,4011],{"id":3604,"depth":35,"text":3604},{"id":3627,"depth":35,"text":3627},{"id":3687,"depth":35,"text":3687},{"id":3737,"depth":962,"text":3738,"children":4013},[4014,4015,4016],{"id":3741,"depth":35,"text":3741},{"id":3751,"depth":35,"text":3751},{"id":3777,"depth":35,"text":3777},{"id":3823,"depth":962,"text":3824,"children":4018},[4019,4020,4021,4022,4023,4024],{"id":3827,"depth":35,"text":3827},{"id":3879,"depth":35,"text":3879},{"id":3890,"depth":35,"text":3891},{"id":3945,"depth":35,"text":3945},{"id":3968,"depth":35,"text":3968},{"id":3985,"depth":35,"text":3985},{"id":3994,"depth":962,"text":3994},"2026-07-29","ETF实物申赎与LOF现金申赎的机制差异决定折溢价收敛速度，跨境ETF的QDII额度约束固化高溢价，商品期货持有成本模型统一解释期限结构形态。",{},"\u002Fwriting\u002F2026-07-29-etf-premium-discount-cost-of-carry","2026-07-29T10:51:17+08:00",{"title":3595,"description":4027},"etf-premium-discount-cost-of-carry","writing\u002F2026-07-29-etf-premium-discount-cost-of-carry",[4035,4036,4037,4038,4039,4040,4041,4042],"ETF","LOF","跨境ETF","QDII","商品期货","持有成本模型","期限结构","折溢价",null,"P-nL6PDPMB-K1dYMhb05-mjrddhVCF2rRa7sbCY8CXo",{"id":104,"title":105,"body":4046,"category":3574,"date":3575,"description":3576,"extension":3577,"hidden":3578,"meta":6362,"navigation":31,"path":3580,"publishedAt":3581,"seo":6363,"slug":3583,"stem":3584,"tags":6364,"updated":3590,"__hash__":3591},{"type":107,"value":4047,"toc":6290},[4048,4050,4052,4064,4066,4068,4070,4075,4085,4093,4095,4097,4099,4113,4115,4117,4119,4121,4193,4195,4199,4204,4212,4214,4216,4221,4223,4245,4253,4255,4259,4281,4291,4297,4301,4329,4333,4335,4337,4389,4399,4403,4429,4437,4439,4443,4465,4475,4499,4501,4507,4509,4511,4527,4537,4547,4549,4551,4553,4571,4573,4575,4579,4619,4625,4627,4645,4647,4652,4656,4658,4664,4669,4673,4679,4691,4693,4699,4701,4715,4717,4743,4747,4749,4755,4757,4759,4769,4781,4785,4801,4807,4809,4815,4817,4835,4843,4845,4855,4857,4859,4869,4871,4876,4884,4888,4898,4900,4906,4910,4912,4922,4930,4934,4936,4942,4946,4952,4956,4960,4962,4964,5028,5030,5035,5057,5075,5083,5089,5091,5097,5099,5115,5117,5123,5127,5132,5136,5140,5148,5150,5154,5156,5172,5182,5184,5186,5190,5195,5201,5209,5211,5213,5217,5219,5224,5226,5230,5232,5237,5241,5249,5271,5275,5279,5293,5295,5299,5305,5310,5318,5330,5332,5336,5341,5349,5361,5367,5379,5381,5385,5387,5409,5451,5455,5457,5462,5468,5474,5476,5516,5522,5526,5528,5530,5535,5537,5565,5577,5583,5589,5591,5593,5595,5599,5621,5623,5627,5651,5653,5657,5683,5685,5687,5689,5691,5703,5707,5709,5711,5739,5743,5745,5747,5751,5763,5765,5767,5769,5775,5777,5783,5785,5791,5793,5803,5805,5813,5815,5819,5821,5829,5831,5835,5837,5839,5853,5855,5857,5877,5881,5883,5949,5955,5957,6029,6031,6033,6055,6063,6065,6067,6081,6083,6093,6095,6101,6103,6109,6111,6115,6117,6123,6125,6131,6137,6139,6143,6145,6147,6152,6154,6158,6164,6166,6168,6170,6172,6174,6176,6180,6182,6184,6186,6226,6228,6242,6244,6246,6288],[110,4049,112],{},[110,4051,115],{},[117,4053,4054],{},[110,4055,121,4056,125,4058,129,4060,133,4062,136],{},[62,4057,124],{},[62,4059,128],{},[62,4061,132],{},[62,4063,132],{},[138,4065,141],{"id":140},[110,4067,144],{},[110,4069,147],{},[149,4071,4073],{"className":4072,"code":153,"language":154,"meta":155},[152],[62,4074,153],{"__ignoreMap":155},[110,4076,160,4077,163,4079,167,4081,163,4083,173],{},[62,4078,132],{},[62,4080,166],{},[62,4082,124],{},[62,4084,172],{},[110,4086,176,4087],{},[178,4088,180,4089,184,4091,188],{},[62,4090,183],{},[62,4092,187],{},[110,4094,191],{},[138,4096,195],{"id":194},[110,4098,198],{},[200,4100,4101,4103,4105,4107,4109,4111],{},[203,4102,205],{},[203,4104,208],{},[203,4106,211],{},[203,4108,214],{},[203,4110,217],{},[203,4112,220],{},[110,4114,223],{},[110,4116,226],{},[138,4118,230],{"id":229},[110,4120,233],{},[235,4122,4123,4131],{},[238,4124,4125],{},[241,4126,4127,4129],{},[244,4128,246],{},[244,4130,249],{},[251,4132,4133,4139,4145,4151,4157,4163,4169,4175,4181,4187],{},[241,4134,4135,4137],{},[256,4136,258],{},[256,4138,261],{},[241,4140,4141,4143],{},[256,4142,266],{},[256,4144,269],{},[241,4146,4147,4149],{},[256,4148,274],{},[256,4150,277],{},[241,4152,4153,4155],{},[256,4154,282],{},[256,4156,285],{},[241,4158,4159,4161],{},[256,4160,290],{},[256,4162,293],{},[241,4164,4165,4167],{},[256,4166,298],{},[256,4168,301],{},[241,4170,4171,4173],{},[256,4172,306],{},[256,4174,309],{},[241,4176,4177,4179],{},[256,4178,314],{},[256,4180,317],{},[241,4182,4183,4185],{},[256,4184,322],{},[256,4186,325],{},[241,4188,4189,4191],{},[256,4190,330],{},[256,4192,333],{},[110,4194,336],{},[110,4196,4197,342],{},[62,4198,341],{},[149,4200,4202],{"className":4201,"code":346,"language":154,"meta":155},[152],[62,4203,346],{"__ignoreMap":155},[110,4205,4206,354,4208,358,4210,362],{},[62,4207,353],{},[62,4209,357],{},[62,4211,361],{},[138,4213,366],{"id":365},[110,4215,369],{},[149,4217,4219],{"className":4218,"code":373,"language":154,"meta":155},[152],[62,4220,373],{"__ignoreMap":155},[110,4222,378],{},[380,4224,4225,4229,4233,4239],{},[203,4226,4227,387],{},[62,4228,386],{},[203,4230,4231,393],{},[62,4232,392],{},[203,4234,4235,354,4237,402],{},[62,4236,398],{},[62,4238,401],{},[203,4240,4241,408,4243,412],{},[62,4242,407],{},[62,4244,411],{},[110,4246,415,4247,419,4249,423,4251,427],{},[62,4248,418],{},[62,4250,422],{},[62,4252,426],{},[138,4254,431],{"id":430},[110,4256,4257,437],{},[62,4258,436],{},[200,4260,4261,4265,4269,4271,4273,4277,4279],{},[203,4262,442,4263,446],{},[62,4264,445],{},[203,4266,449,4267,446],{},[62,4268,452],{},[203,4270,455],{},[203,4272,458],{},[203,4274,461,4275,465],{},[62,4276,464],{},[203,4278,468],{},[203,4280,471],{},[110,4282,474,4283,423,4285,481,4287,485,4289,489],{},[62,4284,477],{},[62,4286,480],{},[62,4288,484],{},[62,4290,488],{},[110,4292,492,4293,496,4295,500],{},[178,4294,495],{},[62,4296,499],{},[110,4298,4299,506],{},[62,4300,505],{},[200,4302,4303,4317],{},[203,4304,4305,408,4307,408,4309,408,4311,408,4313,526,4315,530],{},[62,4306,513],{},[62,4308,516],{},[62,4310,519],{},[62,4312,522],{},[62,4314,525],{},[62,4316,529],{},[203,4318,4319,536,4321,540,4323,544,4325,548,4327,552],{},[62,4320,535],{},[62,4322,539],{},[62,4324,543],{},[62,4326,547],{},[62,4328,551],{},[110,4330,555,4331,559],{},[62,4332,558],{},[138,4334,563],{"id":562},[110,4336,566],{},[235,4338,4339,4347],{},[238,4340,4341],{},[241,4342,4343,4345],{},[244,4344,575],{},[244,4346,578],{},[251,4348,4349,4357,4365,4373,4381],{},[241,4350,4351,4355],{},[256,4352,4353],{},[62,4354,587],{},[256,4356,590],{},[241,4358,4359,4363],{},[256,4360,4361],{},[62,4362,597],{},[256,4364,600],{},[241,4366,4367,4371],{},[256,4368,4369],{},[62,4370,607],{},[256,4372,610],{},[241,4374,4375,4379],{},[256,4376,4377],{},[62,4378,617],{},[256,4380,620],{},[241,4382,4383,4387],{},[256,4384,4385],{},[62,4386,627],{},[256,4388,630],{},[110,4390,633,4391,637,4393,641,4395,645,4397,649],{},[62,4392,636],{},[62,4394,640],{},[62,4396,644],{},[62,4398,648],{},[110,4400,652,4401,656],{},[62,4402,655],{},[200,4404,4405,4409,4411,4413,4421,4423,4427],{},[203,4406,661,4407,446],{},[62,4408,664],{},[203,4410,667],{},[203,4412,670],{},[203,4414,673,4415,408,4417,408,4419,446],{},[62,4416,61],{},[62,4418,73],{},[62,4420,67],{},[203,4422,682],{},[203,4424,685,4425,688],{},[62,4426,78],{},[203,4428,691],{},[110,4430,4431,697,4433,700,4435,704],{},[62,4432,696],{},[62,4434,655],{},[62,4436,703],{},[138,4438,708],{"id":707},[110,4440,4441,714],{},[62,4442,713],{},[200,4444,4445,4447,4449,4451,4455,4457,4461,4463],{},[203,4446,719],{},[203,4448,722],{},[203,4450,725],{},[203,4452,728,4453,446],{},[62,4454,731],{},[203,4456,734],{},[203,4458,737,4459,446],{},[62,4460,740],{},[203,4462,743],{},[203,4464,746],{},[110,4466,4467,752,4469,756,4471,760,4473,764],{},[62,4468,751],{},[62,4470,755],{},[62,4472,759],{},[62,4474,763],{},[149,4476,4477],{"className":767,"code":768,"language":769,"meta":155,"style":155},[62,4478,4479],{"__ignoreMap":155},[773,4480,4481,4483,4485,4487,4489,4491,4493,4495,4497],{"class":775,"line":776},[773,4482,780],{"class":779},[773,4484,784],{"class":783},[773,4486,788],{"class":787},[773,4488,791],{"class":779},[773,4490,795],{"class":794},[773,4492,798],{"class":787},[773,4494,791],{"class":779},[773,4496,803],{"class":794},[773,4498,806],{"class":779},[110,4500,809],{},[110,4502,812,4503,816,4505,820],{},[62,4504,815],{},[62,4506,819],{},[138,4508,824],{"id":823},[110,4510,827],{},[200,4512,4513,4515,4517,4519,4521,4523,4525],{},[203,4514,832],{},[203,4516,835],{},[203,4518,838],{},[203,4520,841],{},[203,4522,844],{},[203,4524,847],{},[203,4526,850],{},[110,4528,853,4529,857,4531,861,4533,865,4535,869],{},[62,4530,856],{},[62,4532,860],{},[62,4534,864],{},[62,4536,868],{},[110,4538,872,4539,876,4541,880,4543,884,4545,888],{},[62,4540,875],{},[62,4542,879],{},[62,4544,883],{},[62,4546,887],{},[110,4548,891],{},[138,4550,895],{"id":894},[110,4552,898],{},[200,4554,4555,4559,4561,4563,4565,4569],{},[203,4556,903,4557,907],{},[62,4558,906],{},[203,4560,910],{},[203,4562,913],{},[203,4564,916],{},[203,4566,919,4567,923],{},[62,4568,922],{},[203,4570,926],{},[110,4572,929],{},[138,4574,933],{"id":932},[110,4576,936,4577,940],{},[62,4578,939],{},[149,4580,4581],{"className":943,"code":944,"language":945,"meta":155,"style":155},[62,4582,4583,4591,4599,4607,4615],{"__ignoreMap":155},[773,4584,4585,4587,4589],{"class":775,"line":776},[773,4586,953],{"class":952},[773,4588,956],{"class":787},[773,4590,959],{"class":779},[773,4592,4593,4595,4597],{"class":775,"line":962},[773,4594,966],{"class":965},[773,4596,969],{"class":952},[773,4598,973],{"class":972},[773,4600,4601,4603,4605],{"class":775,"line":35},[773,4602,978],{"class":965},[773,4604,969],{"class":952},[773,4606,973],{"class":972},[773,4608,4609,4611,4613],{"class":775,"line":985},[773,4610,988],{"class":965},[773,4612,969],{"class":952},[773,4614,973],{"class":972},[773,4616,4617],{"class":775,"line":995},[773,4618,998],{"class":779},[110,4620,1001,4621,1005,4623,1009],{},[62,4622,1004],{},[62,4624,1008],{},[110,4626,1012],{},[380,4628,4629,4631,4637,4639,4643],{},[203,4630,1017],{},[203,4632,1020,4633,1024,4635,1027],{},[62,4634,1023],{},[62,4636,664],{},[203,4638,1030],{},[203,4640,1033,4641,1027],{},[62,4642,1036],{},[203,4644,1039],{},[110,4646,1042],{},[149,4648,4650],{"className":4649,"code":1046,"language":154,"meta":155},[152],[62,4651,1046],{"__ignoreMap":155},[110,4653,1051,4654,1054],{},[62,4655,1036],{},[138,4657,1058],{"id":1057},[110,4659,1061,4660,1065,4662,1069],{},[62,4661,1064],{},[62,4663,1068],{},[149,4665,4667],{"className":4666,"code":1073,"language":154,"meta":155},[152],[62,4668,1073],{"__ignoreMap":155},[110,4670,1078,4671,1082],{},[62,4672,1081],{},[110,4674,1085,4675,354,4677,1092],{},[62,4676,1088],{},[62,4678,1091],{},[110,4680,1095,4681,1099,4683,1103,4685,1107,4687,1111,4689,1115],{},[62,4682,1098],{},[62,4684,1102],{},[62,4686,1106],{},[62,4688,1110],{},[62,4690,1114],{},[138,4692,1119],{"id":1118},[110,4694,4695,1125,4697,1129],{},[62,4696,1124],{},[62,4698,1128],{},[110,4700,1132],{},[200,4702,4703,4707,4709,4711,4713],{},[203,4704,4705,1140],{},[62,4706,1139],{},[203,4708,1143],{},[203,4710,1146],{},[203,4712,1149],{},[203,4714,1152],{},[110,4716,1155],{},[200,4718,4719,4723,4727,4733,4737],{},[203,4720,1160,4721,1164],{},[62,4722,1163],{},[203,4724,1167,4725,1170],{},[62,4726,1139],{},[203,4728,4729,1176,4731,1180],{},[62,4730,1175],{},[62,4732,1179],{},[203,4734,1183,4735,1187],{},[62,4736,1186],{},[203,4738,1190,4739,423,4741,1195],{},[62,4740,1175],{},[62,4742,1186],{},[110,4744,1198,4745],{},[178,4746,1201],{},[138,4748,1205],{"id":1204},[110,4750,1208,4751,1212,4753,1216],{},[62,4752,1211],{},[62,4754,1215],{},[110,4756,1219],{},[110,4758,1222],{},[200,4760,4761,4763,4765,4767],{},[203,4762,1227],{},[203,4764,1230],{},[203,4766,1233],{},[203,4768,1236],{},[110,4770,1239,4771,1243,4773,408,4775,423,4777,1253,4779,1257],{},[62,4772,1242],{},[62,4774,1246],{},[62,4776,1249],{},[62,4778,1252],{},[62,4780,1256],{},[110,4782,4783,1263],{},[62,4784,1262],{},[380,4786,4787,4791,4795,4797,4799],{},[203,4788,4789,1271],{},[62,4790,1270],{},[203,4792,1274,4793,1278],{},[62,4794,1277],{},[203,4796,1281],{},[203,4798,1284],{},[203,4800,1287],{},[110,4802,1290,4803,1294,4805,1298],{},[62,4804,1293],{},[62,4806,1297],{},[138,4808,1302],{"id":1301},[110,4810,1305,4811,1309,4813,1313],{},[62,4812,1308],{},[62,4814,1312],{},[110,4816,1316],{},[200,4818,4819,4821,4823,4825,4827,4829,4831],{},[203,4820,1321],{},[203,4822,1324],{},[203,4824,1327],{},[203,4826,1330],{},[203,4828,1333],{},[203,4830,1336],{},[203,4832,1339,4833,1343],{},[62,4834,1342],{},[110,4836,4837,408,4839,423,4841,1354],{},[62,4838,1348],{},[62,4840,1351],{},[62,4842,1342],{},[110,4844,1357],{},[200,4846,4847,4851],{},[203,4848,4849,1365],{},[62,4850,1364],{},[203,4852,1368,4853,1372],{},[62,4854,1371],{},[138,4856,1376],{"id":1375},[110,4858,1379],{},[110,4860,4861,1385,4863,354,4865,1391,4867,1395],{},[62,4862,1384],{},[62,4864,74],{},[62,4866,1390],{},[62,4868,1394],{},[110,4870,1398],{},[149,4872,4874],{"className":4873,"code":1402,"language":154,"meta":155},[152],[62,4875,1402],{"__ignoreMap":155},[110,4877,4878,1410,4880,1414,4882,1418],{},[62,4879,1409],{},[62,4881,1413],{},[62,4883,1417],{},[110,4885,1421,4886,1425],{},[178,4887,1424],{},[110,4889,1428,4890,1431,4892,1435,4894,1439,4896,1443],{},[62,4891,78],{},[62,4893,1434],{},[62,4895,1438],{},[62,4897,1442],{},[138,4899,1447],{"id":1446},[110,4901,4902,1453,4904,1457],{},[62,4903,1452],{},[62,4905,1456],{},[110,4907,1460,4908,1464],{},[62,4909,1463],{},[110,4911,1467],{},[200,4913,4914,4916,4918,4920],{},[203,4915,1472],{},[203,4917,1475],{},[203,4919,1478],{},[203,4921,1481],{},[110,4923,1484,4924,408,4926,1491,4928,1495],{},[62,4925,1487],{},[62,4927,1490],{},[62,4929,1494],{},[110,4931,1498,4932,1502],{},[62,4933,1501],{},[138,4935,1506],{"id":1505},[110,4937,1509,4938,1513,4940,1517],{},[62,4939,1512],{},[62,4941,1516],{},[110,4943,1520,4944,1523],{},[62,4945,1456],{},[200,4947,4948,4950],{},[203,4949,1528],{},[203,4951,1531],{},[110,4953,1534,4954,1537],{},[62,4955,551],{},[110,4957,1540,4958,1544],{},[62,4959,1543],{},[138,4961,1548],{"id":1547},[110,4963,1551],{},[149,4965,4966],{"className":1554,"code":1555,"language":1556,"meta":155,"style":155},[62,4967,4968,4972,4980,4988,4996,5004,5012,5018,5024],{"__ignoreMap":155},[773,4969,4970],{"class":775,"line":776},[773,4971,1563],{"class":787},[773,4973,4974,4976,4978],{"class":775,"line":962},[773,4975,1417],{"class":783},[773,4977,1570],{"class":779},[773,4979,1573],{"class":794},[773,4981,4982,4984,4986],{"class":775,"line":35},[773,4983,1578],{"class":783},[773,4985,1570],{"class":779},[773,4987,1583],{"class":794},[773,4989,4990,4992,4994],{"class":775,"line":985},[773,4991,1588],{"class":783},[773,4993,1570],{"class":779},[773,4995,1593],{"class":972},[773,4997,4998,5000,5002],{"class":775,"line":995},[773,4999,1598],{"class":783},[773,5001,1570],{"class":779},[773,5003,1603],{"class":972},[773,5005,5006,5008,5010],{"class":775,"line":1606},[773,5007,1609],{"class":783},[773,5009,1570],{"class":779},[773,5011,1614],{"class":794},[773,5013,5014,5016],{"class":775,"line":1617},[773,5015,1620],{"class":783},[773,5017,1623],{"class":779},[773,5019,5020,5022],{"class":775,"line":1626},[773,5021,1629],{"class":779},[773,5023,1632],{"class":794},[773,5025,5026],{"class":775,"line":1635},[773,5027,1563],{"class":787},[110,5029,1640],{},[149,5031,5033],{"className":5032,"code":1644,"language":154,"meta":155},[152],[62,5034,1644],{"__ignoreMap":155},[110,5036,1649,5037,408,5039,408,5041,408,5043,408,5045,408,5047,408,5049,408,5051,1027,5053,1670,5055,1673],{},[62,5038,1417],{},[62,5040,1578],{},[62,5042,1588],{},[62,5044,1658],{},[62,5046,1661],{},[62,5048,1598],{},[62,5050,1609],{},[62,5052,1620],{},[62,5054,1598],{},[62,5056,755],{},[200,5058,5059,5063,5067,5071],{},[203,5060,5061,1680],{},[62,5062,1588],{},[203,5064,1683,5065,1686],{},[62,5066,1588],{},[203,5068,1689,5069,446],{},[62,5070,1658],{},[203,5072,5073,1696],{},[62,5074,1661],{},[110,5076,1699,5077,1702,5079,1705,5081,1709],{},[62,5078,1588],{},[62,5080,1658],{},[62,5082,1708],{},[110,5084,5085,1715,5087,1719],{},[62,5086,1714],{},[62,5088,1718],{},[138,5090,1723],{"id":1722},[110,5092,5093,1729,5095,1732],{},[62,5094,1728],{},[62,5096,696],{},[110,5098,1735],{},[200,5100,5101,5103,5105,5107,5109,5111],{},[203,5102,1740],{},[203,5104,1743],{},[203,5106,1746],{},[203,5108,1749],{},[203,5110,1752],{},[203,5112,5113,1757],{},[62,5114,426],{},[110,5116,1760],{},[380,5118,5119,5121],{},[203,5120,1765],{},[203,5122,1768],{},[110,5124,1771,5125,1774],{},[62,5126,124],{},[149,5128,5130],{"className":5129,"code":1778,"language":154,"meta":155},[152],[62,5131,1778],{"__ignoreMap":155},[110,5133,1783,5134,1787],{},[62,5135,1786],{},[110,5137,1790,5138,1794],{},[62,5139,1793],{},[110,5141,1797,5142,1801,5144,1805,5146,1808],{},[62,5143,1800],{},[62,5145,1804],{},[62,5147,1804],{},[138,5149,1812],{"id":1811},[110,5151,1815,5152,1819],{},[62,5153,1818],{},[110,5155,1822],{},[200,5157,5158,5160,5162,5164,5166,5168,5170],{},[203,5159,1827],{},[203,5161,1830],{},[203,5163,1833],{},[203,5165,1836],{},[203,5167,1839],{},[203,5169,1842],{},[203,5171,1845],{},[110,5173,1848,5174,1852,5176,1856,5178,1860,5180,1864],{},[62,5175,1851],{},[62,5177,1855],{},[62,5179,1859],{},[62,5181,1863],{},[110,5183,1867],{},[138,5185,1871],{"id":1870},[110,5187,1874,5188,1878],{},[62,5189,1877],{},[149,5191,5193],{"className":5192,"code":1882,"language":154,"meta":155},[152],[62,5194,1882],{"__ignoreMap":155},[110,5196,5197,1890,5199,1027],{},[62,5198,1889],{},[62,5200,1893],{},[110,5202,1896,5203,408,5205,1176,5207,1906],{},[62,5204,1899],{},[62,5206,1902],{},[62,5208,1905],{},[138,5210,1910],{"id":1909},[110,5212,1913],{},[1915,5214,5215],{"id":1917},[62,5216,1920],{},[110,5218,1923],{},[149,5220,5222],{"className":5221,"code":1927,"language":154,"meta":155},[152],[62,5223,1927],{"__ignoreMap":155},[110,5225,1932],{},[1915,5227,5228],{"id":1935},[62,5229,1938],{},[110,5231,1941],{},[149,5233,5235],{"className":5234,"code":1945,"language":154,"meta":155},[152],[62,5236,1945],{"__ignoreMap":155},[1915,5238,5239],{"id":1950},[62,5240,1953],{},[110,5242,5243,1959,5245,1962,5247,1965],{},[62,5244,1958],{},[62,5246,132],{},[62,5248,124],{},[380,5250,5251,5253,5255,5261,5265,5269],{},[203,5252,1970],{},[203,5254,1973],{},[203,5256,1976,5257,1980,5259,446],{},[62,5258,1979],{},[62,5260,1983],{},[203,5262,1986,5263,446],{},[62,5264,1938],{},[203,5266,1991,5267,446],{},[62,5268,1994],{},[203,5270,1997],{},[110,5272,2000,5273,2004],{},[62,5274,2003],{},[1915,5276,5277],{"id":2007},[62,5278,2010],{},[110,5280,5281,2016,5283,2020,5285,2024,5287,2028,5289,2032,5291,1027],{},[62,5282,2015],{},[62,5284,2019],{},[62,5286,2023],{},[62,5288,2027],{},[62,5290,2031],{},[62,5292,2035],{},[110,5294,2038],{},[1915,5296,5297],{"id":2041},[62,5298,1786],{},[110,5300,2046,5301,2049,5303,2052],{},[62,5302,132],{},[62,5304,124],{},[149,5306,5308],{"className":5307,"code":1927,"language":154,"meta":155},[152],[62,5309,1927],{"__ignoreMap":155},[110,5311,2060,5312,2064,5314,423,5316,2069],{},[62,5313,2063],{},[62,5315,132],{},[62,5317,124],{},[110,5319,2072,5320,2076,5322,2079,5324,2083,5326,2086,5328,2089],{},[62,5321,2075],{},[62,5323,128],{},[62,5325,2082],{},[62,5327,2075],{},[62,5329,2082],{},[138,5331,2093],{"id":2092},[110,5333,5334,2099],{},[62,5335,2098],{},[149,5337,5339],{"className":5338,"code":2103,"language":154,"meta":155},[152],[62,5340,2103],{"__ignoreMap":155},[110,5342,2108,5343,2111,5345,2115,5347,2118],{},[62,5344,183],{},[62,5346,2114],{},[62,5348,187],{},[380,5350,5351,5353,5355],{},[203,5352,2123],{},[203,5354,2126],{},[203,5356,2129,5357,2133,5359,1027],{},[62,5358,2132],{},[62,5360,187],{},[110,5362,2138,5363,2141,5365,2144],{},[62,5364,187],{},[62,5366,187],{},[110,5368,5369,2150,5371,2154,5373,2158,5375,423,5377,1027],{},[62,5370,2149],{},[62,5372,2153],{},[62,5374,2157],{},[62,5376,2161],{},[62,5378,187],{},[110,5380,2166],{},[110,5382,2169,5383,2172],{},[62,5384,187],{},[138,5386,2176],{"id":2175},[110,5388,5389,2182,5391,408,5393,408,5395,423,5397,2195,5399,2198,5401,408,5403,408,5405,2208,5407,2212],{},[62,5390,2181],{},[62,5392,2185],{},[62,5394,2188],{},[62,5396,2191],{},[62,5398,2194],{},[62,5400,187],{},[62,5402,2201],{},[62,5404,2204],{},[62,5406,2207],{},[62,5408,2211],{},[200,5410,5411,5413,5421,5425,5429,5431,5433,5435,5437,5439,5443,5447,5449],{},[203,5412,2217],{},[203,5414,2220,5415,354,5417,2227,5419,2230],{},[62,5416,2223],{},[62,5418,2226],{},[62,5420,187],{},[203,5422,2233,5423,446],{},[62,5424,2236],{},[203,5426,2239,5427,2243],{},[62,5428,2242],{},[203,5430,2246],{},[203,5432,2249],{},[203,5434,2252],{},[203,5436,2255],{},[203,5438,2258],{},[203,5440,2261,5441,2265],{},[62,5442,2264],{},[203,5444,5445,2271],{},[62,5446,2270],{},[203,5448,2274],{},[203,5450,2277],{},[110,5452,2280,5453,2283],{},[62,5454,187],{},[110,5456,2286],{},[149,5458,5460],{"className":5459,"code":2290,"language":154,"meta":155},[152],[62,5461,2290],{"__ignoreMap":155},[110,5463,2295,5464,2299,5466,2303],{},[62,5465,2298],{},[62,5467,2302],{},[110,5469,2306,5470,2310,5472,2314],{},[62,5471,2309],{},[62,5473,2313],{},[138,5475,2318],{"id":2317},[235,5477,5478,5488],{},[238,5479,5480],{},[241,5481,5482,5484,5486],{},[244,5483,2327],{},[244,5485,2330],{},[244,5487,2333],{},[251,5489,5490,5498,5508],{},[241,5491,5492,5494,5496],{},[256,5493,2340],{},[256,5495,2343],{},[256,5497,2346],{},[241,5499,5500,5502,5506],{},[256,5501,2351],{},[256,5503,5504,2356],{},[62,5505,124],{},[256,5507,2346],{},[241,5509,5510,5512,5514],{},[256,5511,2363],{},[256,5513,2366],{},[256,5515,2369],{},[110,5517,2372,5518,2376,5520,2380],{},[62,5519,2375],{},[62,5521,2379],{},[110,5523,2383,5524,2386],{},[62,5525,488],{},[138,5527,2390],{"id":2389},[110,5529,2393],{},[149,5531,5533],{"className":5532,"code":2397,"language":154,"meta":155},[152],[62,5534,2397],{"__ignoreMap":155},[110,5536,2402],{},[200,5538,5539,5543,5547,5551,5555,5561],{},[203,5540,5541,2410],{},[62,5542,2409],{},[203,5544,5545,2416],{},[62,5546,2415],{},[203,5548,5549,2422],{},[62,5550,2421],{},[203,5552,5553,2428],{},[62,5554,2427],{},[203,5556,5557,408,5559,2437],{},[62,5558,2433],{},[62,5560,2436],{},[203,5562,2440,5563,2444],{},[62,5564,2443],{},[110,5566,2447,5567,2450,5569,2454,5571,354,5573,2460,5575,2464],{},[62,5568,2409],{},[62,5570,2453],{},[62,5572,2457],{},[62,5574,1994],{},[62,5576,2463],{},[110,5578,2467,5579,2471,5581,2475],{},[62,5580,2470],{},[62,5582,2474],{},[110,5584,2478,5585,2482,5587,2486],{},[62,5586,2481],{},[62,5588,2485],{},[138,5590,2490],{"id":2489},[110,5592,2493],{},[1915,5594,2497],{"id":2496},[110,5596,5597,2503],{},[62,5598,2502],{},[200,5600,5601,5603,5605,5607,5609,5613,5615,5617,5619],{},[203,5602,2508],{},[203,5604,2511],{},[203,5606,2514],{},[203,5608,2517],{},[203,5610,5611,2522],{},[62,5612,1598],{},[203,5614,2525],{},[203,5616,2528],{},[203,5618,2531],{},[203,5620,2534],{},[1915,5622,2538],{"id":2537},[110,5624,5625,2544],{},[62,5626,2543],{},[200,5628,5629,5633,5635,5637,5639,5641,5643,5645,5647,5649],{},[203,5630,5631,2551],{},[62,5632,187],{},[203,5634,2554],{},[203,5636,2557],{},[203,5638,2560],{},[203,5640,2563],{},[203,5642,2566],{},[203,5644,2569],{},[203,5646,2572],{},[203,5648,2575],{},[203,5650,2578],{},[1915,5652,2582],{"id":2581},[110,5654,5655,2588],{},[62,5656,2587],{},[200,5658,5659,5661,5663,5665,5667,5669,5671,5673,5675,5677,5679,5681],{},[203,5660,2593],{},[203,5662,2596],{},[203,5664,2599],{},[203,5666,2602],{},[203,5668,2605],{},[203,5670,2608],{},[203,5672,2611],{},[203,5674,2614],{},[203,5676,2617],{},[203,5678,2620],{},[203,5680,2623],{},[203,5682,2626],{},[110,5684,2629],{},[110,5686,2632],{},[138,5688,2636],{"id":2635},[110,5690,2639],{},[200,5692,5693,5695,5697,5699,5701],{},[203,5694,2644],{},[203,5696,2647],{},[203,5698,2650],{},[203,5700,2653],{},[203,5702,2656],{},[110,5704,2659,5705,2663],{},[62,5706,2662],{},[110,5708,2666],{},[110,5710,2669],{},[200,5712,5713,5715,5717,5719,5723,5727,5729,5731,5733,5735,5737],{},[203,5714,2674],{},[203,5716,2677],{},[203,5718,2680],{},[203,5720,2683,5721,2687],{},[62,5722,2686],{},[203,5724,5725,2692],{},[62,5726,887],{},[203,5728,2695],{},[203,5730,2698],{},[203,5732,2701],{},[203,5734,2704],{},[203,5736,2707],{},[203,5738,2710],{},[110,5740,5741,2715],{},[62,5742,183],{},[138,5744,2719],{"id":2718},[110,5746,2722],{},[110,5748,2725,5749,2729],{},[62,5750,2728],{},[200,5752,5753,5755,5757,5759,5761],{},[203,5754,2734],{},[203,5756,2737],{},[203,5758,2740],{},[203,5760,2743],{},[203,5762,2746],{},[110,5764,2749],{},[138,5766,2753],{"id":2752},[1915,5768,2757],{"id":2756},[110,5770,2760,5771,2763,5773,2766],{},[62,5772,418],{},[62,5774,617],{},[1915,5776,2769],{"id":2769},[110,5778,2772,5779,2775,5781,2778],{},[62,5780,1004],{},[62,5782,664],{},[1915,5784,2781],{"id":2781},[110,5786,2772,5787,2786,5789,2790],{},[62,5788,939],{},[62,5790,2789],{},[1915,5792,2793],{"id":2793},[110,5794,2796,5795,2786,5797,2801,5799,2804,5801,1027],{},[62,5796,597],{},[62,5798,1215],{},[62,5800,436],{},[62,5802,2807],{},[1915,5804,2810],{"id":2810},[110,5806,2813,5807,2816,5809,2820,5811,2823],{},[62,5808,1262],{},[62,5810,2819],{},[62,5812,2807],{},[1915,5814,2826],{"id":2826},[110,5816,2829,5817,2833],{},[62,5818,2832],{},[1915,5820,2836],{"id":2836},[110,5822,2839,5823,2843,5825,2846,5827,2850],{},[62,5824,2842],{},[62,5826,1543],{},[62,5828,2849],{},[1915,5830,2853],{"id":2853},[110,5832,2856,5833,2859],{},[62,5834,2181],{},[138,5836,2863],{"id":2862},[1915,5838,2867],{"id":2866},[380,5840,5841,5845,5847,5851],{},[203,5842,2872,5843,2875],{},[62,5844,1800],{},[203,5846,2878],{},[203,5848,2881,5849,1027],{},[62,5850,1786],{},[203,5852,2886],{},[110,5854,2889],{},[1915,5856,2892],{"id":2892},[149,5858,5859],{"className":2895,"code":2896,"language":2897,"meta":155,"style":155},[62,5860,5861,5869],{"__ignoreMap":155},[773,5862,5863,5865,5867],{"class":775,"line":776},[773,5864,2904],{"class":787},[773,5866,2907],{"class":794},[773,5868,2910],{"class":794},[773,5870,5871,5873,5875],{"class":775,"line":962},[773,5872,2904],{"class":787},[773,5874,2907],{"class":794},[773,5876,2919],{"class":794},[110,5878,5879,2924],{},[62,5880,2075],{},[1915,5882,2928],{"id":2927},[149,5884,5885],{"className":2895,"code":2931,"language":2897,"meta":155,"style":155},[62,5886,5887,5895,5903,5911,5919,5927,5935,5939],{"__ignoreMap":155},[773,5888,5889,5891,5893],{"class":775,"line":776},[773,5890,2904],{"class":787},[773,5892,2907],{"class":794},[773,5894,2942],{"class":794},[773,5896,5897,5899,5901],{"class":775,"line":962},[773,5898,2904],{"class":787},[773,5900,2907],{"class":794},[773,5902,2951],{"class":794},[773,5904,5905,5907,5909],{"class":775,"line":35},[773,5906,2904],{"class":787},[773,5908,2907],{"class":794},[773,5910,2960],{"class":794},[773,5912,5913,5915,5917],{"class":775,"line":985},[773,5914,2904],{"class":787},[773,5916,2907],{"class":794},[773,5918,2969],{"class":794},[773,5920,5921,5923,5925],{"class":775,"line":995},[773,5922,2904],{"class":787},[773,5924,2907],{"class":794},[773,5926,2978],{"class":794},[773,5928,5929,5931,5933],{"class":775,"line":1606},[773,5930,2983],{"class":787},[773,5932,2986],{"class":794},[773,5934,2989],{"class":972},[773,5936,5937],{"class":775,"line":1617},[773,5938,2995],{"class":2994},[773,5940,5941,5943,5945,5947],{"class":775,"line":1626},[773,5942,2983],{"class":787},[773,5944,3002],{"class":794},[773,5946,3005],{"class":794},[773,5948,3008],{"class":794},[110,5950,3011,5951,3014,5953,3017],{},[62,5952,1938],{},[62,5954,1938],{},[1915,5956,3020],{"id":3020},[149,5958,5959],{"className":2895,"code":3023,"language":2897,"meta":155,"style":155},[62,5960,5961,5971,5981,5989,5997,6013],{"__ignoreMap":155},[773,5962,5963,5965,5967,5969],{"class":775,"line":776},[773,5964,3030],{"class":787},[773,5966,3033],{"class":972},[773,5968,3036],{"class":794},[773,5970,3039],{"class":794},[773,5972,5973,5975,5977,5979],{"class":775,"line":962},[773,5974,3030],{"class":787},[773,5976,3033],{"class":972},[773,5978,3036],{"class":794},[773,5980,3050],{"class":794},[773,5982,5983,5985,5987],{"class":775,"line":35},[773,5984,3055],{"class":787},[773,5986,3058],{"class":972},[773,5988,3061],{"class":794},[773,5990,5991,5993,5995],{"class":775,"line":985},[773,5992,3055],{"class":787},[773,5994,3058],{"class":972},[773,5996,3070],{"class":794},[773,5998,5999,6001,6003,6005,6007,6009,6011],{"class":775,"line":995},[773,6000,3075],{"class":787},[773,6002,3078],{"class":972},[773,6004,3081],{"class":972},[773,6006,3084],{"class":794},[773,6008,3087],{"class":972},[773,6010,3090],{"class":794},[773,6012,3093],{"class":794},[773,6014,6015,6017,6019,6021,6023,6025,6027],{"class":775,"line":1606},[773,6016,3075],{"class":787},[773,6018,3078],{"class":972},[773,6020,3081],{"class":972},[773,6022,3084],{"class":794},[773,6024,3087],{"class":972},[773,6026,3090],{"class":794},[773,6028,3110],{"class":794},[1915,6030,3113],{"id":3113},[110,6032,3116],{},[149,6034,6035],{"className":2895,"code":3119,"language":2897,"meta":155,"style":155},[62,6036,6037],{"__ignoreMap":155},[773,6038,6039,6041,6043,6045,6047,6049,6051,6053],{"class":775,"line":776},[773,6040,2904],{"class":787},[773,6042,2907],{"class":794},[773,6044,3130],{"class":794},[773,6046,3133],{"class":972},[773,6048,3136],{"class":952},[773,6050,3139],{"class":794},[773,6052,3142],{"class":779},[773,6054,806],{"class":952},[110,6056,3147,6057,3150,6059,3153,6061,1027],{},[62,6058,187],{},[62,6060,187],{},[62,6062,183],{},[138,6064,3159],{"id":3158},[1915,6066,3163],{"id":3162},[110,6068,3166,6069,3169,6071,3172,6073,408,6075,3179,6077,3183,6079,3186],{},[62,6070,124],{},[62,6072,2035],{},[62,6074,3175],{},[62,6076,3178],{},[62,6078,3182],{},[62,6080,187],{},[1915,6082,3189],{"id":3189},[110,6084,3192,6085,3195,6087,3198,6089,3202,6091,3205],{},[62,6086,1786],{},[62,6088,2035],{},[62,6090,3201],{},[62,6092,1994],{},[1915,6094,3209],{"id":3208},[110,6096,3212,6097,3216,6099,3220],{},[62,6098,3215],{},[62,6100,3219],{},[1915,6102,3223],{"id":3223},[110,6104,3226,6105,408,6107,3231],{},[62,6106,1348],{},[62,6108,1351],{},[1915,6110,3234],{"id":3234},[110,6112,3237,6113,3240],{},[62,6114,1008],{},[1915,6116,3244],{"id":3243},[110,6118,3247,6119,3251,6121,3254],{},[62,6120,3250],{},[62,6122,187],{},[1915,6124,3258],{"id":3257},[110,6126,3261,6127,3264,6129,3267],{},[62,6128,187],{},[62,6130,187],{},[110,6132,3270,6133,3274,6135,3277],{},[62,6134,3273],{},[62,6136,2157],{},[1915,6138,3281],{"id":3280},[110,6140,3284,6141,3287],{},[62,6142,1800],{},[138,6144,3291],{"id":3290},[110,6146,3294],{},[149,6148,6150],{"className":6149,"code":3298,"language":154,"meta":155},[152],[62,6151,3298],{"__ignoreMap":155},[110,6153,3303],{},[110,6155,3306,6156,3309],{},[62,6157,1793],{},[110,6159,3312,6160,3316,6162,3319],{},[62,6161,3315],{},[62,6163,1804],{},[138,6165,3323],{"id":3322},[110,6167,3326],{},[110,6169,3329],{},[110,6171,3332],{},[110,6173,3335],{},[110,6175,3338],{},[110,6177,3341,6178,3344],{},[62,6179,128],{},[110,6181,3347],{},[138,6183,3351],{"id":3350},[110,6185,3354],{},[380,6187,6188,6192,6198,6206,6210,6214,6222,6224],{},[203,6189,3359,6190,3362],{},[62,6191,418],{},[203,6193,3359,6194,423,6196,3370],{},[62,6195,3367],{},[62,6197,2807],{},[203,6199,3359,6200,408,6202,408,6204,3379],{},[62,6201,939],{},[62,6203,1124],{},[62,6205,1262],{},[203,6207,3382,6208,3385],{},[62,6209,505],{},[203,6211,3359,6212,3390],{},[62,6213,655],{},[203,6215,3359,6216,408,6218,408,6220,3402],{},[62,6217,3395],{},[62,6219,3398],{},[62,6221,3401],{},[203,6223,3405],{},[203,6225,3408],{},[110,6227,3411],{},[200,6229,6230,6232,6234,6236,6238,6240],{},[203,6231,3416],{},[203,6233,3419],{},[203,6235,3422],{},[203,6237,3425],{},[203,6239,3428],{},[203,6241,3431],{},[110,6243,3434],{},[138,6245,3437],{"id":3437},[200,6247,6248,6253,6258,6263,6268,6273,6278,6283],{},[203,6249,6250],{},[3443,6251,3448],{"href":3445,"rel":6252},[3447],[203,6254,6255],{},[3443,6256,3455],{"href":3453,"rel":6257},[3447],[203,6259,6260],{},[3443,6261,3462],{"href":3460,"rel":6262},[3447],[203,6264,6265],{},[3443,6266,3469],{"href":3467,"rel":6267},[3447],[203,6269,6270],{},[3443,6271,3476],{"href":3474,"rel":6272},[3447],[203,6274,6275],{},[3443,6276,3483],{"href":3481,"rel":6277},[3447],[203,6279,6280],{},[3443,6281,3490],{"href":3488,"rel":6282},[3447],[203,6284,6285],{},[3443,6286,3497],{"href":3495,"rel":6287},[3447],[3499,6289,3501],{},{"title":155,"searchDepth":962,"depth":962,"links":6291},[6292,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,6303,6304,6305,6306,6307,6308,6309,6310,6311,6312,6313,6320,6321,6322,6323,6324,6329,6330,6331,6341,6348,6358,6359,6360,6361],{"id":140,"depth":962,"text":141},{"id":194,"depth":962,"text":195},{"id":229,"depth":962,"text":230},{"id":365,"depth":962,"text":366},{"id":430,"depth":962,"text":431},{"id":562,"depth":962,"text":563},{"id":707,"depth":962,"text":708},{"id":823,"depth":962,"text":824},{"id":894,"depth":962,"text":895},{"id":932,"depth":962,"text":933},{"id":1057,"depth":962,"text":1058},{"id":1118,"depth":962,"text":1119},{"id":1204,"depth":962,"text":1205},{"id":1301,"depth":962,"text":1302},{"id":1375,"depth":962,"text":1376},{"id":1446,"depth":962,"text":1447},{"id":1505,"depth":962,"text":1506},{"id":1547,"depth":962,"text":1548},{"id":1722,"depth":962,"text":1723},{"id":1811,"depth":962,"text":1812},{"id":1870,"depth":962,"text":1871},{"id":1909,"depth":962,"text":1910,"children":6314},[6315,6316,6317,6318,6319],{"id":1917,"depth":35,"text":1920},{"id":1935,"depth":35,"text":1938},{"id":1950,"depth":35,"text":1953},{"id":2007,"depth":35,"text":2010},{"id":2041,"depth":35,"text":1786},{"id":2092,"depth":962,"text":2093},{"id":2175,"depth":962,"text":2176},{"id":2317,"depth":962,"text":2318},{"id":2389,"depth":962,"text":2390},{"id":2489,"depth":962,"text":2490,"children":6325},[6326,6327,6328],{"id":2496,"depth":35,"text":2497},{"id":2537,"depth":35,"text":2538},{"id":2581,"depth":35,"text":2582},{"id":2635,"depth":962,"text":2636},{"id":2718,"depth":962,"text":2719},{"id":2752,"depth":962,"text":2753,"children":6332},[6333,6334,6335,6336,6337,6338,6339,6340],{"id":2756,"depth":35,"text":2757},{"id":2769,"depth":35,"text":2769},{"id":2781,"depth":35,"text":2781},{"id":2793,"depth":35,"text":2793},{"id":2810,"depth":35,"text":2810},{"id":2826,"depth":35,"text":2826},{"id":2836,"depth":35,"text":2836},{"id":2853,"depth":35,"text":2853},{"id":2862,"depth":962,"text":2863,"children":6342},[6343,6344,6345,6346,6347],{"id":2866,"depth":35,"text":2867},{"id":2892,"depth":35,"text":2892},{"id":2927,"depth":35,"text":2928},{"id":3020,"depth":35,"text":3020},{"id":3113,"depth":35,"text":3113},{"id":3158,"depth":962,"text":3159,"children":6349},[6350,6351,6352,6353,6354,6355,6356,6357],{"id":3162,"depth":35,"text":3163},{"id":3189,"depth":35,"text":3189},{"id":3208,"depth":35,"text":3209},{"id":3223,"depth":35,"text":3223},{"id":3234,"depth":35,"text":3234},{"id":3243,"depth":35,"text":3244},{"id":3257,"depth":35,"text":3258},{"id":3280,"depth":35,"text":3281},{"id":3290,"depth":962,"text":3291},{"id":3322,"depth":962,"text":3323},{"id":3350,"depth":962,"text":3351},{"id":3437,"depth":962,"text":3437},{},{"title":105,"description":3576},[3586,3587,274,3588,322,314,3589],{"id":6366,"title":6367,"body":6368,"category":6518,"date":3575,"description":6519,"extension":3577,"hidden":3578,"meta":6520,"navigation":31,"path":6521,"publishedAt":3581,"seo":6522,"slug":6523,"stem":6524,"tags":6525,"updated":3575,"__hash__":6530},"writing\u002Fwriting\u002F2026-07-26-trading-cognition-and-compound-reality.md","交易是为了赚钱还是验证认知：从量化门槛到复利现实",{"type":107,"value":6369,"toc":6507},[6370,6373,6377,6380,6383,6387,6390,6393,6396,6399,6402,6405,6408,6412,6415,6435,6438,6449,6452,6455,6469,6472,6475,6478,6481,6484,6488,6491,6494,6498,6501,6504],[110,6371,6372],{},"刷到一个清华博士的视频，说想赚钱就去量化。但这对于普通人的门槛是不是太高？就算跨过去了，市场容量真的够赚到钱吗？带着这些问题和 AI 聊了一圈，从量化门槛一路聊到\"你到底为什么做交易\"，把思路理了一遍。",[138,6374,6376],{"id":6375},"机构量化-vs-系统化交易","机构量化 vs 系统化交易",[110,6378,6379],{},"机构量化（幻方、九坤级别）的门槛确实很高：博士学历、海量 tick 数据、专用算力、上亿起步资金，而且赛道内卷、alpha 衰减快、容量有限。",[110,6381,6382],{},"但普通人能做的是\"系统化交易\"而非机构量化。20 日均线 + 严格止损 + 行业 ETF，这套逻辑本质就是量化思维。真正的问题不是门槛，是把博士生的参照系错误地套用在自己身上。这类建议对清北学生可操作，对大多数人是废话，因为它省略了关键前提：清北学生的起点已经过滤掉 90% 的竞争。",[138,6384,6386],{"id":6385},"只是为了赚钱可能是对自己的贬低","\"只是为了赚钱\"可能是对自己的贬低",[110,6388,6389],{},"我说做交易只是为了赚钱。但仔细想想，我对系统和机制本身一直有探究欲--止损逻辑、去中心化的交易所架构、自己项目的技术选型会追求优雅和性能。真正纯粹为钱的人不会纠结止损后策略逻辑对不对。",[110,6391,6392],{},"如果账户明天清零，还会不会继续研究这套系统？答案可能是会的。因为做交易挣不挣钱是对我认知是否正确的检验，猜对了奖励，猜错了亏钱。我在市场里的目的是验证认知，想知道自己是怎样的人。",[110,6394,6395],{},"核心驱动不是钱（AI说的 我持怀疑态度），是\"认知闭环\"--市场是不会说谎的裁判，钱只是计分板。",[138,6397,6398],{"id":6398},"一个需要警惕的特质",[110,6400,6401],{},"对\"是否正确\"极度敏感的人，容易把坚持系统等同于坚持自我，把止损等同于承认\"我是错的\"。如果哪天开始扛单不是因为信号而是无法接受\"我看错了\"，这个特质就从优势变成风险。",[110,6403,6404],{},"关键区分：把\"验证认知\"和\"验证自我价值\"分开。市场说这笔交易错了，不等于整个判断体系错了。我相信市场大部分时间是对的，应该是我往市场靠去检验自己，而不是市场错的时候坚持自己。但空仓时说这话容易，真正重仓浮亏 5%、系统说止损但脑子里说\"只是短期波动\"的那一刻，这套认知还在不在才是真考验。",[110,6406,6407],{},"止损系统的价值不只是风控，更是强制执行那些理性认同但情绪会动摇的信念。",[138,6409,6411],{"id":6410},"_10-万到几百万数学可行执行淘汰大多数人","10 万到几百万：数学可行，执行淘汰大多数人",[110,6413,6414],{},"市场容量直接排除--A 股日均成交额 8000 亿到 1.2 万亿，几十万到几百万对市场影响约等于零。真实难度分三段：",[200,6416,6417,6423,6429],{},[203,6418,6419,6422],{},[178,6420,6421],{},"10 万到 100 万","：拼策略稳定性和纪律一致性",[203,6424,6425,6428],{},[178,6426,6427],{},"100 万到 500 万","：拼复利需要时间，以及账户变大后心态是否变化（100 万亏 10% 是 10 万，感受和 10 万亏 10% 完全不同）",[203,6430,6431,6434],{},[178,6432,6433],{},"500 万以上","：拼市场风格切换时系统能否不变形",[110,6436,6437],{},"参照现有系统严格执行，年化 15%-30% 的历史区间成立。三种情景的复利推算：",[200,6439,6440,6443,6446],{},[203,6441,6442],{},"**保守 15%**：10 万到 100 万约 17 年，到 500 万约 39 年",[203,6444,6445],{},"**中性 20%**：10 万到 100 万约 13 年，到 500 万约 21 年",[203,6447,6448],{},"**乐观 25%**：10 万到 100 万约 11 年，到 500 万约 15 年",[110,6450,6451],{},"数学上可行，但\"10 年严格执行\"淘汰的人比策略本身多。年化均值背后有巨大的方差，最大风险不是策略本身，而是震荡市连续止损后大多数人会改变规则。",[138,6453,6454],{"id":6454},"人群中能走到哪",[200,6456,6457,6460,6463,6466],{},[203,6458,6459],{},"能长期跑赢通胀：前 20%",[203,6461,6462],{},"年化 15%+ 持续 5 年以上：前 5%",[203,6464,6465],{},"系统化策略 10 年不变形：前 2-3%",[203,6467,6468],{},"10 万做到 500 万：前 1-2%",[110,6470,6471],{},"这个门槛不是天才门槛，而是纪律和时间门槛。输给认知的少，输给自己的多。",[138,6473,6474],{"id":6474},"交易不是生活的全部",[110,6476,6477],{},"正常认真上班拿现金流，有自己的爱好爱好，偶尔玩玩游戏，继续交易探索市场和世界。每个部分各司其职：工作管现金流，运动游戏管切换放松，交易是认知战场。",[110,6479,6480],{},"这个心态本身是优势。很多人把交易当生活全部，盯盘过度、情绪放大、决策变差。有工作提供现金流、有运动游戏切换状态，交易在这个结构里心理压力更小，压力小反而更容易执行系统。",[110,6482,6483],{},"但有一个前提：执行密度。想清楚和做到之间有 gap，关键不是今天想通了，而是明天还在做。",[138,6485,6487],{"id":6486},"一个佩服的-up-主","一个佩服的 UP 主",[110,6489,6490],{},"关注过一个 UP 主，清华毕业三十多岁，提过资产千万级。他的操作和认知确实让人佩服：航运信息差套利没记错一天挣了20w，6月8日暴跌开中证 500 期权多单为国护盘，科技股暴跌后判断主线仍是科技股事后看来也是对的。他还分享打新、股东福利等内容。",[110,6492,6493],{},"他的宏观判断和产业理解思路值得学，具体操作和套利思路也有参考价值。没有实盘记录这点确实无法验证，但判断以及操作确实很牛逼。",[138,6495,6497],{"id":6496},"a-股打新现状","A 股打新现状",[110,6499,6500],{},"查了一下 A 股打新的数据：2024 年新股首日平均涨幅 253%，单签平均收益约 2 万元；2025 年至 6 月，45 只新股无一破发。但主板网上中签率仅 0.03% 左右，参与人数超 1100 万户，普通人中签概率极低，更像是彩票。网下机构打新年化约 4.5%，但门槛是亿级资金账户。包括今年以来的一些大肉签（比如沐曦股份 摩尔线程 最近的长鑫存储）上市当日涨幅都很夸张，随便中几签都是几万块。可惜科创板门槛高啊。（我什么时候才能有50w）",[138,6502,6503],{"id":6503},"止损永远是最重要的",[110,6505,6506],{},"按照均线止损太死板，套到很多个股上买点太高了。但不管用什么方式入场，止损永远是第一位的。买点可以慢慢校准，进场理由可以有很多种，但止损必须进场前定好，而且不能因为浮亏就移动它。止损一旦守不住，什么策略都白搭。在市场里还是先活下来比较重要。",{"title":155,"searchDepth":962,"depth":962,"links":6508},[6509,6510,6511,6512,6513,6514,6515,6516,6517],{"id":6375,"depth":962,"text":6376},{"id":6385,"depth":962,"text":6386},{"id":6398,"depth":962,"text":6398},{"id":6410,"depth":962,"text":6411},{"id":6454,"depth":962,"text":6454},{"id":6474,"depth":962,"text":6474},{"id":6486,"depth":962,"text":6487},{"id":6496,"depth":962,"text":6497},{"id":6503,"depth":962,"text":6503},"交易","从清华博士的建议想要赚钱就去做量化出发，梳理普通人做系统化交易的现实路径、人群比例和自我认知驱动。",{},"\u002Fwriting\u002F2026-07-26-trading-cognition-and-compound-reality",{"title":6367,"description":6519},"trading-cognition-and-compound-reality","writing\u002F2026-07-26-trading-cognition-and-compound-reality",[6518,6526,6527,6528,6529],"量化","认知","复利","系统化交易","ncRskTdQt3kCImby9qKSkM5aUBbcdCb-MjUngbbkfEs",{"id":6532,"title":6533,"body":6534,"category":6518,"date":3575,"description":6710,"extension":3577,"hidden":3578,"meta":6711,"navigation":31,"path":6712,"publishedAt":6713,"seo":6714,"slug":6715,"stem":6716,"tags":6717,"updated":4043,"__hash__":6724},"writing\u002Fwriting\u002F2026-07-26-btc-synthetic-futures-arbitrage.md","BTC期权合成期货套利：用定价偏差锁定无风险利润",{"type":107,"value":6535,"toc":6701},[6536,6539,6543,6546,6549,6553,6559,6567,6570,6573,6576,6582,6588,6591,6595,6598,6601,6605,6612,6618,6621,6624,6638,6643,6651,6657,6662,6673,6678,6688,6691,6695,6698],[110,6537,6538],{},"看到一套量化交易系统看板：年化 197%、极低回撤、多平仓少持仓。核心策略是利用 Deribit 期权市场合成的虚拟期货价格与 Binance 永续合约价格之间的定价偏差进行对冲套利。当时对着看板追问了 AI 几轮，才把这套合成期货套利的逻辑理清楚。这篇文章是给自己留的复盘笔记。",[138,6540,6542],{"id":6541},"deribit-是什么","Deribit 是什么",[110,6544,6545],{},"Deribit 是目前加密货币期权交易量最大的交易所，总部在巴拿马，2016 年上线。BTC 和 ETH 期权的主流流动性都在这里，行权价、到期日覆盖范围广，机构参与度高。它的期权以币本位结算（BTC 期权用 BTC 结算），也支持永续合约和交割期货。",[110,6547,6548],{},"正因为 Deribit 在期权市场的深度优势，它的期权定价成为合成期货策略的基础端。另一端选 Binance 永续合约，是因为 Binance 流动性最好、API 稳定、支持开空，适合做对冲腿。",[138,6550,6552],{"id":6551},"核心概念合成期货","核心概念：合成期货",[110,6554,6555,6556,1027],{},"期权平价公式告诉我们：",[178,6557,6558],{},"买入 Call + 卖出 Put = 合成期货多头",[200,6560,6561,6564],{},[203,6562,6563],{},"买入 Call（看涨期权）：涨了赚，跌了不亏",[203,6565,6566],{},"卖出 Put（看跌期权）：涨了没事，跌了亏",[110,6568,6569],{},"两者组合后的盈亏轨迹，与直接持有 BTC 现货或期货完全一致——涨了赚、跌了亏。这就是\"合成期货\"。",[138,6571,6572],{"id":6572},"两种套利方向",[110,6574,6575],{},"关键在于：Deribit 期权合成的期货价格，和 Binance 永续合约的价格，经常不一致。",[110,6577,6578,6581],{},[178,6579,6580],{},"转换套利（Conversion \u002F SF+BS）","：当合成期货价格 > Binance 期货价格时，在 Deribit 卖出合成期货（卖出 Call + 买入 Put），同时在 Binance 买入永续合约。",[110,6583,6584,6587],{},[178,6585,6586],{},"反转套利（Reversal \u002F BF+SS）","：当合成期货价格 \u003C Binance 期货价格时，在 Deribit 买入合成期货（买入 Call + 卖出 Put），同时在 Binance 卖出永续合约。",[110,6589,6590],{},"本质上就是\"哪边便宜哪边买，哪边贵了哪边卖\"。",[138,6592,6594],{"id":6593},"对冲机制为什么涨跌都不怕","对冲机制：为什么涨跌都不怕",[110,6596,6597],{},"币安端永远做与 Deribit 完全相反的动作。Deribit 开合成多头，Binance 必开永续空头。BTC 暴涨暴跌时，两端的盈亏完全抵消，行情风险归零。",[110,6599,6600],{},"利润不来自方向判断，而是开仓瞬间两端的定价差。期权到期结算时，合成价格强制回归现货价，基差收敛完成无风险落袋。",[138,6602,6604],{"id":6603},"一个关键区分行权价-现价","一个关键区分：行权价 ≠ 现价",[110,6606,6607,6608,6611],{},"看板中的合约代码 ",[62,6609,6610],{},"BTC-10JUN26-62000","，62000 是行权价，不是当时的市场现价。当时 Binance 的标记价约为 62,991 美元。因为现价高于行权价，看涨期权呈浮盈状态，看跌期权的权利金已被吃满。",[110,6613,6614,6615,1027],{},"比对套利机会时，标的不是行权价本身，而是",[178,6616,6617],{},"合成总成本 = 行权价 + 买Call权利金 - 卖Put权利金",[138,6619,6620],{"id":6620},"完整数值演练",[110,6622,6623],{},"设定场景：",[200,6625,6626,6629,6632],{},[203,6627,6628],{},"Binance 现价：$63,000",[203,6630,6631],{},"Deribit 行权价 $62,000 的期权：Call 权利金 $1,500，Put 权利金 $300",[203,6633,6634,6635],{},"合成空头总成本：$62,000 + $1,500 - $300 = ",[178,6636,6637],{},"$63,200",[110,6639,6640],{},[178,6641,6642],{},"开仓动作：",[200,6644,6645,6648],{},[203,6646,6647],{},"Binance 开多，$63,000",[203,6649,6650],{},"Deribit 合成空头（卖 Call + 买 Put），$63,200",[110,6652,6653,6654],{},"开仓瞬间锁定利润：$63,200 - $63,000 = ",[178,6655,6656],{},"$200",[110,6658,6659],{},[178,6660,6661],{},"极端行情 A - 暴涨至 $80,000：",[200,6663,6664,6667,6670],{},[203,6665,6666],{},"Binance 多头盈利：+$17,000",[203,6668,6669],{},"Deribit 合成空头亏损：-$16,800",[203,6671,6672],{},"合计：**+$200**",[110,6674,6675],{},[178,6676,6677],{},"极端行情 B - 暴跌至 $40,000：",[200,6679,6680,6683,6686],{},[203,6681,6682],{},"Binance 多头亏损：-$23,000",[203,6684,6685],{},"Deribit 合成空头盈利：+$23,200",[203,6687,6672],{},[110,6689,6690],{},"无论涨跌，总利润始终为开仓时锁定的 $200。到期自动结算收敛，整个过程是左手倒右手，利润在开仓那一下就已经确定了。",[138,6692,6694],{"id":6693},"模型不难难的是工程","模型不难，难的是工程",[110,6696,6697],{},"这套套利的交易系统作者说过一句话：套利模型倒是不难，难的是一些工程上的事情。",[110,6699,6700],{},"确实，逻辑上这就是一个低买高卖的价差捕捉，但实际跑起来要处理的问题远比模型复杂：两个交易所的 API 延迟差异、订单簿深度和滑点、手续费摩擦对利润空间的侵蚀、资金在两个平台之间的划转效率、期权到期时的自动结算对齐、极端行情下的风控（硬止损、Gamma 检查、行情降级）、以及资金容量上限——能套的价差就那么多，资金量大了反而吃不掉。这些工程细节才是决定系统能不能真正赚钱的关键。",{"title":155,"searchDepth":962,"depth":962,"links":6702},[6703,6704,6705,6706,6707,6708,6709],{"id":6541,"depth":962,"text":6542},{"id":6551,"depth":962,"text":6552},{"id":6572,"depth":962,"text":6572},{"id":6593,"depth":962,"text":6594},{"id":6603,"depth":962,"text":6604},{"id":6620,"depth":962,"text":6620},{"id":6693,"depth":962,"text":6694},"看到一套量化套利系统后追问AI，拆解Deribit合成期货与Binance永续合约之间的跨平台套利逻辑，含完整数值演练。",{},"\u002Fwriting\u002F2026-07-26-btc-synthetic-futures-arbitrage","2026-07-26T13:37:11+08:00",{"title":6533,"description":6710},"btc-synthetic-futures-arbitrage","writing\u002F2026-07-26-btc-synthetic-futures-arbitrage",[6718,6719,6720,6721,6722,6723],"BTC","期权","套利","合成期货","Deribit","Binance","o_rfqacyzAeRyNmp-ceWG6OvHBlEmjo5vFyZhRVC06A",{"id":6726,"title":6727,"body":6728,"category":3574,"date":3575,"description":6893,"extension":3577,"hidden":3578,"meta":6894,"navigation":31,"path":6895,"publishedAt":6896,"seo":6897,"slug":6898,"stem":6899,"tags":6900,"updated":4043,"__hash__":6904},"writing\u002Fwriting\u002F2026-07-26-why-vector-database.md","向量库到底有什么用",{"type":107,"value":6729,"toc":6886},[6730,6734,6737,6748,6751,6765,6769,6772,6786,6789,6793,6796,6799,6819,6826,6829,6832,6835,6853,6856,6859,6883],[138,6731,6733],{"id":6732},"sql-存个相同列-join-一下不就行了","SQL 存个相同列 join 一下不就行了？",[110,6735,6736],{},"这是我最初的问题。看起来很合理：embedding 不就是一串数字吗，存个字段，SQL join 一下就完事了。",[110,6738,6739,6740,6743,6744,6747],{},"但 SQL 擅长的是 ",[62,6741,6742],{},"WHERE A = B"," 这种精确匹配。Embedding 是高维浮点数组，代表语义特征。两句话语义相似，它们的向量数值却绝不相等。",[62,6745,6746],{},"WHERE Va = Vb"," 直接返回空。",[110,6749,6750],{},"所以向量库真正在做的事情有两层：",[380,6752,6753,6759],{},[203,6754,6755,6758],{},[178,6756,6757],{},"数学计算","：用余弦相似度等算法算高维空间的\"距离\"，而不是判等。",[203,6760,6761,6764],{},[178,6762,6763],{},"专属索引（ANN 算法）","：传统 B-Tree 索引对高维数据失效，会退化成全表扫描。向量库用 HNSW 之类的索引在高维空间建\"地图\"，做到毫秒级近似最近邻检索。",[138,6766,6768],{"id":6767},"那-rag-到底是怎么跑的","那 RAG 到底是怎么跑的",[110,6770,6771],{},"搞清楚向量库的作用之后，整个 RAG 链路就顺了：",[380,6773,6774,6777,6780,6783],{},[203,6775,6776],{},"用户提问 -> 经过 Embedding 模型 -> 转成向量",[203,6778,6779],{},"拿向量去向量库里找最近的文档片段",[203,6781,6782],{},"找到的相关资料 + 用户原问题一起拼成 Prompt 发给大模型",[203,6784,6785],{},"大模型结合资料回答，减少幻觉",[110,6787,6788],{},"核心就是：输入给大模型之前，先用向量检索把相关上下文捞回来，拼在一起再喂进去。",[138,6790,6792],{"id":6791},"全量召回还是选几条","全量召回还是选几条？",[110,6794,6795],{},"这是第二个直觉问题：如果数据库里相关的东西很多，比如 50 页文本都沾边，全部塞进大模型？还是挑几条最相关的？",[110,6797,6798],{},"答案是必须精选截断。原因有三个：",[200,6800,6801,6807,6813],{},[203,6802,6803,6806],{},[178,6804,6805],{},"上下文窗口有限","：大模型的 Context Window 有上限，塞不下就是塞不下。",[203,6808,6809,6812],{},[178,6810,6811],{},"中间迷失","：上下文太长时，大模型容易忽略中间的内容。",[203,6814,6815,6818],{},[178,6816,6817],{},"Token 费用","：输入越长越贵。",[110,6820,6821,6822,6825],{},"所以标准做法是设置 ",[62,6823,6824],{},"top_k","（比如 limit=5），只取相似度最高的前 N 个片段。入库之前也先把长文本切成小块（比如每段 500 字），精准匹配局部内容，避免带入无关段落。",[138,6827,6828],{"id":6828},"但我的数据量没那么大",[110,6830,6831],{},"说到这里我反应过来了--我的复盘日志根本没多少数据。几万字而已，全塞进去不就完了？",[110,6833,6834],{},"确实可以。小数据量下\"全上下文注入\"更简单有效：",[200,6836,6837,6843],{},[203,6838,6839,6842],{},[178,6840,6841],{},"逻辑完整","：保留完整的时间线和因果链，不会因为切片断章取义。",[203,6844,6845,6848,6849,6852],{},[178,6846,6847],{},"架构极简","：直接 SQL ",[62,6850,6851],{},"SELECT *","，全部喂给大模型，不需要向量索引和切片逻辑。",[138,6854,6855],{"id":6855},"那向量库还有没有意义",[110,6857,6858],{},"有。即便是小数据量，向量库（或 SQL 的向量插件）仍然有几个不可替代的能力：",[200,6860,6861,6871,6877],{},[203,6862,6863,6866,6867,6870],{},[178,6864,6865],{},"模糊语义匹配","：日志里写的是\"肝到两点\"或\"披星戴月\"，向量检索能匹配到\"加班\"的语义。传统 SQL ",[62,6868,6869],{},"LIKE"," 搜不到这种。",[203,6872,6873,6876],{},[178,6874,6875],{},"扩展性","：日志会越积越多，一两年后可能就塞不进上下文窗口了。向量检索的成本随数据量亚线性增长，全量注入则是线性上升。",[203,6878,6879,6882],{},[178,6880,6881],{},"聚类与挖掘","：基于向量距离可以做无标签的主题归类和情绪分析，这是全量注入给大模型做不到的。",[110,6884,6885],{},"所以比较务实的方案是：短周期（比如近一周）的分析直接 SQL 全量提取，喂给大模型；跨年度的特定问题搜索走向量匹配。两条路各管各的，不冲突。",{"title":155,"searchDepth":962,"depth":962,"links":6887},[6888,6889,6890,6891,6892],{"id":6732,"depth":962,"text":6733},{"id":6767,"depth":962,"text":6768},{"id":6791,"depth":962,"text":6792},{"id":6828,"depth":962,"text":6828},{"id":6855,"depth":962,"text":6855},"从\"SQL 加个列不就行了\"到理解向量检索和 RAG 的完整链路，以及小数据量场景下到底该怎么选。",{},"\u002Fwriting\u002F2026-07-26-why-vector-database","2026-07-26T00:09:14+08:00",{"title":6727,"description":6893},"why-vector-database","writing\u002F2026-07-26-why-vector-database",[6901,6902,6903],"向量数据库","Embedding","RAG","hdqIEfJHF7KE9cYD971ShCFoCR9FbJVBIvdlCmdrbf4",1785763813111]