<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>朝朝听雨</title><description>物物而不物于物，念念而不念于念</description><link>http://rainzt.cn/</link><templateTheme>Aemeath</templateTheme><templateThemeVersion>6.13.5</templateThemeVersion><templateThemeUrl>https://rainzt.cn/</templateThemeUrl><lastBuildDate>2026年9月3日 08:18:43</lastBuildDate><item><title>自动上架系统：为跨境电商打造的高端批量发布中枢</title><link>http://rainzt.cn/posts/auto-listing-system/</link><guid isPermaLink="true">http://rainzt.cn/posts/auto-listing-system/</guid><description>一套围绕 BigSeller、Shopee 和 TikTok Shop 打造的跨境电商自动上架系统，把表格、图库、视频、AI 标题、店铺状态和发布流程串成可监控、可复用、可扩展的自动化产品。</description><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;项目定位&lt;a href=&quot;#项目定位&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;自动上架系统不是一个简单的“点按钮脚本”，而是一套面向跨境电商运营场景设计的批量发布中枢。它把 Excel 表格、产品图库、视频素材、AI 标题生成、BigSeller 页面操作、店铺状态确认和运行历史记录串成一条完整链路，让重复、易错、耗时的上架工作变成可配置、可监控、可复盘的产品化流程。&lt;/p&gt;&lt;p&gt;目前系统已经落地到两个核心店铺/平台方向：Shopee 和 TikTok Shop。它们分别拥有独立 Worker、独立运行状态、独立日志和发布策略，能够针对不同平台的页面结构、编辑入口、保存逻辑和成功判定做定制处理。这个基础非常重要，因为真正的电商自动化从来不是“一套流程跑天下”，而是要让每个平台的差异被系统吸收，让运营人员面对的是统一、稳定、清晰的控制台。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;自动上架系统控制台&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/1.JqckWBy-_yVAGv.webp&quot; srcset=&quot;/_astro/1.JqckWBy-_1m038a.webp 640w, /_astro/1.JqckWBy-_Z8cl77.webp 750w, /_astro/1.JqckWBy-_Z1Tuz4l.webp 828w, /_astro/1.JqckWBy-_132TDQ.webp 1080w, /_astro/1.JqckWBy-_f8fk1.webp 1280w, /_astro/1.JqckWBy-_Z2mXe48.webp 1668w, /_astro/1.JqckWBy-_yVAGv.webp 1920w&quot; /&gt;&lt;figcaption&gt;自动上架系统控制台&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;已实现能力&lt;a href=&quot;#已实现能力&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;系统当前以 BigSeller 为核心操作入口，通过内置 Chromium 与 Playwright 自动化完成商品编辑、素材上传、标题生成、变体校验和最终发布。运营人员只需要准备表格和素材，系统就能按照预设模板把复杂步骤拆解执行。&lt;/p&gt;&lt;p&gt;它已经覆盖了批量上架中最容易出错的环节：自动搜索商品、进入编辑页、删除旧图、上传主图和详情图、上传视频、绑定变体图、生成或拼接商品标题、发布前截图留档、成功后关闭页面，并把每一次运行沉淀为历史记录。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;任务运行面板&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/2.Df3gN4pj_Z10vvlR.webp&quot; srcset=&quot;/_astro/2.Df3gN4pj_Z19cCJC.webp 640w, /_astro/2.Df3gN4pj_2qM6O2.webp 750w, /_astro/2.Df3gN4pj_EtRQN.webp 828w, /_astro/2.Df3gN4pj_Zwpcow.webp 1080w, /_astro/2.Df3gN4pj_Z1kjQIm.webp 1280w, /_astro/2.Df3gN4pj_17KMGq.webp 1668w, /_astro/2.Df3gN4pj_Z10vvlR.webp 1920w&quot; /&gt;&lt;figcaption&gt;任务运行面板&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;模板化：把经验变成配置&lt;a href=&quot;#模板化把经验变成配置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;平板套是当前最成熟的应用场景。系统内置了多套产品模板，例如 360 旋转壳、3Y 折叠壳、书本式保护壳、笔槽款、通用品牌模板等，并按品牌维护图片组、主图序号、变体图序号、AI 分析图片、视频策略、标题结构和机型关键词。&lt;/p&gt;&lt;p&gt;这意味着系统并不是把某一次上架动作写死，而是把“运营经验”沉淀成可调整的模板。今天它可以服务平板保护套，明天也可以扩展到手机壳、配件、家居小商品、数码周边、服饰配件，甚至任何存在规律化素材和规格组合的产品线。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;模板配置&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/3.DF7sOXCk_ZblqaJ.webp&quot; srcset=&quot;/_astro/3.DF7sOXCk_ZixWN1.webp 640w, /_astro/3.DF7sOXCk_Z1MKm3i.webp 750w, /_astro/3.DF7sOXCk_1v8xNp.webp 828w, /_astro/3.DF7sOXCk_hJRLB.webp 1080w, /_astro/3.DF7sOXCk_Zv9Lxe.webp 1280w, /_astro/3.DF7sOXCk_1VURRy.webp 1668w, /_astro/3.DF7sOXCk_ZblqaJ.webp 1920w&quot; /&gt;&lt;figcaption&gt;模板配置&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;AI 标题：让批量发布更像精细运营&lt;a href=&quot;#ai-标题让批量发布更像精细运营&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;系统接入 AI 标题生成能力，可以根据图片内容和模板规则生成更适合平台展示的英文标题。它不仅能做固定标题拼接，也支持 AI 分析图片、随机重排品牌和机型关键词、按平台字符限制控制输出。&lt;/p&gt;&lt;p&gt;这部分的价值不只是“省时间”，更重要的是让批量上架保持足够的内容差异和商品表达质量。对高 SKU、高频发布的跨境店铺来说，标题质量、关键词覆盖和可读性会直接影响商品点击、搜索曝光和后续转化。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;AI 与标题配置&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/4.Do9ty007_Z1pBRBc.webp&quot; srcset=&quot;/_astro/4.Do9ty007_Z1O2AEK.webp 640w, /_astro/4.Do9ty007_1KW8ST.webp 750w, /_astro/4.Do9ty007_Zl53k.webp 828w, /_astro/4.Do9ty007_ZVvyDQ.webp 1080w, /_astro/4.Do9ty007_Z1JqdXG.webp 1280w, /_astro/4.Do9ty007_HEqr6.webp 1668w, /_astro/4.Do9ty007_Z1pBRBc.webp 1920w&quot; /&gt;&lt;figcaption&gt;AI 与标题配置&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;运行可视化：自动化必须可控&lt;a href=&quot;#运行可视化自动化必须可控&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一个高端自动化产品，不能只追求“跑得快”，还要让人知道它正在做什么、做到哪里、哪里可能有风险。这个系统提供实时日志、运行状态、暂停/恢复、店铺信息确认、截图留档、历史记录和 Token 消耗监控。&lt;/p&gt;&lt;p&gt;当任务进入高风险步骤，例如上传素材、匹配变体、发布商品之前，系统会保留关键过程信息，让运营人员可以追踪问题，而不是等任务失败后只看到一个模糊结果。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;实时运行状态&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/5.BoC-MSEP_2pxIeg.webp&quot; srcset=&quot;/_astro/5.BoC-MSEP_Z2vvKJb.webp 640w, /_astro/5.BoC-MSEP_14sXOt.webp 750w, /_astro/5.BoC-MSEP_ZGOf7K.webp 828w, /_astro/5.BoC-MSEP_Z2bx6Ck.webp 1080w, /_astro/5.BoC-MSEP_25JmQL.webp 1280w, /_astro/5.BoC-MSEP_Zwm6wn.webp 1668w, /_astro/5.BoC-MSEP_2pxIeg.webp 1920w&quot; /&gt;&lt;figcaption&gt;实时运行状态&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;双平台基础：Shopee 与 TikTok Shop&lt;a href=&quot;#双平台基础shopee-与-tiktok-shop&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;目前已经实现的两个店铺/平台方向是 Shopee 和 TikTok Shop。Shopee 侧更强调 BigSeller 商品列表、编辑页、素材上传和“保存并发布”的完整链路；TikTok Shop 侧则针对 TK 页面入口、表格起始行、编辑按钮、更新按钮和成功判断做了独立适配。&lt;/p&gt;&lt;p&gt;两条链路共用系统级能力，例如配置中心、内置浏览器、素材管理、历史记录、截图、AI 标题、SSE 日志流，同时保留平台差异化逻辑。这种设计让后续扩展更自然：新增平台不需要推倒重来，而是在统一底座上接入新的 Worker、页面策略和模板规则。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;Shopee 与 TikTok Shop 工作区&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/6.B4sBFD10_VJar.webp&quot; srcset=&quot;/_astro/6.B4sBFD10_2tVSA7.webp 640w, /_astro/6.B4sBFD10_YJukP.webp 750w, /_astro/6.B4sBFD10_ZLxIBo.webp 828w, /_astro/6.B4sBFD10_u337M.webp 1080w, /_astro/6.B4sBFD10_ZiQBc3.webp 1280w, /_astro/6.B4sBFD10_29e3dJ.webp 1668w, /_astro/6.B4sBFD10_VJar.webp 1920w&quot; /&gt;&lt;figcaption&gt;Shopee 与 TikTok Shop 工作区&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;周边工具：从上架延伸到运营维护&lt;a href=&quot;#周边工具从上架延伸到运营维护&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;围绕上架主流程，系统还加入了批量上传表格、批量添加新型号、批量开启折扣、清空素材中心、视频中心、截图中心、历史海报等工具。这些功能看起来像“小工具”，实际是电商运营每天都会碰到的高频维护动作。&lt;/p&gt;&lt;p&gt;把这些动作统一在同一个系统里，能够减少来回切换工具、手动复制路径、重复打开页面带来的损耗，也让自动化从“单点功能”升级成“运营工作台”。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;批量工具&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/7.D_IWXZW2_6eBXK.webp&quot; srcset=&quot;/_astro/7.D_IWXZW2_Z1L2RnQ.webp 640w, /_astro/7.D_IWXZW2_1NVRaN.webp 750w, /_astro/7.D_IWXZW2_2DDdz.webp 828w, /_astro/7.D_IWXZW2_zkUV6.webp 1080w, /_astro/7.D_IWXZW2_ZdyInJ.webp 1280w, /_astro/7.D_IWXZW2_2evV23.webp 1668w, /_astro/7.D_IWXZW2_6eBXK.webp 1920w&quot; /&gt;&lt;figcaption&gt;批量工具&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;可定制方向&lt;a href=&quot;#可定制方向&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这套系统最有价值的地方，是它具备继续定制的空间。平板套只是第一个成熟行业模板，未来可以沿着三个方向继续升级。&lt;/p&gt;&lt;p&gt;第一是产品线定制。不同品类的 SKU 结构、图片规则、标题逻辑和规格字段不同，系统可以为手机壳、耳机配件、电脑包、服饰配件、家居百货等产品建立独立模板，把每个行业的上架经验沉淀成可复用模块。&lt;/p&gt;&lt;p&gt;第二是平台定制。除了 Shopee 和 TikTok Shop，后续可以继续接入 Lazada、Tokopedia、Shopify、WooCommerce、Amazon Seller、Temu 或本地化 ERP 后台。平台变化并不影响系统的核心价值：统一素材、统一配置、统一状态、统一历史，再用平台适配层处理差异。&lt;/p&gt;&lt;p&gt;第三是运营策略定制。未来可以继续加入标题 A/B 策略、价格与库存联动、自动翻译、多语言标题、违禁词检测、图片合规检查、店铺维度权限、任务队列调度、失败重试策略和团队协作审计。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;历史记录与复盘&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/8.y3j6AD8y_1MV3BP.webp&quot; srcset=&quot;/_astro/8.y3j6AD8y_Z4CTA0.webp 640w, /_astro/8.y3j6AD8y_Z1yPiPh.webp 750w, /_astro/8.y3j6AD8y_1J3B1q.webp 828w, /_astro/8.y3j6AD8y_2h2mzb.webp 1080w, /_astro/8.y3j6AD8y_1t7Hfl.webp 1280w, /_astro/8.y3j6AD8y_Z18XL8N.webp 1668w, /_astro/8.y3j6AD8y_1MV3BP.webp 1920w&quot; /&gt;&lt;figcaption&gt;历史记录与复盘&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;产品愿景&lt;a href=&quot;#产品愿景&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;自动上架系统最终要打造的，不只是一个能替人点击页面的工具，而是一个可以交付给高强度跨境团队使用的专业产品：稳定、清晰、可扩展、可复盘，并且能跟着业务一起长大。&lt;/p&gt;&lt;p&gt;当平台变多、产品变多、店铺变多、素材变多，真正稀缺的不是单次操作速度，而是流程标准化能力。这个系统的方向，就是把复杂运营流程装进一个可靠的中枢里，让团队把精力放回选品、素材、定价和增长，而不是反复消耗在机械上架上。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;系统总览&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;911&quot; src=&quot;/_astro/9.DJ8vicPa_ZFYumO.webp&quot; srcset=&quot;/_astro/9.DJ8vicPa_ZNyqkM.webp 640w, /_astro/9.DJ8vicPa_Z2iKOA4.webp 750w, /_astro/9.DJ8vicPa_1085gD.webp 828w, /_astro/9.DJ8vicPa_ZcSbpt.webp 1080w, /_astro/9.DJ8vicPa_Z10MPJj.webp 1280w, /_astro/9.DJ8vicPa_1rhNFt.webp 1668w, /_astro/9.DJ8vicPa_ZFYumO.webp 1920w&quot; /&gt;&lt;figcaption&gt;系统总览&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从母系社会到父权制度：亲缘、私有制与现代性别关系的演变(浅谈男女对立)</title><link>http://rainzt.cn/posts/from-matriarchal-society-to-patriarchy/</link><guid isPermaLink="true">http://rainzt.cn/posts/from-matriarchal-society-to-patriarchy/</guid><description>从亲缘确认、私有制和生产方式出发，浅谈母系亲缘结构、父权制度与现代男女对立背后的历史逻辑。</description><pubDate>Thu, 20 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;最近网络上的声音很大，男女对立也很严重，本期我们不站队任何一方，只从历史的角度去看待客观事实。
再次叠甲，“男女对立是阶级矛盾转移”的观点认为，社会中的一些性别冲突，表面上表现为男性与女性之间的利益冲突，但深层可能与经济结构、资源分配和阶层差异有关。例如，住房、就业、生育、教育等压力，本质上可能来自社会资源分配问题，却容易被转化为男女之间的相互指责。上层或既有利益结构在这种理论中被认为可能通过强化身份矛盾，让普通群体内部产生分裂，从而弱化对更深层经济问题的关注。不过，这并不意味着所有性别问题都是阶级矛盾造成的，现实中的性别不平等也有其独立存在的原因，二者往往是相互交织的。
再次声明，本站文章仅代表本站观点，若你不认可我的观点尽可离去并举报，如果你的观点能打动我，我会与你辩论，如果没有我会觉得你很无知并永久拉黑。&lt;/p&gt;&lt;/blockquote&gt;
&lt;section&gt;&lt;h2&gt;引言：社会制度并非天然存在&lt;a href=&quot;#引言社会制度并非天然存在&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;很多人会思考一个问题：&lt;/p&gt;&lt;p&gt;为什么现代社会大多数文明都形成了父姓制度？&lt;/p&gt;&lt;p&gt;为什么子女通常随父姓，而不是随母姓？&lt;/p&gt;&lt;p&gt;为什么人类历史上出现了从母系亲属结构向父系社会结构的转变？&lt;/p&gt;&lt;p&gt;进一步来说，为什么今天女性开始获得越来越多的社会权利，而与此同时，许多普通男性也开始感受到来自传统性别结构的压力？&lt;/p&gt;&lt;p&gt;这些问题表面上讨论的是男女关系，实际上背后涉及的是更深层的社会逻辑：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;亲缘确认、生产方式、财产继承以及社会结构的变化。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;很多制度并不是某一个群体单纯设计出来的，而是在特定生产条件下，为了解决现实问题而逐渐形成的。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;一、为什么父姓制度会成为许多文明的选择？&lt;a href=&quot;#一为什么父姓制度会成为许多文明的选择&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;section&gt;&lt;h2&gt;母亲拥有天然确定的血缘关系&lt;a href=&quot;#母亲拥有天然确定的血缘关系&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;从生理意义上来说，母亲对于孩子的血缘关系具有天然确定性。&lt;/p&gt;&lt;p&gt;女性经历：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;怀孕；&lt;/li&gt;
&lt;li&gt;分娩；&lt;/li&gt;
&lt;li&gt;哺育。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;因此，一个母亲可以百分百确认：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;这个孩子属于自己的血脉。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;这种确定性，使母系亲缘关系天然具有稳定基础。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;父系血缘为什么需要社会制度确认？&lt;a href=&quot;#父系血缘为什么需要社会制度确认&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;相比之下，父亲的血缘关系在生理层面并不存在同等程度的确定性。&lt;/p&gt;&lt;p&gt;男性无法通过自身身体直接确认：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;这个孩子是否一定拥有自己的血缘。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;这也是为什么在人类社会发展过程中，围绕父系血缘逐渐产生了一系列社会制度：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;从父姓；&lt;/li&gt;
&lt;li&gt;婚姻制度；&lt;/li&gt;
&lt;li&gt;宗族体系；&lt;/li&gt;
&lt;li&gt;财产继承规则。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这些制度的核心作用之一，就是加强父系血缘的社会确认。&lt;/p&gt;&lt;p&gt;换句话说：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;生理上无法完全确认的父系关系，需要通过社会制度进行确认。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;二、姓氏制度不仅是身份问题，也是亲缘识别系统&lt;a href=&quot;#二姓氏制度不仅是身份问题也是亲缘识别系统&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;很多人认为姓氏只是一个称呼。&lt;/p&gt;&lt;p&gt;但在传统社会中，姓氏承担着更加重要的功能：&lt;/p&gt;&lt;section&gt;&lt;h2&gt;第一，确认家族归属&lt;a href=&quot;#第一确认家族归属&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;同一个姓氏意味着：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;共同祖先；&lt;/li&gt;
&lt;li&gt;共同宗族；&lt;/li&gt;
&lt;li&gt;共同继承关系。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;在传统农业社会中，这对于土地、财富和权力传递非常重要。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;第二，帮助社会识别婚姻关系&lt;a href=&quot;#第二帮助社会识别婚姻关系&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果所有亲属都按照母系姓氏传承，那么一些亲缘关系可能隐藏在同姓体系中。&lt;/p&gt;&lt;p&gt;而父姓制度，使父系宗族更加清晰。&lt;/p&gt;&lt;p&gt;传统宗族中的祠堂、族谱，并不仅仅是财富继承工具，它实际上也是一种社会组织系统。&lt;/p&gt;&lt;p&gt;它解决的问题包括：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;谁属于这个家族；&lt;/li&gt;
&lt;li&gt;谁拥有继承资格；&lt;/li&gt;
&lt;li&gt;哪些人之间不能通婚。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;三、为什么许多母系社会中出现较弱的婚姻绑定？&lt;a href=&quot;#三为什么许多母系社会中出现较弱的婚姻绑定&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;这里需要明确：&lt;/p&gt;&lt;p&gt;母系社会并不等于女性统治社会，也不意味着所有母系社会都采用同一种婚姻模式。&lt;/p&gt;&lt;p&gt;但在人类学研究中，可以发现，一些母系亲属社会中存在较弱化的婚姻绑定结构。&lt;/p&gt;&lt;p&gt;原因并非简单来自道德观念，而是来自亲缘确认逻辑。&lt;/p&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;当父亲身份无法被确认时，男性投入模式会发生变化&lt;a href=&quot;#当父亲身份无法被确认时男性投入模式会发生变化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在一些母系社会中，与孩子长期共同生活并承担抚育责任的男性，并不一定是生父，而可能是母亲一方的兄弟，也就是舅舅。&lt;/p&gt;&lt;p&gt;原因在于：&lt;/p&gt;&lt;p&gt;舅舅可以确认：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;这个孩子一定属于自己母系家族的血缘。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;而生父无法获得同等程度的确定。&lt;/p&gt;&lt;p&gt;因此，在这样的社会结构中，母系亲缘关系更加稳定。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;父亲角色的社会意义与制度有关&lt;a href=&quot;#父亲角色的社会意义与制度有关&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;当社会无法建立稳定的父系确认机制时，对于父亲身份的制度需求自然较低。&lt;/p&gt;&lt;p&gt;而当社会进入私有财产积累阶段后，情况开始改变。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;四、农业革命如何推动父系制度形成？&lt;a href=&quot;#四农业革命如何推动父系制度形成&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;人类早期社会主要依靠：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;采集；&lt;/li&gt;
&lt;li&gt;狩猎；&lt;/li&gt;
&lt;li&gt;群体共享资源。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这一时期，私人财富积累有限。&lt;/p&gt;&lt;p&gt;但农业时代出现后，人类社会发生重大变化。&lt;/p&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;土地和财富改变了社会逻辑&lt;a href=&quot;#土地和财富改变了社会逻辑&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;农业生产带来了：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;土地；&lt;/li&gt;
&lt;li&gt;牲畜；&lt;/li&gt;
&lt;li&gt;粮食储备；&lt;/li&gt;
&lt;li&gt;私人财产。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;当财富开始积累，一个新的问题出现：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;财产应该传给谁？&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;母亲当然可以确认自己的孩子。&lt;/p&gt;&lt;p&gt;但是男性如果希望自己的财富传递给自己的直系后代，就需要解决父系血缘确认问题。&lt;/p&gt;&lt;p&gt;于是：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;私有财产出现&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;需要确定继承人&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;加强父系血缘确认&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;发展姓氏、婚姻、宗族制度&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;五、父权制度并非单纯来自男性意志，而是生产结构的结果&lt;a href=&quot;#五父权制度并非单纯来自男性意志而是生产结构的结果&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;很多社会制度不能简单理解为：&lt;/p&gt;&lt;p&gt;“某个群体压迫另一个群体”。&lt;/p&gt;&lt;p&gt;更准确地说：&lt;/p&gt;&lt;p&gt;制度往往是生产方式和现实需求共同塑造的结果。&lt;/p&gt;&lt;p&gt;农业文明时期：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;体力是重要生产力；&lt;/li&gt;
&lt;li&gt;战争依赖身体力量；&lt;/li&gt;
&lt;li&gt;土地开发依赖大量劳动力。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;由于生育和哺育成本，女性平均承担更多家庭照护限制。&lt;/p&gt;&lt;p&gt;而男性平均具有更强的体力优势。&lt;/p&gt;&lt;p&gt;于是：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;农业生产&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;体力竞争&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;私有财产继承&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;父系制度强化&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;六、为什么现代社会正在改变传统性别结构？&lt;a href=&quot;#六为什么现代社会正在改变传统性别结构&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;工业革命以后，人类社会进入新的阶段。&lt;/p&gt;&lt;p&gt;现代社会越来越依赖：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;知识；&lt;/li&gt;
&lt;li&gt;技术；&lt;/li&gt;
&lt;li&gt;教育；&lt;/li&gt;
&lt;li&gt;管理；&lt;/li&gt;
&lt;li&gt;沟通能力。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;生产力的核心逐渐从体力转向脑力。&lt;/p&gt;&lt;p&gt;这意味着：&lt;/p&gt;&lt;p&gt;过去男性占优势的部分，正在减少。&lt;/p&gt;&lt;p&gt;女性在：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;教育；&lt;/li&gt;
&lt;li&gt;商业；&lt;/li&gt;
&lt;li&gt;科技；&lt;/li&gt;
&lt;li&gt;医疗；&lt;/li&gt;
&lt;li&gt;管理；&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;等领域拥有越来越多的发展空间。&lt;/p&gt;&lt;p&gt;这并不是简单来源于思想变化，而是生产方式变化带来的结果。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;七、女性解放与生产力变化&lt;a href=&quot;#七女性解放与生产力变化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;恩格斯曾提出：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;女性解放与社会生产方式变化密切相关。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;这一观点的核心逻辑是：&lt;/p&gt;&lt;p&gt;旧社会中的性别关系，与私有制、家庭结构和社会等级体系存在联系。&lt;/p&gt;&lt;p&gt;当生产方式变化，旧有结构自然会受到挑战。&lt;/p&gt;&lt;p&gt;过去：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;体力生产&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;男性优势&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;传统父权结构&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;现代：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;知识生产&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;性别差异降低&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;新的社会关系形成&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;八、父权制度不仅影响女性，也影响普通男性&lt;a href=&quot;#八父权制度不仅影响女性也影响普通男性&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;讨论父权制度时，很多人首先想到女性受到限制。&lt;/p&gt;&lt;p&gt;但另一方面，传统父权结构也给男性塑造了大量责任要求。&lt;/p&gt;&lt;p&gt;尤其对于普通男性而言，父权社会给予的更多是一种责任期待，而不一定是真正的权力。&lt;/p&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;第一层压力：必须成功&lt;a href=&quot;#第一层压力必须成功&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;传统观念告诉男性：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;要成家；&lt;/li&gt;
&lt;li&gt;要立业；&lt;/li&gt;
&lt;li&gt;要承担家庭责任。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;成功似乎成为男性价值的重要证明。&lt;/p&gt;&lt;p&gt;但现实中，并不是所有男性都能获得与责任匹配的资源。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;第二层压力：婚姻成为社会评价体系&lt;a href=&quot;#第二层压力婚姻成为社会评价体系&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在传统观念中：&lt;/p&gt;&lt;p&gt;结婚不仅是一种情感选择，也被赋予了成功象征。&lt;/p&gt;&lt;p&gt;男性往往被期待：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;买房；&lt;/li&gt;
&lt;li&gt;提供稳定收入；&lt;/li&gt;
&lt;li&gt;承担家庭经济责任。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;婚姻因此不仅成为情感关系，也成为一种社会压力。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;第三层压力：责任感被无限扩大&lt;a href=&quot;#第三层压力责任感被无限扩大&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;责任本身并没有问题。&lt;/p&gt;&lt;p&gt;问题在于：&lt;/p&gt;&lt;p&gt;如果一个人需要承担所有责任，却缺少对应的支持和选择空间，那么责任就可能变成压力。&lt;/p&gt;&lt;p&gt;对于普通男性而言，传统父权结构有时意味着：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;需要承担“家庭支柱”的角色，却未必拥有真正的社会资源和权力。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;九、真正的问题不是男女，而是旧制度留下的压力&lt;a href=&quot;#九真正的问题不是男女而是旧制度留下的压力&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;父权制度并不是简单的：&lt;/p&gt;&lt;p&gt;“男性获益，女性受害”。&lt;/p&gt;&lt;p&gt;它更像是一套等级结构：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;女性可能受到角色限制；&lt;/li&gt;
&lt;li&gt;普通男性可能承担过度责任；&lt;/li&gt;
&lt;li&gt;少数掌握资源的人获得更多优势。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;因此，真正需要被反思的，并不是某一个性别，而是：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;一个社会是否能够让每个人拥有更自由、更平等的发展空间。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;十、从历史角度看，人类正在进入新的阶段&lt;a href=&quot;#十从历史角度看人类正在进入新的阶段&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;回顾整个人类社会：&lt;/p&gt;&lt;section&gt;&lt;h2&gt;原始社会阶段&lt;a href=&quot;#原始社会阶段&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;生产力较低：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;财富有限；&lt;/li&gt;
&lt;li&gt;血缘以母系确认更加稳定。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;农业社会阶段&lt;a href=&quot;#农业社会阶段&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;私有财产出现：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;继承需求增强；&lt;/li&gt;
&lt;li&gt;父系制度发展；&lt;/li&gt;
&lt;li&gt;宗族结构强化。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;工业与信息社会阶段&lt;a href=&quot;#工业与信息社会阶段&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;生产方式变化：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;体力不再决定全部价值；&lt;/li&gt;
&lt;li&gt;男女能力差异进一步缩小；&lt;/li&gt;
&lt;li&gt;传统性别结构开始调整。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;结语：制度会随着生产力变化而变化&lt;a href=&quot;#结语制度会随着生产力变化而变化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;人类历史并不是简单的男女对立史。&lt;/p&gt;&lt;p&gt;更多时候，它是一部：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;人类不断调整社会制度，以适应新的生产方式和生活需求的历史。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;从母系亲缘结构，到父系宗族体系，再到现代社会对性别关系的重新探索。&lt;/p&gt;&lt;p&gt;其中变化的并不仅仅是男女地位，而是整个社会组织方式。&lt;/p&gt;&lt;p&gt;未来社会会走向哪里，没有人可以完全预测。&lt;/p&gt;&lt;p&gt;但可以确定的是：&lt;/p&gt;&lt;p&gt;当生产力、经济结构和人的认知不断变化时，旧有制度也会不断被重新审视。&lt;/p&gt;&lt;p&gt;历史不会停留在过去。&lt;/p&gt;&lt;p&gt;它会继续寻找新的平衡。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>我回望青春时，我的青春是最昂贵的；
我正值青春时，我的青春是最廉价的。</title><link>http://rainzt.cn/posts/others/youth-most-expensive/youth-most-expensive/</link><guid isPermaLink="true">http://rainzt.cn/posts/others/youth-most-expensive/youth-most-expensive/</guid><description>一篇关于校园、游戏、喜欢与告别的青春回望。</description><pubDate>Mon, 17 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;后来才知道，真正舍不得的是日子&lt;a href=&quot;#后来才知道真正舍不得的是日子&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;有些年份，人在其中时并不觉得有什么特别。&lt;/p&gt;&lt;p&gt;天照常亮，铃照常响，黑板上的粉笔字一层叠着一层。夏天从窗外吹进来的风是热的，冬天走廊尽头总灌着冷气。我们每天抱着书，从一间教室走到另一间教室，嫌课间太短，嫌晚自习太长，嫌考试太多，也嫌日子过得太慢。&lt;/p&gt;&lt;p&gt;那时候谁也不会想到，许多年以后，真正舍不得的，偏偏就是这些曾经觉得漫长的日子。&lt;/p&gt;&lt;p&gt;我也是后来才知道——&lt;/p&gt;&lt;p&gt;原来青春在拥有的时候，几乎一文不值。&lt;/p&gt;&lt;p&gt;而一旦失去，便再也无从估价。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2020 年的秋天&lt;a href=&quot;#2020-年的秋天&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我记得2020年的秋天。&lt;/p&gt;&lt;p&gt;其实已经记不清那年秋天究竟冷得早不早，也记不得开学那天穿的是什么衣服，只记得教学楼的走廊很长，课间总有人聚在那里。&lt;/p&gt;&lt;p&gt;后来不知道是谁带来了毽子。&lt;/p&gt;&lt;p&gt;一群人便围成一个并不好看的圈。&lt;/p&gt;&lt;p&gt;毽子从这个人的脚尖飞到另一个人的肩头，踢歪了便笑，掉在地上就弯腰去捡。上课铃一响，众人作鸟兽散，踩着铃声往教室里跑。&lt;/p&gt;&lt;p&gt;现在想来，那实在算不得什么值得记住的事情。&lt;/p&gt;&lt;p&gt;可偏偏很多年以后，大考的题目忘了，名次忘了，老师说过的话也忘得七七八八，我却还能想起那只毽子在半空里打着旋。&lt;/p&gt;&lt;p&gt;大概记忆本来就不讲道理。&lt;/p&gt;&lt;p&gt;重要的事未必留下，真正留下来的，反倒总是一些无用的东西。&lt;/p&gt;&lt;p&gt;比如一阵风。&lt;/p&gt;&lt;p&gt;一句玩笑。&lt;/p&gt;&lt;p&gt;一个人在走廊上回过头来的样子。&lt;/p&gt;&lt;p&gt;又或者，是放学时天边那一点将暗未暗的光。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;十六七岁的时间太多了&lt;a href=&quot;#十六七岁的时间太多了&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;刚上高中的时候，我似乎还算一个意气很盛的人。&lt;/p&gt;&lt;p&gt;做班长，第一次月考数学考了141，年级第一。&lt;/p&gt;&lt;p&gt;少年人大抵都这样。&lt;/p&gt;&lt;p&gt;只凭一次考得不错，便觉得前路也不过如此。&lt;/p&gt;&lt;p&gt;那时的我从不真正害怕时间。&lt;/p&gt;&lt;p&gt;一道题今天不会，还有明天；一次考试失手，还有下一次；一句话今天没说出口，也总觉得往后还有机会。&lt;/p&gt;&lt;p&gt;我们对“以后”有一种近乎盲目的信任。&lt;/p&gt;&lt;p&gt;仿佛它是一条很长很长的路，无论今天停在哪里，明天起来，总还能接着走。&lt;/p&gt;&lt;p&gt;所以时间在那时候很便宜。&lt;/p&gt;&lt;p&gt;一个课间可以拿来踢毽子。&lt;/p&gt;&lt;p&gt;一个晚自习可以偷偷玩五子棋。&lt;/p&gt;&lt;p&gt;一个周末，可以几个人抱着手机，一局接一局地打游戏。&lt;/p&gt;&lt;p&gt;输了便再开。&lt;/p&gt;&lt;p&gt;赢了便笑。&lt;/p&gt;&lt;p&gt;谁也不会觉得几个小时过去了有什么可惜。&lt;/p&gt;&lt;p&gt;因为十六七岁的时间太多了。&lt;/p&gt;&lt;p&gt;多得让人误以为，它永远不会见底。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;屏幕背后的那些晚上&lt;a href=&quot;#屏幕背后的那些晚上&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;后来有一段时间，我很喜欢露娜。&lt;/p&gt;&lt;p&gt;月下不停地穿来穿去，一次断大便足够让人懊恼半天。&lt;/p&gt;&lt;p&gt;现在偶尔翻到那些旧截图，已经很难再理解当时为什么会为了一个MVP高兴那么久。&lt;/p&gt;&lt;p&gt;但我仍然舍不得删。&lt;/p&gt;&lt;p&gt;倒不是怀念游戏。&lt;/p&gt;&lt;p&gt;我只是舍不得屏幕背后的那些晚上。&lt;/p&gt;&lt;p&gt;那时候有人喊一句“上号”，几个人就真的都在。&lt;/p&gt;&lt;p&gt;没有人要提前约时间，也没有人会在打到一半时说自己明天还有会议。&lt;/p&gt;&lt;p&gt;那个年纪，人的世界很小。&lt;/p&gt;&lt;p&gt;一间教室。&lt;/p&gt;&lt;p&gt;一个操场。&lt;/p&gt;&lt;p&gt;几个人。&lt;/p&gt;&lt;p&gt;一部手机。&lt;/p&gt;&lt;p&gt;就足够盛下整个夜晚。&lt;/p&gt;&lt;div&gt;
  &lt;div&gt;
    
  &lt;/div&gt;
  &lt;p&gt;一段王者荣耀的画面，放在这里，作为那些一起开黑、一起把夜晚打发掉的回声。&lt;/p&gt;
  &lt;a href=&quot;https://www.bilibili.com/video/BV1f6b86tEBz/&quot; target=&quot;_blank&quot;&gt;在哔哩哔哩打开这段视频 ↗&lt;/a&gt;
&lt;/div&gt;&lt;p&gt;后来世界变大了，认识的人多了，能去的地方也多了。&lt;/p&gt;&lt;p&gt;反倒越来越难凑齐当年的那几个人。&lt;/p&gt;&lt;p&gt;于是才明白，人这一生最奇怪的地方，大概就是：&lt;/p&gt;&lt;p&gt;总在拥有很少的时候，觉得自己什么也没有；&lt;/p&gt;&lt;p&gt;等后来真的拥有了许多，才开始怀念曾经那一点点东西。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;那时的我站在操场&lt;a href=&quot;#那时的我站在操场&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;再后来，我去了操场。&lt;/p&gt;&lt;p&gt;成了体育生。&lt;/p&gt;&lt;p&gt;黄昏从此有了另一种样子。&lt;/p&gt;&lt;p&gt;放学铃响以后，别人往教室走，我往跑道走。&lt;/p&gt;&lt;p&gt;夏天的塑胶跑道晒过一天，热气还伏在上面。跑起来的时候风从耳边过去，汗一点点把衣服浸透。冬天则冷，呼出来的气在眼前散掉，手指僵着，连热身都觉得漫长。&lt;/p&gt;&lt;p&gt;那时候我并不喜欢这些。&lt;/p&gt;&lt;p&gt;累就是累。&lt;/p&gt;&lt;p&gt;疼就是疼。&lt;/p&gt;&lt;p&gt;训练结束时，满脑子只剩下吃饭、洗澡、回教室。&lt;/p&gt;&lt;p&gt;可如今再想起来，那些傍晚竟比很多后来精心安排过的旅行还要清楚。&lt;/p&gt;&lt;p&gt;我记得训练结束以后，天往往还剩一点光。&lt;/p&gt;&lt;p&gt;操场上的人没有完全散。&lt;/p&gt;&lt;p&gt;远处教学楼已经亮起了灯。&lt;/p&gt;&lt;p&gt;有人从食堂方向往回走。&lt;/p&gt;&lt;p&gt;风把树吹得轻轻响。&lt;/p&gt;&lt;p&gt;那时的我站在那里，不会觉得这一幕有什么值得留恋。&lt;/p&gt;&lt;p&gt;我只想着，晚自习快开始了。&lt;/p&gt;&lt;p&gt;可如今让我再回去一次，我大概会站得久一点。&lt;/p&gt;&lt;p&gt;不会急着走。&lt;/p&gt;&lt;p&gt;也不会急着把那天过完。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;喜欢一个人，其实是很轻的事情&lt;a href=&quot;#喜欢一个人其实是很轻的事情&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;青春里当然也有喜欢的人。&lt;/p&gt;&lt;p&gt;只是现在再回望，我已经很难把它单独从那些岁月里剥离出来。&lt;/p&gt;&lt;p&gt;喜欢一个人，其实是很轻的事情。&lt;/p&gt;&lt;p&gt;轻得像上课时回头看的一眼。&lt;/p&gt;&lt;p&gt;像故意调近一点的座位。&lt;/p&gt;&lt;p&gt;像晚自习前那十几分钟。&lt;/p&gt;&lt;p&gt;像训练结束以后，明明一身汗，还想先去见一面。&lt;/p&gt;&lt;p&gt;那时候说过那么多话。&lt;/p&gt;&lt;p&gt;如今却没有几句还能完整记得。&lt;/p&gt;&lt;p&gt;反而记得一些毫无意义的片段。&lt;/p&gt;&lt;p&gt;她坐在后面。&lt;/p&gt;&lt;p&gt;我知道她在那里。&lt;/p&gt;&lt;p&gt;有时回头。&lt;/p&gt;&lt;p&gt;有时不回头。&lt;/p&gt;&lt;p&gt;有时一句话也不说。&lt;/p&gt;&lt;p&gt;现在想来，少年人的喜欢之所以动人，或许不是因为它多么深刻。&lt;/p&gt;&lt;p&gt;而是因为那时候，我们还没有学会衡量。&lt;/p&gt;&lt;p&gt;没有值不值得。&lt;/p&gt;&lt;p&gt;没有以后怎么办。&lt;/p&gt;&lt;p&gt;没有这段关系最终会走到哪里。&lt;/p&gt;&lt;p&gt;甚至连所谓“永远”，也只是一个说出口时并不觉得沉重的词。&lt;/p&gt;&lt;p&gt;只是喜欢。&lt;/p&gt;&lt;p&gt;于是便靠近一些。&lt;/p&gt;&lt;p&gt;仅此而已。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;那些没有日期的日子&lt;a href=&quot;#那些没有日期的日子&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;2021年5月17日，我至今还记得。&lt;/p&gt;&lt;p&gt;年轻的时候，总喜欢记住故事开始的日期。&lt;/p&gt;&lt;p&gt;后来才知道，真正值得记住的，其实不是哪一天开始。&lt;/p&gt;&lt;p&gt;而是开始以后，那些没有日期的日子。&lt;/p&gt;&lt;p&gt;一起吃过多少顿饭。&lt;/p&gt;&lt;p&gt;绕过多少圈操场。&lt;/p&gt;&lt;p&gt;等过多少次下课。&lt;/p&gt;&lt;p&gt;说过多少句无关紧要的话。&lt;/p&gt;&lt;p&gt;没有人会把这些写进纪念册。&lt;/p&gt;&lt;p&gt;日历也不会特意替你保存。&lt;/p&gt;&lt;p&gt;它们只是一天一天地发生，然后一天一天地过去。&lt;/p&gt;&lt;p&gt;直到有一天，你忽然发现，那些日子竟然已经全部成为过去。&lt;/p&gt;&lt;p&gt;这时候再去寻找，却连一个准确的日期都没有。&lt;/p&gt;&lt;p&gt;只剩下一种模糊的感觉。&lt;/p&gt;&lt;p&gt;好像那几年总是有风。&lt;/p&gt;&lt;p&gt;总是有人在身边。&lt;/p&gt;&lt;p&gt;总觉得明天还很长。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;真正的告别很少有一场郑重的告别&lt;a href=&quot;#真正的告别很少有一场郑重的告别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;高三以后，很多事情开始不再像从前。&lt;/p&gt;&lt;p&gt;成绩掉下去。&lt;/p&gt;&lt;p&gt;心也乱起来。&lt;/p&gt;&lt;p&gt;曾经以为笔直向前的路，突然生出许多岔口。&lt;/p&gt;&lt;p&gt;那个考了141分以后觉得未来理所当然会很好的人，也渐渐沉默下来。&lt;/p&gt;&lt;p&gt;现在回头看，那大概是我第一次真正意识到：&lt;/p&gt;&lt;p&gt;人生并不会因为你年轻，就永远朝着自己想象的方向去。&lt;/p&gt;&lt;p&gt;有些人会离开。&lt;/p&gt;&lt;p&gt;有些事情会失控。&lt;/p&gt;&lt;p&gt;有些努力没有结果。&lt;/p&gt;&lt;p&gt;有些承诺说出口的时候是真的，后来失效的时候也是真的。&lt;/p&gt;&lt;p&gt;我们总是在很年轻的时候，以为遗憾来自“没有得到”。&lt;/p&gt;&lt;p&gt;后来才发现，遗憾更多时候来自——&lt;/p&gt;&lt;p&gt;曾经得到过。&lt;/p&gt;&lt;p&gt;只是那时不知道那就是最后一次。&lt;/p&gt;&lt;p&gt;最后一次并肩走过那条路。&lt;/p&gt;&lt;p&gt;最后一次坐在那个座位。&lt;/p&gt;&lt;p&gt;最后一次晚自习后一起离开。&lt;/p&gt;&lt;p&gt;最后一次很自然地说：&lt;/p&gt;&lt;p&gt;明天见。&lt;/p&gt;&lt;p&gt;所谓告别，很少真的有一场郑重的告别。&lt;/p&gt;&lt;p&gt;更多时候，它只是一个普通下午。&lt;/p&gt;&lt;p&gt;大家像往常一样分开。&lt;/p&gt;&lt;p&gt;谁也没有回头。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;离开那间教室以后&lt;a href=&quot;#离开那间教室以后&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;毕业以后，我很少再梦见考试。&lt;/p&gt;&lt;p&gt;倒是偶尔会梦见学校。&lt;/p&gt;&lt;p&gt;梦里的教学楼并没有什么变化。&lt;/p&gt;&lt;p&gt;走廊还是那样长。&lt;/p&gt;&lt;p&gt;教室里的灯还是白的。&lt;/p&gt;&lt;p&gt;有人趴在桌上睡觉，有人在后排讲话。&lt;/p&gt;&lt;p&gt;我走进去，也没有人觉得奇怪。&lt;/p&gt;&lt;p&gt;仿佛我只是出去了一会儿。&lt;/p&gt;&lt;p&gt;可醒来以后，要很久才能反应过来：&lt;/p&gt;&lt;p&gt;原来我已经离开那里很多年了。&lt;/p&gt;&lt;p&gt;有一次我忽然想到，人这一生会拥有很多房间。&lt;/p&gt;&lt;p&gt;后来住过的房子可能比那间教室宽敞得多，桌子也更好，灯也更亮。&lt;/p&gt;&lt;p&gt;可唯独那间教室，一旦走出去以后，就再也不能真正回去。&lt;/p&gt;&lt;p&gt;就算哪天重新推开门，也已经只是一个来访的人。&lt;/p&gt;&lt;p&gt;自己的座位坐着别人。&lt;/p&gt;&lt;p&gt;黑板上写着别人的课程。&lt;/p&gt;&lt;p&gt;窗外还是同一棵树。&lt;/p&gt;&lt;p&gt;可故事已经换了一批人。&lt;/p&gt;&lt;p&gt;那一刻大概才算真正明白了什么叫时间。&lt;/p&gt;&lt;p&gt;它不会毁掉什么。&lt;/p&gt;&lt;p&gt;它甚至让很多东西原封不动地留在那里。&lt;/p&gt;&lt;p&gt;只是把你从里面拿走。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;时间开始有了价格&lt;a href=&quot;#时间开始有了价格&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;后来，我也渐渐成了一个会计算的人。&lt;/p&gt;&lt;p&gt;会算得失，会想结果，会知道一件事情该不该做。&lt;/p&gt;&lt;p&gt;许多东西开始有了价格。&lt;/p&gt;&lt;p&gt;一次选择有代价。&lt;/p&gt;&lt;p&gt;一天时间有成本。&lt;/p&gt;&lt;p&gt;一件商品值多少钱，一项工作该投入多少精力，很多事情都可以慢慢算清楚。&lt;/p&gt;&lt;p&gt;可越是这样，我越想起十六七岁的自己。&lt;/p&gt;&lt;p&gt;那个时候的我，恰恰什么都不算。&lt;/p&gt;&lt;p&gt;会把一个下午完整地交给一场游戏。&lt;/p&gt;&lt;p&gt;会为了一个人多绕半圈操场。&lt;/p&gt;&lt;p&gt;会在晚自习前浪费十分钟，只为了说几句如今已经记不得的话。&lt;/p&gt;&lt;p&gt;会在走廊里踢一只毫无用处的毽子。&lt;/p&gt;&lt;p&gt;会望着窗外发呆。&lt;/p&gt;&lt;p&gt;会觉得一天太长。&lt;/p&gt;&lt;p&gt;会盼下课。&lt;/p&gt;&lt;p&gt;会盼周末。&lt;/p&gt;&lt;p&gt;会盼长大。&lt;/p&gt;&lt;p&gt;我们用最昂贵的东西，做着最没有意义的事。&lt;/p&gt;&lt;p&gt;而这大概就是青春。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;如果现在回到从前&lt;a href=&quot;#如果现在回到从前&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我有时会想，如果现在真的给我一次机会，让我回到从前，我会不会改变些什么。&lt;/p&gt;&lt;p&gt;也许不会。&lt;/p&gt;&lt;p&gt;我甚至不想告诉那个少年未来会发生什么。&lt;/p&gt;&lt;p&gt;不告诉他哪次考试会失败。&lt;/p&gt;&lt;p&gt;不告诉他谁最后会离开。&lt;/p&gt;&lt;p&gt;不告诉他应该珍惜谁。&lt;/p&gt;&lt;p&gt;也不告诉他哪些选择以后会后悔。&lt;/p&gt;&lt;p&gt;因为一旦什么都提前知道，那就不再是青春了。&lt;/p&gt;&lt;p&gt;我只想在某个傍晚远远地看他一眼。&lt;/p&gt;&lt;p&gt;看他训练结束，站在操场边喘气。&lt;/p&gt;&lt;p&gt;看他低头看看时间，觉得晚自习快迟到了。&lt;/p&gt;&lt;p&gt;看他匆匆往教学楼走。&lt;/p&gt;&lt;p&gt;晚霞落在背后。&lt;/p&gt;&lt;p&gt;校服被风吹起来一点。&lt;/p&gt;&lt;p&gt;他不知道很多年以后，会有人如此怀念这一刻。&lt;/p&gt;&lt;p&gt;他只是往前跑。&lt;/p&gt;&lt;p&gt;甚至嫌天黑得太慢。&lt;/p&gt;&lt;p&gt;我不会叫住他。&lt;/p&gt;&lt;p&gt;只是想替很多年后的自己，多看一会儿。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;青春昂贵在哪里&lt;a href=&quot;#青春昂贵在哪里&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;现在我终于知道，青春昂贵在哪里。&lt;/p&gt;&lt;p&gt;不是因为年轻。&lt;/p&gt;&lt;p&gt;不是因为爱情。&lt;/p&gt;&lt;p&gt;也不是因为那几年发生过多少值得讲述的故事。&lt;/p&gt;&lt;p&gt;它昂贵，只因为一切都不能重来。&lt;/p&gt;&lt;p&gt;2020年的秋天不能。&lt;/p&gt;&lt;p&gt;第一次月考的141不能。&lt;/p&gt;&lt;p&gt;走廊里飞起来的毽子不能。&lt;/p&gt;&lt;p&gt;操场黄昏的风不能。&lt;/p&gt;&lt;p&gt;晚自习前的十几分钟不能。&lt;/p&gt;&lt;p&gt;那个曾经相信“以后还有很多”的自己，也不能。&lt;/p&gt;&lt;p&gt;而最让人无可奈何的是——&lt;/p&gt;&lt;p&gt;这些东西真正属于我的时候，我从没觉得它们稀有。&lt;/p&gt;&lt;p&gt;我嫌时间走得慢。&lt;/p&gt;&lt;p&gt;嫌晚自习太长。&lt;/p&gt;&lt;p&gt;嫌高三难熬。&lt;/p&gt;&lt;p&gt;一心想往前走。&lt;/p&gt;&lt;p&gt;后来真的走了很远，才忍不住频频回头。&lt;/p&gt;&lt;p&gt;原来人总是在青春里盼望远方，&lt;/p&gt;&lt;p&gt;又在远方里，想念青春。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;在远方里想念青春&lt;a href=&quot;#在远方里想念青春&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如今再经过下雨的夜晚，我有时还是会停下来听一会儿。&lt;/p&gt;&lt;p&gt;雨落在窗沿，落在屋檐，也落进一些已经很远的年份里。&lt;/p&gt;&lt;p&gt;我不知道从前那个少年是否也曾在某个晚自习里听过同一场雨。&lt;/p&gt;&lt;p&gt;也许有。&lt;/p&gt;&lt;p&gt;也许没有。&lt;/p&gt;&lt;p&gt;其实都不重要了。&lt;/p&gt;&lt;p&gt;重要的是，他曾经在那里。&lt;/p&gt;&lt;p&gt;我也曾经在那里。&lt;/p&gt;&lt;p&gt;那几年并不完美。&lt;/p&gt;&lt;p&gt;有欢喜，也有狼狈；有得到，也有失去。&lt;/p&gt;&lt;p&gt;可它们终于还是一点一点，把今天的我送到了这里。&lt;/p&gt;&lt;p&gt;于是有一天，当我回过头去，我才发现——&lt;/p&gt;&lt;p&gt;原来我这一生最奢侈的时候，并不是后来拥有了什么。&lt;/p&gt;&lt;p&gt;而是十七岁那年，&lt;/p&gt;&lt;p&gt;我有整整一个下午可以挥霍，&lt;/p&gt;&lt;p&gt;有一个明天可以相信，&lt;/p&gt;&lt;p&gt;有一些人以为永远不会走散，&lt;/p&gt;&lt;p&gt;还有一个尚且不知道岁月为何物的自己。&lt;/p&gt;&lt;p&gt;那时候，我什么都没有。&lt;/p&gt;&lt;p&gt;却好像什么都有。&lt;/p&gt;&lt;p&gt;而如今，我终于知道它们有多贵。&lt;/p&gt;&lt;p&gt;只是再也买不回来了。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>朝朝听雨，岁岁有风</title><link>http://rainzt.cn/posts/chao-chao-listening-rain-years-wind/</link><guid isPermaLink="true">http://rainzt.cn/posts/chao-chao-listening-rain-years-wind/</guid><description>今天不说遗憾，只讲讲我和她的故事。写给那段被照亮过的高中岁月，也写给如今终于学会从容回望的自己。</description><pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;写在故事之前&lt;a href=&quot;#写在故事之前&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;今天是 2026 年 8 月 14 日。距离我的上一段感情，已经过去一年半了。&lt;/p&gt;&lt;p&gt;翻相册的时候，我看到了最近打出的高光操作——金标镜，多少有点想显摆。也就是这一点小小的得意，把我拽回了当年的那些事情。&lt;/p&gt;&lt;p&gt;我的博客叫“朝朝听雨”，ZZT 是我本名的缩写，Y 是她名字的最后一字，拼在一起便是“听雨”，对应着 Rain。这几年下来，不管是网名，还是做过的项目、写过的作品，落款里总藏着这几个字母。&lt;/p&gt;&lt;p&gt;说起来也算我痴情。倒不是刻意放不下，只是习惯了，也舍不得抹掉那些年月的痕迹。也是想提醒自己：物物而不物于物，念念而不念于念。&lt;/p&gt;&lt;p&gt;“朝朝听雨”现在的寓意，已经不再是我和她之间的专属暗码，而是独属于我个人的精神角落。它装得下年少的青春旧事，也盛得下如今的成熟从容；记得住被照亮的恩情，也将这份光继续延续下去。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;今天不说遗憾，只讲讲我和她的故事。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;故事从高一开始&lt;a href=&quot;#故事从高一开始&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;她是个很特别的姑娘，憨憨的，带着点没被磨平的傻气，和身边很多人都不一样。我总爱说她憨批，看着她的时候，总像看见初中时的自己——干净、透亮，对世界还抱着毫无保留的热忱。&lt;/p&gt;&lt;p&gt;故事要从 2020 年的秋天说起，那年我刚上高一。&lt;/p&gt;&lt;p&gt;九月入学。初三我当过半年班长，偏巧我爸认识广州大同中学的老师，而我们班主任恰好是从那所学校调过来的。我本来打定主意高中不再管事，还是被班主任直接任命成了班长。现在回头看，倒也算因祸得福。&lt;/p&gt;&lt;p&gt;高中的日子总过得缓慢又无聊。下课闲得发慌时，我就和几个相熟的男女同学凑在走廊踢毽子。起初只有零星几个人，后来队伍越拉越长，她也混在了里面。&lt;/p&gt;&lt;p&gt;你或许以为我们的故事是从毽子开始的。&lt;/p&gt;&lt;p&gt;其实不是。那时候认识归认识，我依旧我行我素。晚自习总爱坐到讲台上，和前排同学天南海北地聊，要么就拉着人玩飞行棋、真心话大冒险、五子棋。第一次月考我数学考了 141 分，拿了年级第一，便更没把心思放在学习上。&lt;/p&gt;&lt;p&gt;真正的转机是我舍友阿 B。阿 B 和他朋友小黑，加上她和她闺蜜，几个人总凑在一起开黑，连跪到小黑都嫌他们菜，嚷嚷着要找个大神带。于是他们找上了我。&lt;/p&gt;&lt;p&gt;就这么着，我们才算真正熟络起来。她玩法师，走中路。&lt;/p&gt;&lt;p&gt;从蝉鸣聒噪的盛夏，到呵气成雾的深冬。白天我们在走廊踢毽子、传纸条，课上偷偷讲小话，晚自习凑在一起写作业、玩桌游、扯些没营养的闲话；周末就抱着手机组队打王者，日子热热闹闹地往前跑。原本对她没什么特别感觉的我，不知不觉间，就动了心。&lt;/p&gt;&lt;p&gt;借着班长的便利，我把自己的座位调到了第一列第一个，她就坐在我身后。抬头低头总能撞见，聊天的时间越来越长，距离也越靠越近。&lt;/p&gt;&lt;p&gt;2021 年 5 月 17 日，我和她告白了。&lt;/p&gt;&lt;p&gt;我赌对了，她也喜欢我。&lt;/p&gt;&lt;p&gt;我们在一起了。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;我喜欢她&lt;a href=&quot;#我喜欢她&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;喜欢她干干净净的单纯。连故意逗她、看她气鼓鼓又拿我没办法的样子，心里都涨着满满的欢喜；喜欢她身上淡淡的、很好闻的香味，每次凑近都觉得莫名安心；喜欢她藏不住的可爱，一颦一笑都带着少年人独有的鲜活；更喜欢她眼睛里的光，亮闪闪的，像盛着整片星空。&lt;/p&gt;&lt;p&gt;我也曾有过意气风发的时候，有过属于自己的耀眼时刻。可那段日子里，我整个人都颓丧得厉害，世界灰蒙蒙的，连自己的光都快熄灭了。是她一步步走过来，把光重新递到了我手里，把我的日子照得透亮。&lt;/p&gt;&lt;p&gt;在一起之后的日子，像裹了层橘子味的糖衣。&lt;/p&gt;&lt;p&gt;上课的时候，我会趁老师转身写板书，悄悄往后递小纸条，写些无聊的废话，或者画个丑兮兮的小人；课间她会戳我的后背，递过来一颗水果糖，或者跟我吐槽刚才那道数学题有多离谱。晚自习我依旧坐讲台上，目光却总不自觉地飘向她的座位，撞见她抬头看我，就各自偷偷弯起嘴角。&lt;/p&gt;&lt;p&gt;周末除了打游戏，我们也会约着出去。去公园漫无目的地散步，去街边小店吃牛杂，去电影院看喜欢的动漫、电影。她走路总爱牵着我的手，而我总是觉得人多而不好意思。那时候总觉得日子很长，长到我们可以这样并肩走很久很久。&lt;/p&gt;&lt;p&gt;我把游戏 ID 改成了藏着她名字的字符，把日常的细碎都写进仅彼此可见的空间里，连后来做第一个小项目时，下意识就用了“听雨”的名字。那时候没想过以后会分开，只觉得这份喜欢要藏进所有我能留下的痕迹里。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;高二：走进彼此的日常&lt;a href=&quot;#高二走进彼此的日常&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;到了高二，我们的关系才算真正往彼此的生活里扎了根。&lt;/p&gt;&lt;p&gt;刚升学年那会儿，我们还守着各自的节奏，吃饭结伴旧友，课间各有圈子，算不上时时刻刻黏在一起。变故藏在那年的秋风里：她和相交多年的闺蜜闹了绝交，原本热热闹闹的日常忽然空了一大块。那段日子她总独来独往，连吃饭都缩在食堂角落，安安静静的。&lt;/p&gt;&lt;p&gt;我便顺理成章地走进了她的空档里。午饭陪她挤在食堂长桌，晚饭后绕着塑胶跑道慢慢散步，从教学楼前的香樟树下，走到田径场的铁丝网边。从前那些分给朋友的时间，渐渐都换成了彼此的陪伴。&lt;/p&gt;&lt;p&gt;后来我成了体育生，这份陪伴也没被训练冲散，反倒成了更笃定的日常。每天下午下课铃响，我直奔操场训练，她就安安静静地等我——有时在看台坐着翻书，有时回教室写作业，算着我结束的时间。等我带着一身汗气跑完练完，两人再慢悠悠晃去食堂吃晚饭。&lt;/p&gt;&lt;p&gt;饭后她先回教室自习，我回宿舍冲澡换衣，赶在晚自修开始前的十几分钟，凑到她桌边挨着说些没营养的闲话，腻歪好一阵才舍得回自己座位。&lt;/p&gt;&lt;p&gt;那些日子重复又细碎，却比任何告白都更让人安心。好像从那时起，我们才不只是“在一起的两个人”，而是真正走进了彼此的日常里。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;高三：遗憾&lt;a href=&quot;#高三遗憾&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;转眼到了高三，日子被高考的倒计时推着往前走，可我整个人却陷入一种浑浑噩噩的状态。明明已经迫近高考，我却没有半点奋力冲刺的劲头，成绩不进反退，越埋头学，越觉得力不从心，分数一次比一次不尽如人意。&lt;/p&gt;&lt;p&gt;是我的选择与失误，最后让我们没能奔赴同一所大学。&lt;/p&gt;&lt;p&gt;异地的距离横亘在我们之间，也埋下了往后走散的伏笔。或许这算不上分开的直接导火索，却是绕不开的间接缘由。少年人的感情，抵不住现实的落差。当两个人不能再朝夕相处，那些日常的陪伴、课间的碰面，很多矛盾与隔阂，也由此慢慢滋生。&lt;/p&gt;&lt;p&gt;现在回头看，这便是青春里一大桩无可奈何的遗憾。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;分开之后&lt;a href=&quot;#分开之后&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;后来的故事，那就不是那么开心的故事了，其实也俗套：分班、升学、磨合、争吵。少年人的感情热烈又脆弱，曾经以为能撑过所有难关，最终还是走散在了半路。&lt;/p&gt;&lt;p&gt;分开后的那段日子其实不好过，我花了很久才慢慢走出来。只是时至今日，再想起那些年月，最先冒出来的依旧是走廊的风、毽子落地的声响、晚自习的暖黄灯光，还有她眼睛里亮闪闪的光。&lt;/p&gt;&lt;p&gt;我没改掉那些带着她痕迹的名字，不是还在等什么，只是舍不得那段被照亮的岁月。毕竟是她，在我最黯淡的年纪里，认认真真地，做了我的光。&lt;/p&gt;&lt;p&gt;如今再翻起游戏截图，想起当年一手露娜带飞的意气，想起金标到手时迫不及待想分享的心情，想起那些藏在 ID 和名字里的少年心事，只剩一点淡淡的、温柔的怀念。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;朝朝听雨，岁岁有风&lt;a href=&quot;#朝朝听雨岁岁有风&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最后呢，希望大家：物物而不物于物，念念而不念于念。&lt;/p&gt;&lt;p&gt;而我，&lt;/p&gt;&lt;p&gt;朝朝听雨，岁岁有风。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>Agent 赋予电商自动化：从业务规则到可控的自动上架框架</title><link>http://rainzt.cn/posts/agent-ecommerce-automation/</link><guid isPermaLink="true">http://rainzt.cn/posts/agent-ecommerce-automation/</guid><description>从一套自动上架系统的实践出发，拆解 AI Agent、Playwright、CDP 和 ERP 如何组合成一套可观察、可回滚、可扩展的跨境电商自动化框架。</description><pubDate>Sun, 09 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2026 年 5 月底，我第一次把 AI 真正放进跨境电商的日常工作里。不是让它帮我写一段宣传文案，也不是问它一个 API 怎么调用，而是让它参与搭建一套全自动上架系统。&lt;/p&gt;
&lt;p&gt;这件事最初的动机很朴素：每天重复打开页面、整理表格、上传图片、匹配规格、修改标题，再盯着页面确认有没有保存成功。单次操作并不难，难的是每天重复几十次以后，人的注意力会先于代码耗尽。&lt;/p&gt;
&lt;p&gt;后来我发现，电商自动化真正难的部分从来不是“让浏览器点一下按钮”，而是把业务规则讲清楚，再把每个容易出错的环节做成可以观察、可以重试、可以追责的流程。下面就记录这套思路，也把我在实践中踩过的坑一起写下来。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;先说结论：自动化的核心不是点击，而是规则&lt;a href=&quot;#先说结论自动化的核心不是点击而是规则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;很多人第一次做浏览器自动化，会从录制操作开始：打开页面、点击按钮、填入文本、上传文件，然后把录制结果保存成脚本。这种方式在演示阶段很直观，但一旦页面结构、商品规格或图片顺序发生变化，脚本就会变得非常脆弱。&lt;/p&gt;&lt;p&gt;我现在更愿意把一次上架拆成四层：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;数据层&lt;/strong&gt;：表格、图片、视频、规格和标题素材分别是什么。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规则层&lt;/strong&gt;：什么图片属于哪个 SKU，什么规格允许发布，标题如何拼接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行层&lt;/strong&gt;：由 Playwright、CDP 和平台适配器完成页面操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;观察层&lt;/strong&gt;：任务做到哪一步、为什么失败、是否进入异常队列，都要有记录。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;可以把它理解成下面这条链路：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;flowchart LR
    A[表格与素材] --&amp;gt; B[规则解析]
    B --&amp;gt; C[任务队列]
    C --&amp;gt; D[平台 Worker]
    D --&amp;gt; E[Playwright + CDP]
    E --&amp;gt; F[页面校验]
    F --&amp;gt; G[截图、日志、结果]
    G --&amp;gt; H{是否成功}
    H --&amp;gt;|是| I[归档]
    H --&amp;gt;|否| J[重试或异常队列]&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;我希望它是全自动执行，但不是无法解释的黑盒。自动化可以替我完成机械动作，每一步也必须留下可以追溯的痕迹。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;为什么我更推荐从 ERP 开始&lt;a href=&quot;#为什么我更推荐从-erp-开始&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;理论上，直接操控店铺后台当然也能实现自动上架。但在实践中，我更推荐先围绕 ERP 做自动化，原因主要有三个。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;1. ERP 的数据更结构化&lt;a href=&quot;#1-erp-的数据更结构化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;商品名称、规格、库存、价格、图片和视频通常都已经被整理成字段。自动化系统不必从一个复杂页面里猜测“这段文字到底是什么”，而是可以先读取结构化数据，再按照模板生成任务。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 风险边界更清楚&lt;a href=&quot;#2-风险边界更清楚&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;如果所有平台都直接接入，某个平台的小改版就可能影响整条流程。ERP 作为中间层，可以把共用的数据准备、素材管理和任务记录放在一起，再把 Shopee、TikTok Shop 等平台的差异放进各自的 Worker 里。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. 多平台差异可以被隔离&lt;a href=&quot;#3-多平台差异可以被隔离&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Shopee 和 TikTok Shop 的页面入口、编辑按钮、保存逻辑和成功提示并不一样。如果强行写成一套流程，后期会出现大量条件分支。更稳定的方式是共用底座，平台只负责自己的适配。&lt;/p&gt;&lt;p&gt;指纹浏览器同样可以做自动化，而且对某些场景很方便。但账号风险、登录状态和长期大规模运行是否稳定，不能只凭一次成功运行就下结论。使用这类方案时，需要自行评估平台规则、账号权限和风控边界，不要把“能跑通”误认为“可以无限放大”。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;站外浏览器自动化接口与支持框架&quot; loading=&quot;lazy&quot; width=&quot;1267&quot; height=&quot;602&quot; src=&quot;/_astro/image1.BmpDIoV6_BAozs.webp&quot; srcset=&quot;/_astro/image1.BmpDIoV6_ZAQj3T.webp 640w, /_astro/image1.BmpDIoV6_Z14ppSN.webp 750w, /_astro/image1.BmpDIoV6_Z1wOd61.webp 828w, /_astro/image1.BmpDIoV6_12YaOF.webp 1080w, /_astro/image1.BmpDIoV6_BAozs.webp 1267w&quot; /&gt;&lt;figcaption&gt;站外浏览器自动化接口与支持框架&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;技术底座：Playwright、CDP 与固定 Chromium&lt;a href=&quot;#技术底座playwrightcdp-与固定-chromium&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我的技术组合是 Playwright + CDP + 固定版本 Chromium。&lt;/p&gt;&lt;p&gt;Playwright 负责定位元素、填写表单、上传素材、等待页面状态和截取结果；CDP 负责连接已经打开的浏览器；固定版本 Chromium 则尽量减少不同浏览器版本带来的接口差异。&lt;/p&gt;&lt;p&gt;连接浏览器的大致代码如下：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;chromium&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Browser&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;playwright&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connectBrowser&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cdpUrl&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Browser&lt;/span&gt;&lt;span&gt;&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chromium&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;connectOverCDP&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cdpUrl&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;browser&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connectBrowser&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;http://127.0.0.1:9222&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;browser&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;contexts&lt;/span&gt;&lt;span&gt;()[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;browser&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;newContext&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;pages&lt;/span&gt;&lt;span&gt;()[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;newPage&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;goto&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;https://example.com/products&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;waitUntil&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;domcontentloaded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;固定 Chromium 的代价是项目体积会大一些，但它解决了两个实际问题：一是不同浏览器版本的接口行为不完全一致，二是图片上传顺序、文件选择器和页面等待时机更容易出现细微差异。对于需要重复运行的任务，我更愿意牺牲一些体积，换取结果的一致性。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;固定版本 Chromium 运行环境&quot; loading=&quot;lazy&quot; width=&quot;1103&quot; height=&quot;632&quot; src=&quot;/_astro/image2.D2sJ9Ii4_Z2s6p9U.webp&quot; srcset=&quot;/_astro/image2.D2sJ9Ii4_Z2qc6iG.webp 640w, /_astro/image2.D2sJ9Ii4_Z1sLVlx.webp 750w, /_astro/image2.D2sJ9Ii4_Z26kUJm.webp 828w, /_astro/image2.D2sJ9Ii4_Z1d3ScW.webp 1080w, /_astro/image2.D2sJ9Ii4_Z2s6p9U.webp 1103w&quot; /&gt;&lt;figcaption&gt;固定版本 Chromium 运行环境&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;需要注意的是，CDP 不是“绕过平台规则”的通行证。它只是让自动化程序连接到浏览器调试端口，实际仍然是在页面上进行正常的用户操作。登录状态、权限和平台规则都应该由运营者自己负责。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;AI Agent 到底帮了什么忙&lt;a href=&quot;#ai-agent-到底帮了什么忙&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我并没有让 AI 直接生成一个“万能上架机器人”，而是把它当成一个会观察、会提问、会整理的开发搭档。&lt;/p&gt;&lt;p&gt;它最有价值的地方，通常在下面几个环节：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;观察页面&lt;/strong&gt;：读取当前页面的按钮、表格、规格和错误提示。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拆解流程&lt;/strong&gt;：把“上架一个商品”拆成可验证的步骤。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定位问题&lt;/strong&gt;：当选择器失效时，根据截图和日志判断是页面未加载、入口变化还是权限问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;整理规则&lt;/strong&gt;：把散落在聊天记录和经验里的判断，整理成配置文件和校验函数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生成样板代码&lt;/strong&gt;：先快速搭出 Worker、日志、重试和页面对象，再由我检查细节。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;最重要的一点是：AI 可以帮我更快地把想法变成可运行的东西，但它不知道我的业务边界。比如“这个 SKU 的第 3 张图不能上传”可能是一个临时异常，也可能是这个产品本来就没有第 3 张图。最终决定规则的人，仍然是我。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;两种业务场景，不能用同一套规则&lt;a href=&quot;#两种业务场景不能用同一套规则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;POD 产品：规则稳定，适合批量化&lt;a href=&quot;#pod-产品规则稳定适合批量化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;POD 产品通常有比较规整的数据和素材：款式、颜色、尺寸、主图、详情图和变体之间有明确关系。只要把模板定义清楚，就可以批量处理很多商品。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;搬运或铺货：差异更大，但同样可以全自动&lt;a href=&quot;#搬运或铺货差异更大但同样可以全自动&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;搬运和铺货同样可以全自动。它们通常需要翻译、清理厂家信息、生成场景图、判断平台类目、重新组织规格，规则确实比 POD 更复杂，但复杂不等于必须人工逐条确认。只要把这些判断拆成可配置的规则和可调用的 AI 步骤，就可以让采集、清洗、翻译、配图、SKU 生成、上架和发布全部自动跑完。&lt;/p&gt;&lt;p&gt;真正需要保留的是异常队列，而不是人工审批链：缺少必填字段、图片不符合尺寸、类目无法判断或平台返回风险提示时，任务暂停在可定位的节点，修正规则或补齐数据后再自动继续。我的做法是共享任务队列、日志和素材中心，但为不同产品线保留独立模板和校验策略。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;批量铺货与任务配置界面&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;832&quot; src=&quot;/_astro/image4.BdCfIs8t_1QLiPG.webp&quot; srcset=&quot;/_astro/image4.BdCfIs8t_2lqPbc.webp 640w, /_astro/image4.BdCfIs8t_1QLiPG.webp 699w&quot; /&gt;&lt;figcaption&gt;批量铺货与任务配置界面&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;图片与 SKU 的匹配：最容易被低估的难题&lt;a href=&quot;#图片与-sku-的匹配最容易被低估的难题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;图片文件名看起来很简单，真正处理起来却非常容易错位。&lt;/p&gt;&lt;p&gt;例如下面是一组商品素材：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;AB001-1.jpg  # AB001 主图&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;AB001-2.jpg  # AB001 详情图&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;AB001-3.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;AB001-4.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;AB001-5.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;AB002-1.jpg  # AB002 另一种颜色或规格的主图&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;AB002-2.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;最基础的规则是：同一个前缀属于同一个图片组，&lt;code&gt;-1&lt;/code&gt; 作为主图，其余作为详情图。但仅靠字符串切分还不够，至少要同时做三层校验：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AssetGroup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cover&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;detail&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;groupAssets&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;files&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AssetGroup&lt;/span&gt;&lt;span&gt;[] {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;groups&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[]&amp;gt;();&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;files&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)-(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;\d&lt;/span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;(png&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;jpe&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;webp)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;continue&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [, &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;groups&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;groups&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;&lt;span&gt;groups&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt;()].&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;&lt;span&gt;(([&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;]) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ordered&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aNo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Number&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/-(&lt;/span&gt;&lt;span&gt;\d&lt;/span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)?.[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;999&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bNo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Number&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/-(&lt;/span&gt;&lt;span&gt;\d&lt;/span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)?.[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;999&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aNo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bNo&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;cover&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ordered&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;detail&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ordered&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;slice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;代码只能完成“文件名分组”，不能代替业务校验。真正执行上传前，还要检查：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;当前 ERP 页面显示的 SKU 是否等于图片组的 &lt;code&gt;key&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;主图是否确实是 &lt;code&gt;-1&lt;/code&gt;，有没有缺号或重复编号。&lt;/li&gt;
&lt;li&gt;当前变体的颜色、尺寸与表格字段是否一致。&lt;/li&gt;
&lt;li&gt;当前任务有没有读到下一组产品的图片。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;第三点和第四点很关键。曾经遇到过流程全部显示成功，但图片被挂到了错误 SKU 上。因为系统只看到了“上传成功”，却没有验证“上传到了正确的位置”。自动化要判断的是业务结果，而不是浏览器动作有没有报错。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;批量编辑中的图片分组与 SKU 关系&quot; loading=&quot;lazy&quot; width=&quot;893&quot; height=&quot;424&quot; src=&quot;/_astro/image5.DHByjJdD_Z16iV2O.webp&quot; srcset=&quot;/_astro/image5.DHByjJdD_Z18yrsX.webp 640w, /_astro/image5.DHByjJdD_1mAPw2.webp 750w, /_astro/image5.DHByjJdD_DafiL.webp 828w, /_astro/image5.DHByjJdD_Z16iV2O.webp 893w&quot; /&gt;&lt;figcaption&gt;批量编辑中的图片分组与 SKU 关系&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;变体图片上传位置校验&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;199&quot; src=&quot;/_astro/image6.BPrftriA_1kBfvS.webp&quot; srcset=&quot;/_astro/image6.BPrftriA_8zJa3.webp 640w, /_astro/image6.BPrftriA_Z19XzTO.webp 750w, /_astro/image6.BPrftriA_1kBfvS.webp 806w&quot; /&gt;&lt;figcaption&gt;变体图片上传位置校验&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;把平台操作写成 Worker，而不是一条超长脚本&lt;a href=&quot;#把平台操作写成-worker而不是一条超长脚本&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一个平台 Worker 可以只负责一件事：接收标准化任务，完成这个平台的页面操作，并返回结构化结果。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ListingTask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;variants&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Array&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;{ &lt;/span&gt;&lt;span&gt;sku&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] }&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ListingResult&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ok&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;publishToShopee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;playwright&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;Page&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ListingTask&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ListingResult&lt;/span&gt;&lt;span&gt;&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getByPlaceholder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;搜索商品&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getByRole&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;编辑&quot;&lt;/span&gt;&lt;span&gt; }).&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getByLabel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;商品标题&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;uploadImages&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;variants&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;verifyVariants&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;variants&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getByRole&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;保存并发布&quot;&lt;/span&gt;&lt;span&gt; }).&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getByText&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;发布成功&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;waitFor&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;visible&quot;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;ok&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;published&quot;&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ok&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;publish&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;instanceof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;Shopee 与 TikTok Shop 的平台任务工作区&quot; loading=&quot;lazy&quot; width=&quot;1267&quot; height=&quot;587&quot; src=&quot;/_astro/image3.DRhkBReV_1kLwUr.webp&quot; srcset=&quot;/_astro/image3.DRhkBReV_Z39hHh.webp 640w, /_astro/image3.DRhkBReV_Z28QWWJ.webp 750w, /_astro/image3.DRhkBReV_2sUnDY.webp 828w, /_astro/image3.DRhkBReV_Z2nySsK.webp 1080w, /_astro/image3.DRhkBReV_1kLwUr.webp 1267w&quot; /&gt;&lt;figcaption&gt;Shopee 与 TikTok Shop 的平台任务工作区&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;这里有两个值得保留的习惯：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;每完成一个高风险步骤，就记录一个明确的 &lt;code&gt;step&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;返回结构化结果，而不是只在控制台打印一句“失败了”。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这样做的好处是，后续可以把 Shopee、TikTok Shop 或其他平台的结果统一放进任务历史里，界面不需要知道每个平台内部的细节。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;失败重试，但不要无脑重试&lt;a href=&quot;#失败重试但不要无脑重试&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;网络波动、页面响应慢和临时弹窗都可能造成偶发失败，所以任务需要重试机制。但“失败就再点一次”也很危险，尤其是发布、扣库存或创建优惠券这类不可逆动作。&lt;/p&gt;&lt;p&gt;我会把步骤分成三类：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;可安全重试&lt;/strong&gt;：打开页面、读取列表、等待元素、获取截图。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要幂等判断后重试&lt;/strong&gt;：上传图片、保存草稿、更新标题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不做盲目重试&lt;/strong&gt;：最终发布、扣库存、创建订单等有副作用的操作，先做幂等校验，再由任务状态机自动决定是否继续。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;一个简单的重试包装器可以这样写：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;retry&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;&amp;gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;options&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;attempts&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;delayMs&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;attempts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;options&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;attempts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delayMs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;options&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;delayMs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;800&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastError&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;attempts&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastError&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;attempts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;delayMs&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastError&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;真正接入系统时，还要给重试加上截图、当前 URL、任务 ID 和步骤名。否则过几天回头看日志，只知道某个任务失败过，却不知道当时页面到底是什么状态。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;为什么一定要做可视化界面&lt;a href=&quot;#为什么一定要做可视化界面&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;自动化不是越黑盒越高级。对于电商任务，我希望界面至少能回答下面这些问题：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;当前正在处理哪一个商品、哪一个 SKU？&lt;/li&gt;
&lt;li&gt;已经完成了多少步，卡在哪一步？&lt;/li&gt;
&lt;li&gt;失败是网络问题、页面变化，还是业务规则不匹配？&lt;/li&gt;
&lt;li&gt;图片和规格有没有完成对应校验？&lt;/li&gt;
&lt;li&gt;是否保存了关键截图和日志？&lt;/li&gt;
&lt;li&gt;这个任务可以安全重试，还是应该进入异常队列？&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;所以我会把运行面板当成产品的一部分，而不是开发阶段的调试工具。实时日志、成功/失败统计、暂停/恢复、截图留档和历史记录，都会直接影响运营人员是否敢于使用这套系统。&lt;/p&gt;&lt;p&gt;最理想的状态不是“所有任务都永远成功”，而是任务失败时，系统能在几十秒内定位原因，把任务放回重试队列或异常队列，而不是让人重新从头操作。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;从自动上架继续往外扩展&lt;a href=&quot;#从自动上架继续往外扩展&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;自动上架只是一个入口。围绕同一套数据、素材和任务系统，后续可以继续扩展：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;AI 生成商品主图、换背景和场景图；&lt;/li&gt;
&lt;li&gt;根据平台规则自动生成多语言标题和描述；&lt;/li&gt;
&lt;li&gt;采集供应商商品并清理厂家信息；&lt;/li&gt;
&lt;li&gt;跑图、算价、整理利润和库存；&lt;/li&gt;
&lt;li&gt;批量设置折扣、优惠券和活动价格；&lt;/li&gt;
&lt;li&gt;在发布前做违禁词、图片尺寸和品牌信息检查；&lt;/li&gt;
&lt;li&gt;用队列调度不同店铺，并保留团队操作审计。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这些功能看起来分散，其实共享的是同一条底座：结构化数据、可追踪的任务、明确的规则和可靠的执行器。底座稳定以后，新增一个工具不再是复制一份脚本，而是接入同一套任务系统。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;AI Agent 串联电商自动化工作流&quot; loading=&quot;lazy&quot; width=&quot;924&quot; height=&quot;439&quot; src=&quot;/_astro/image7.BQC36XSQ_Z10dgVP.webp&quot; srcset=&quot;/_astro/image7.BQC36XSQ_7aeGB.webp 640w, /_astro/image7.BQC36XSQ_Z2rQB7k.webp 750w, /_astro/image7.BQC36XSQ_1SSVtl.webp 828w, /_astro/image7.BQC36XSQ_Z10dgVP.webp 924w&quot; /&gt;&lt;figcaption&gt;AI Agent 串联电商自动化工作流&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;最后：Agent 替我完成动作，但不会替我承担判断&lt;a href=&quot;#最后agent-替我完成动作但不会替我承担判断&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这次实践让我最明显的感受是，AI 把“想法到原型”的距离压缩得非常短。以前可能要先查文档、搭环境、写一堆样板代码，才能看到第一个页面；现在可以先把业务目标、页面截图和现有规则交给 AI，让它帮助我拆分模块、定位元素、补齐日志，再由我持续校正。&lt;/p&gt;&lt;p&gt;但效率提高以后，真正重要的判断反而更贵了：为什么要做这个工具？哪些规则必须固定？哪些异常不能自动处理？出了问题由谁确认和负责？&lt;/p&gt;&lt;p&gt;Agent 可以观察页面、整理数据、调用工具，也可以陪我把一个流程逐步变成代码。它能替我完成大量机械执行，却不能替我决定什么结果才算正确。&lt;/p&gt;&lt;p&gt;所以我对“AI 赋予电商自动化”的理解，不是让人从系统里消失，而是让人从重复点击和逐条审批里出来，把时间留给规则设计、体验和异常处理。全自动铺货并不意味着盲目放行，而是让每个判断都有规则、有校验、有记录，只有真正无法判断的异常才会被单独拎出来。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>作品展示页动画实现原理</title><link>http://rainzt.cn/posts/portfolio-showcase-animation-principles/</link><guid isPermaLink="true">http://rainzt.cn/posts/portfolio-showcase-animation-principles/</guid><description>从滚动读数、目标与渲染进度、分镜区间出发，拆解 Rainzt.cn 作品展示页当前的工程化实现：长滚动固定舞台、requestAnimationFrame 平滑、媒体解码、快门转场与内容可访问性边界。</description><pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最初做作品展示页时，我把它理解成“让五张卡片依次动起来”。后来页面经历了几轮调整，真正变化的不是多加了几个位移或淡入，而是实现的重心从“堆动画效果”转成了“维护一条可控制、可验证的滚动时间轴”。&lt;/p&gt;
&lt;p&gt;现在的页面仍然没有引入重型动画库。Astro 负责结构和资源声明，CSS 负责固定舞台、裁切和图层关系，原生 TypeScript 只做一件事：把浏览器滚动位置转换为稳定的分镜状态。卡片、黑场快门、角色、视频和结尾文案都消费同一个进度值，但它们不再直接绑定原始滚动事件。&lt;/p&gt;
&lt;p&gt;这篇文章记录作品展示页的实现思路。文中的区间、资源策略和代码片段以当时的实现为准，不再使用早期的 &lt;code&gt;760vh / 680vh&lt;/code&gt; 时间轴或“滚一下就立刻改样式”的写法。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;目标不是播放，而是让读者控制节奏&lt;a href=&quot;#目标不是播放而是让读者控制节奏&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;作品展示区有几个约束，它们决定了这不是一组普通的 CSS 入场动画：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;读者慢慢滚动时，每一张作品卡片都要有足够的停留时间；停下来时，画面也应停住。&lt;/li&gt;
&lt;li&gt;快速滚动、触摸板惯性和不同刷新率不应让画面突然跳帧。&lt;/li&gt;
&lt;li&gt;转场要有连续的图层关系，不能让黑场、角色和下一幕像三次互不相干的淡入。&lt;/li&gt;
&lt;li&gt;首次进入展示区时，图片解码不能集中阻塞主线程。&lt;/li&gt;
&lt;li&gt;动画不是作品信息的唯一载体；键盘和辅助技术用户仍应能在普通作品列表中访问每个链接。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;换句话说，这一页的输入不是“经过了多少秒”，而是“读者在叙事中的位置”。页面滚动位置只是输入信号，真正被渲染的是一个受控的 &lt;code&gt;0 ~ 1&lt;/code&gt; 进度。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;scroll / resize&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;目标进度 targetShowcaseProgress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;requestAnimationFrame 平滑追赶&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;渲染进度 renderedShowcaseProgress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;phaseAt() 分镜区间 -&amp;gt; 卡片 / 快门 / 结尾场景&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;固定舞台：用滚动距离换取阅读时间&lt;a href=&quot;#固定舞台用滚动距离换取阅读时间&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;展示区使用“长容器 + sticky 舞台”的结构。长容器提供滚动距离，舞台始终固定在视口内，所有镜头都在这个舞台上完成。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;cinematic-showcase&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;showcase&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;showcase-pin&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-showcase&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;showcase-intro&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;showcase-panels&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;showcase-interlude&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;showcase-finale&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;当前 CSS 使用的是桌面端 &lt;code&gt;850vh&lt;/code&gt;、窄屏端 &lt;code&gt;760vh&lt;/code&gt;，而不是早期更短的数值：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.cinematic-showcase&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position: &lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;850&lt;/span&gt;&lt;span&gt;vh&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;margin-top: &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.showcase-pin&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position: &lt;/span&gt;&lt;span&gt;sticky&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;top: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;svh&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;min-height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;37.2&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;overflow: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;isolation: &lt;/span&gt;&lt;span&gt;isolate&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (max-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.cinematic-showcase&lt;/span&gt;&lt;span&gt; { height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;760&lt;/span&gt;&lt;span&gt;vh&lt;/span&gt;&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;这里的 &lt;code&gt;850vh&lt;/code&gt; 不是动画持续时间，更像是时间轴的可用长度。真正可用的滚动距离不是容器高度本身，而是容器高度减去视口高度：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;measureShowcase&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;showcaseSection&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showcaseStart&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;scrollY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showcaseSection&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getBoundingClientRect&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;top&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showcaseDistance&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showcaseSection&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;offsetHeight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;innerHeight&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;clamp&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;scrollY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showcaseStart&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showcaseDistance&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这样做有两个细节。&lt;/p&gt;&lt;p&gt;第一，&lt;code&gt;showcaseStart&lt;/code&gt; 在测量时写成相对文档的绝对位置，而不是每一帧都重新读取 &lt;code&gt;getBoundingClientRect()&lt;/code&gt;。滚动路径中只需计算一次减法和一次夹取，避免不必要的布局读取。&lt;/p&gt;&lt;p&gt;第二，舞台高度选用 &lt;code&gt;100svh&lt;/code&gt;。手机浏览器地址栏展开和收起时，&lt;code&gt;svh&lt;/code&gt; 比传统 &lt;code&gt;vh&lt;/code&gt; 更稳定；同时在 &lt;code&gt;resize&lt;/code&gt; 时重新测量，并立即同步渲染进度，避免视口变化后动画从旧位置缓慢追赶到新位置。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resizeShowcase&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;measureShowcase&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;targetShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getShowcaseProgress&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;targetShowcaseProgress&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;applyShowcaseProgress&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;移动端并没有简单地把故事压缩得更快。当前实现缩短了总行程，并隐藏卡片中的长说明文字；保留编号和标题，让每个镜头仍有可理解的停留。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;不在 scroll 回调里直接渲染&lt;a href=&quot;#不在-scroll-回调里直接渲染&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最容易写出的版本，是在每次 &lt;code&gt;scroll&lt;/code&gt; 事件中读取进度并修改所有元素的 &lt;code&gt;style&lt;/code&gt;。它在鼠标滚轮上似乎可行，但触摸板惯性、主线程短暂停顿和高刷新率设备会让同一段动画显得忽快忽慢。&lt;/p&gt;&lt;p&gt;当前实现把“用户想去哪里”和“当前画面画到哪里”分开：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SCROLL_SCRUB_DURATION&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;180&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SHOWCASE_SETTLE_EPSILON&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.00008&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;targetShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showcaseFrame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastShowcaseTimestamp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;requestShowcaseUpdate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;targetShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getShowcaseProgress&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;showcaseFrame&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastShowcaseTimestamp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showcaseFrame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;requestAnimationFrame&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;renderShowcase&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;滚动事件只更新目标值，并且以被动监听注册：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;scroll&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;requestShowcaseUpdate&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;passive&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;resize&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;resizeShowcase&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;passive&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;实际渲染发生在 &lt;code&gt;requestAnimationFrame&lt;/code&gt; 中。这里没有使用固定比例的 &lt;code&gt;progress += 0.1&lt;/code&gt;，而是使用基于时间差的指数平滑：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renderShowcase&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;timestamp&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;elapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastShowcaseTimestamp&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;64&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;timestamp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastShowcaseTimestamp&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;16.67&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastShowcaseTimestamp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;timestamp&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blend&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;exp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt;elapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SCROLL_SCRUB_DURATION&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;targetShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blend&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;abs&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;targetShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;SHOWCASE_SETTLE_EPSILON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;targetShowcaseProgress&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;applyShowcaseProgress&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;abs&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;targetShowcaseProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renderedShowcaseProgress&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;SHOWCASE_SETTLE_EPSILON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showcaseFrame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;requestAnimationFrame&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;renderShowcase&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showcaseFrame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastShowcaseTimestamp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;blend = 1 - exp(-dt / duration)&lt;/code&gt; 的好处是刷新率无关：在 60Hz 和 120Hz 屏幕上，画面趋近目标的速度一致。&lt;code&gt;elapsed&lt;/code&gt; 被限制到 64ms，标签页从后台恢复时也不会因一个异常大的时间差直接跳到终点。进度足够接近后停止申请下一帧，减少静止状态下的无效工作。&lt;/p&gt;&lt;p&gt;渲染函数内部还有一层更细的保护：当新旧进度相差小于 &lt;code&gt;0.00001&lt;/code&gt; 时，不再重写样式。这一层在慢速滚动或停住后很有价值，因为页面的图层比普通卡片页多得多。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;phaseAt：把连续进度切成可维护的分镜&lt;a href=&quot;#phaseat把连续进度切成可维护的分镜&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;所有镜头都从同一个 &lt;code&gt;progress&lt;/code&gt; 出发，但每个镜头只关心属于自己的时间窗口。为此实现了一个“局部阶段函数”：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clamp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clamp&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phaseAt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;progress&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;progress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.001&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;phaseAt(progress, start, end)&lt;/code&gt; 会先把任意区间归一化到 &lt;code&gt;0 ~ 1&lt;/code&gt;，再套一层 smoothstep。它有三个稳定的状态：开始前为 0，区间内平滑变化，结束后为 1。这样每段动画不需要知道页面总高度，也不用自己处理越界。&lt;/p&gt;&lt;p&gt;当前时间轴大致如下：&lt;/p&gt;












































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;分镜&lt;/th&gt;&lt;th&gt;进度区间&lt;/th&gt;&lt;th&gt;目的&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;引导文案淡出&lt;/td&gt;&lt;td&gt;0.00 - 约 0.21&lt;/td&gt;&lt;td&gt;让读者从引导进入作品卡片&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;五张卡片入场&lt;/td&gt;&lt;td&gt;第 &lt;em&gt;i&lt;/em&gt; 张为 0.04 + 0.055i 到 0.16 + 0.055i&lt;/td&gt;&lt;td&gt;逐张落位，最后一张不被前几张挤压&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;卡片文字入场&lt;/td&gt;&lt;td&gt;第 &lt;em&gt;i&lt;/em&gt; 张为 0.16 + 0.05i 到 0.25 + 0.05i&lt;/td&gt;&lt;td&gt;图片先建立画面，再交代标题&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;卡片退场&lt;/td&gt;&lt;td&gt;0.60 - 0.72&lt;/td&gt;&lt;td&gt;统一清空，不留下半透明边框&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;黑场与角色&lt;/td&gt;&lt;td&gt;0.63 - 0.97&lt;/td&gt;&lt;td&gt;把卡片阶段交给快门转场&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;上下快门合拢 / 展开&lt;/td&gt;&lt;td&gt;0.72 - 0.84 / 0.91 - 0.97&lt;/td&gt;&lt;td&gt;形成完整的一次闭合与打开&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;结尾场景&lt;/td&gt;&lt;td&gt;0.88 - 1.00&lt;/td&gt;&lt;td&gt;在转场遮挡下预热，再分层揭示&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;表格里的数字不是“魔法常量”，而是对叙事节奏的显式描述。它们集中在渲染函数里，调整某一幕时只需要移动相邻区间，不会牵连一堆独立的 &lt;code&gt;@keyframes&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;卡片为什么要同时算透明度和几何退场&lt;a href=&quot;#卡片为什么要同时算透明度和几何退场&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;五张作品卡片由数据数组生成，动画代码只使用索引。入场和退场使用同一组局部进度：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;inProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phase&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.04&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.055&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.16&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.055&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dissolve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phase&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.60&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.72&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;direction&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startingY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;inProgress&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;direction&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;150&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fanX&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dissolve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;3.25&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fanY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dissolve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1.35&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;panel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;opacity&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;inProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dissolve&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;panel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;transform&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;translate3d(&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fanX&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vw, &quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;startingY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fanY&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;%, 0) &quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;scale(&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dissolve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.12&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;) &quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;rotate(&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; ((&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dissolve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1.35&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;deg)&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;入场时，奇偶卡片从相反方向进入；退场时，它们微微散开、缩小并旋转。更重要的是，透明度使用 &lt;code&gt;inProgress * (1 - dissolve)&lt;/code&gt;，所以退场结束时一定回到 0。&lt;/p&gt;&lt;p&gt;这个约束是有原因的。早期版本曾保留少量半透明残影，卡片边框会在纯黑转场和窗景上变成一层浅灰横带。对于这种需要明确切镜的页面，残影不是氛围，而是上一幕没有离开干净的证据。&lt;/p&gt;&lt;p&gt;卡片文字单独安排了入场和退场，避免与图片一起仓促消失：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copyIn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phase&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.16&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.05&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.25&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.05&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copyOut&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phase&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.58&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.70&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;opacity&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;copyIn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copyOut&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;transform&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;translate3d(0, &quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copyIn&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;18&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dissolve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;26&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;px, 0)&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这让“图像抵达”和“文字可阅读”成为两个独立的节拍。设计上真正需要保证的不是卡片出现得多炫，而是最后一张卡片完全出现后，读者仍有机会理解它。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;快门转场不是淡出，而是一段可交接的场景&lt;a href=&quot;#快门转场不是淡出而是一段可交接的场景&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;卡片退场后，展示区进入黑场。这里的核心不是给画面叠一层黑色，而是明确每一层的职责：&lt;/p&gt;




























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;图层&lt;/th&gt;&lt;th&gt;作用&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;黑色背景&lt;/td&gt;&lt;td&gt;切断前一段丰富的卡片画面，给下一幕提供干净底色&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;上下两条横幅&lt;/td&gt;&lt;td&gt;从左右移动，完成快门的合拢和再次展开&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;前景角色&lt;/td&gt;&lt;td&gt;在黑场中连接卡片阶段与结尾场景&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;两个大字&lt;/td&gt;&lt;td&gt;只在快门闭合后的短暂读秒出现&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;结尾场景&lt;/td&gt;&lt;td&gt;先在黑场后方完成准备，再在遮挡结束后被看见&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;上下横幅不是只进不出。它们先从画面外移动到中间，在角色和文字退出时回到两侧：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stripsIn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phase&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.72&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.84&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stripsOut&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phase&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.91&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.97&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stripPosition&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stripsIn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stripsOut&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;shutterStripLeft&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;transform&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;translate3d(&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; ((&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stripPosition&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;108&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;%, 0, 0)&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;shutterStripRight&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;transform&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;translate3d(&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; ((&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stripPosition&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;108&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;%, 0, 0)&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;结尾场景则从 &lt;code&gt;0.88&lt;/code&gt; 就开始在快门后方进入：整体场景先渐显，视频和中景在 &lt;code&gt;0.90 - 0.97&lt;/code&gt; 进入，前景角色到 &lt;code&gt;0.94 - 1.00&lt;/code&gt; 才落位。这个重叠不是浪费，而是为了让读者先看到“场景已在”，再感受到前、中、后景的空间关系。&lt;/p&gt;&lt;p&gt;视频只在进度超过一个很小的阈值后尝试播放，并忽略被浏览器自动播放策略拒绝的结果：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phase&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.90&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.97&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;finaleVideo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;opacity&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;setVisibility&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;finaleVideo&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.02&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;finaleVideo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;paused&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;finaleVideo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;visibility&lt;/code&gt; 会在图层完全不可见时关闭其可见性；视频仍然带有 &lt;code&gt;muted&lt;/code&gt;、&lt;code&gt;loop&lt;/code&gt;、&lt;code&gt;playsinline&lt;/code&gt; 和 &lt;code&gt;preload=“auto”&lt;/code&gt;。即使视频不能自动播放，静态中景和前景仍会完整呈现，结尾不会变成空白。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;资源预热：不要把所有图片同时 decode&lt;a href=&quot;#资源预热不要把所有图片同时-decode&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;展示区一次会用到卡片、快门和结尾场景的多张图片。如果在首次进入页面时对所有图片立即调用 &lt;code&gt;decode()&lt;/code&gt;，图片解码可能和首段滚动竞争主线程，反而让第一幕出现卡顿。&lt;/p&gt;&lt;p&gt;当前策略按两个图片为一组预热，每完成一组就让出一个动画帧：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;warmShowcaseMedia&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;batch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showcaseImageBatches&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;batch&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;waitForImage&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;decode&lt;/span&gt;&lt;span&gt;?.().&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;requestAnimationFrame&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;waitForImage()&lt;/code&gt; 同时监听 &lt;code&gt;load&lt;/code&gt; 和 &lt;code&gt;error&lt;/code&gt;，单张资源失败不会把整条预热链卡住。这个策略不追求“最早把所有内容解码完”，而是控制一次被调度到主线程的工作量，让首页和第一段滚动仍保持响应。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;动画层不承担链接职责&lt;a href=&quot;#动画层不承担链接职责&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;展示区不承担项目链接职责：卡片列表和快门转场容器标记为 &lt;code&gt;aria-hidden&lt;/code&gt;，结尾场景中的图片使用空的 &lt;code&gt;alt&lt;/code&gt;。真正可点击、带标题和描述的项目内容在下面的普通作品网格中：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;showcase-panels&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;works-section&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;works&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;work-card&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;work-card-link&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这避免了同一批作品被屏幕阅读器重复朗读，也让动效失效、被跳过或尚未加载时，内容仍有一条稳定的阅读路径。首屏的“向下看作品”入口指向 &lt;code&gt;#showcase&lt;/code&gt;，保证想体验叙事的读者进入动画开头；而普通网格则负责承载可访问的项目导航。&lt;/p&gt;&lt;p&gt;这里还有一个应继续完善的点：当前的 &lt;code&gt;prefers-reduced-motion&lt;/code&gt; 已经关闭了常规内容的 reveal 过渡，但还没有为整段分镜提供专门的静态展示模式。由于作品链接不依赖动画层，内容不会丢失；下一步更理想的做法是让该媒体查询直接展示首帧或静态作品列表，并跳过滚动平滑与视频播放。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;验证不是“看起来能动”&lt;a href=&quot;#验证不是看起来能动&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;每次调整分镜后，我会从三个方向检查：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;节奏：慢慢滚动时，第五张卡片和结尾文案是否都有可阅读的停留；快速滚动后画面是否平稳追上目标，而不是闪到终点。&lt;/li&gt;
&lt;li&gt;图层：卡片退场后是否确实为 0；黑场是否干净；快门再次打开时，结尾场景是否已经准备好而没有白屏或裸露的中间状态。&lt;/li&gt;
&lt;li&gt;环境：在展示区内改变窗口尺寸或旋转手机，进度是否保持；禁用或拒绝视频自动播放时，结尾是否仍可理解；键盘访问时，作品链接是否只在普通列表中出现一次。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;内容改动完成后，仓库内使用以下命令检查：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type-check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;结语&lt;a href=&quot;#结语&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这页最终留下的不是一套“滚动动画技巧”，而是一种更适合维护的拆分方式：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;CSS 管舞台、裁切和图层；&lt;/li&gt;
&lt;li&gt;滚动事件只更新目标；&lt;/li&gt;
&lt;li&gt;rAF 负责在帧间平滑渲染；&lt;/li&gt;
&lt;li&gt;分镜函数把连续进度切成可读的区间；&lt;/li&gt;
&lt;li&gt;资源预热和可访问内容各自有独立边界。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;当动画需要继续加镜头、换素材或调整节奏时，最重要的不是继续往页面里塞效果，而是先问清楚：这段变化处在哪个区间、它和前后镜头如何交接、内容是否仍有不依赖动画的路径。把这些问题写进结构之后，滚动才会真正成为叙事，而不是一串碰巧同时发生的视觉效果。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>写完这一批工具后，我重新打开了自己的博客</title><link>http://rainzt.cn/posts/after-the-tools-i-returned-to-my-blog/</link><guid isPermaLink="true">http://rainzt.cn/posts/after-the-tools-i-returned-to-my-blog/</guid><description>跨境电商自动化定制项目告一段落后，我重新回望电脑里堆满的工具文件夹，也重新想清楚了初代博客与第二代博客各自要承载什么。</description><pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近一段时间，电脑终于安静了一点。&lt;/p&gt;
&lt;p&gt;不是风扇不转了，也不是消息提示彻底消失了，而是那些排着队等我处理的定制项目，大多已经交付。剩下的一些收尾、反馈和维护，也交给合作人员继续跟进。连续很久被需求、调试和交付日期推着往前走之后，日程表上总算出现了一点没有被预先占满的空白。&lt;/p&gt;
&lt;p&gt;我没有立刻开一个新项目，而是先整理电脑。&lt;/p&gt;
&lt;p&gt;桌面上有周报、表格转换、上架表格制作、库存清理、批量库存处理、SKU 销量汇总；往文件夹里再翻，还有订单筛选、活动 Excel 工具、图片搜索、错误 SKU 修改、自动识别、主图优化和上架状态监控。Shopee、TikTok Shop、美客多，不同平台的规则被拆进不同脚本里；同一个工具又会因为客户流程不同，留下好几个版本。&lt;/p&gt;
&lt;p&gt;这些文件夹没有统一命名，也谈不上整齐，但它们比周报更诚实地记录了我前段时间在做什么。&lt;/p&gt;
&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;桌面上的 SKU、周报、表格与库存工具&quot; loading=&quot;lazy&quot; width=&quot;394&quot; height=&quot;205&quot; src=&quot;/_astro/desktop-tools-1.BHYpwXuP_Z1JT9Ki.webp&quot; srcset=&quot;/_astro/desktop-tools-1.BHYpwXuP_Z1JT9Ki.webp 394w&quot; /&gt;&lt;figcaption&gt;桌面上的 SKU、周报、表格与库存工具&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;围绕 TikTok、美客多和主图处理建立的项目文件夹&quot; loading=&quot;lazy&quot; width=&quot;162&quot; height=&quot;200&quot; src=&quot;/_astro/desktop-tools-2.UVDPNNx8_1iLX24.webp&quot; srcset=&quot;/_astro/desktop-tools-2.UVDPNNx8_1iLX24.webp 162w&quot; /&gt;&lt;figcaption&gt;围绕 TikTok、美客多和主图处理建立的项目文件夹&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;订单筛选、活动 Excel、自动上架等项目目录&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;250&quot; src=&quot;/_astro/project-folders-1.De9pbmqU_24Wz0Y.webp&quot; srcset=&quot;/_astro/project-folders-1.De9pbmqU_24Wz0Y.webp 386w&quot; /&gt;&lt;figcaption&gt;订单筛选、活动 Excel、自动上架等项目目录&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;上架监控、博客与各类辅助程序目录&quot; loading=&quot;lazy&quot; width=&quot;392&quot; height=&quot;401&quot; src=&quot;/_astro/project-folders-2.OdYEdK6k_Z1Vg5Mr.webp&quot; srcset=&quot;/_astro/project-folders-2.OdYEdK6k_Z1Vg5Mr.webp 392w&quot; /&gt;&lt;figcaption&gt;上架监控、博客与各类辅助程序目录&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;项目名称有的直白，有的临时，有的还带着“新”“最终版”的痕迹。它们拼在一起，就是这段时间最具体的工作记录。&lt;/em&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;文件夹背后，是一个个很具体的问题&lt;a href=&quot;#文件夹背后是一个个很具体的问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;做自动化工具久了，很容易把工作概括成几个听起来很大的词：跨境电商、批量处理、流程自动化、AI 辅助。&lt;/p&gt;&lt;p&gt;可真正落到手上的需求，通常没有这么抽象。&lt;/p&gt;&lt;p&gt;它可能只是商家每天都要从平台导出一份 Excel，再按固定规则删除、筛选和拆分；可能是几千个 SKU 的库存状态对不上，需要从多个表格里重新汇总；也可能是同一批商品要适配不同平台的上架模板，字段名称、图片顺序、变体规则都不一样。还有一些工作并不难，却要不断重复：打开页面、复制内容、上传图片、检查状态、等待结果，再把异常项目重新处理一遍。&lt;/p&gt;&lt;p&gt;这些动作单独看都很小，连续做几个小时却足以消耗掉一个人的耐心。&lt;/p&gt;&lt;p&gt;我的工作，就是先把商家原本的操作完整走一遍，弄清楚哪些步骤不能变、哪些判断藏在经验里、哪些异常以前全靠人工兜底，再把它们一点点写进程序。需求沟通、流程拆解、脚本编写、界面调整、数据校验、打包交付，最后再根据真实使用反馈继续改。&lt;/p&gt;&lt;p&gt;有时候最费时间的并不是写代码，而是听懂一句“这里和以前一样处理就行”。所谓“以前一样”，背后可能藏着十几条没有写进文档的规则。只有把这些规则问出来、试出来，自动化才不是机械地替人点击，而是真的能接住工作。&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;将多店铺 Shopee 订单集中处理的订单筛选工具&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;913&quot; src=&quot;/_astro/shopee-order-filter.DCyKafFp_Z13XUhr.webp&quot; srcset=&quot;/_astro/shopee-order-filter.DCyKafFp_Z2hUpxS.webp 640w, /_astro/shopee-order-filter.DCyKafFp_1JJVzk.webp 750w, /_astro/shopee-order-filter.DCyKafFp_2qjGcP.webp 828w, /_astro/shopee-order-filter.DCyKafFp_Z1wwr0j.webp 1080w, /_astro/shopee-order-filter.DCyKafFp_1QarVJ.webp 1280w, /_astro/shopee-order-filter.DCyKafFp_m1Bsg.webp 1668w, /_astro/shopee-order-filter.DCyKafFp_Z13XUhr.webp 1920w&quot; /&gt;&lt;figcaption&gt;将多店铺 Shopee 订单集中处理的订单筛选工具&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;围绕图案分类、检索与导出设计的智能管理系统&quot; loading=&quot;lazy&quot; width=&quot;1650&quot; height=&quot;878&quot; src=&quot;/_astro/pattern-library.VMJctkGg_Z13A07h.webp&quot; srcset=&quot;/_astro/pattern-library.VMJctkGg_49qQo.webp 640w, /_astro/pattern-library.VMJctkGg_21yjuk.webp 750w, /_astro/pattern-library.VMJctkGg_Z18gXBP.webp 828w, /_astro/pattern-library.VMJctkGg_ZCFsQ0.webp 1080w, /_astro/pattern-library.VMJctkGg_w9fuV.webp 1280w, /_astro/pattern-library.VMJctkGg_Z13A07h.webp 1650w&quot; /&gt;&lt;figcaption&gt;围绕图案分类、检索与导出设计的智能管理系统&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;一个项目处理订单数据，一个项目管理图像资产。界面和用途不同，出发点却一样：把反复发生的工作整理成清晰、稳定、可复用的流程。&lt;/em&gt;&lt;/p&gt;&lt;p&gt;这也是为什么电脑里的工具越来越多。它们并不是我事先规划好的一张产品路线图，而是现实需求一点点推出来的结果。一个客户遇到库存问题，就有了库存工具；另一个客户长期受困于上架流程，就继续往表格、图片、浏览器自动化和状态监控上延伸。&lt;/p&gt;&lt;p&gt;回头看，这些看似零散的文件夹，其实是一张很具体的需求地图。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;初代博客，曾经更像一间线上店铺&lt;a href=&quot;#初代博客曾经更像一间线上店铺&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;工具越做越多之后，我自然需要一个集中展示它们的地方。于是有了初代博客。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;初代博客首页&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;913&quot; src=&quot;/_astro/first-generation-blog.BhQoqQn2_LFWXM.webp&quot; srcset=&quot;/_astro/first-generation-blog.BhQoqQn2_Z2vAcxt.webp 640w, /_astro/first-generation-blog.BhQoqQn2_ZhdXGK.webp 750w, /_astro/first-generation-blog.BhQoqQn2_ZuG99q.webp 828w, /_astro/first-generation-blog.BhQoqQn2_Z1GQTF8.webp 1080w, /_astro/first-generation-blog.BhQoqQn2_1sEeWz.webp 1280w, /_astro/first-generation-blog.BhQoqQn2_Z290EYV.webp 1668w, /_astro/first-generation-blog.BhQoqQn2_LFWXM.webp 1920w&quot; /&gt;&lt;figcaption&gt;初代博客首页&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;那时搭建网站的目标非常明确：展示能力、介绍工具、承接需求。首页要让访客尽快知道我能做什么，项目页面要说明功能、参数和使用场景，文案也要回答潜在客户最关心的问题——这个程序能解决什么、适不适合自己的业务、要怎样联系开发者。&lt;/p&gt;&lt;p&gt;从这个目标看，初代博客并没有做错什么。它认真完成了当时的任务，也留下了我做服务器、前端和脚本开发的一段经历。&lt;/p&gt;&lt;p&gt;只是现在再回头看，我发现它更像一间布置过的线上店铺，而不是我真正想长期写下去的博客。&lt;/p&gt;&lt;p&gt;我习惯在文章里介绍“工具具备哪些功能”，却很少写“我为什么会想到这样做”；会认真展示运行效果，却不太记录调试到深夜时到底卡在了哪里；会把项目整理成一份完整、漂亮的说明，却略过了那些绕路、误判和临时改主意的瞬间。&lt;/p&gt;&lt;p&gt;网站里有我完成的东西，却不一定有完整的我。&lt;/p&gt;&lt;p&gt;这不是初代博客的问题，而是那个阶段的我确实把大部分注意力放在了业务上。那时的网站首先要服务接单，个人表达自然排在后面。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第二代博客，是一次有意的减法&lt;a href=&quot;#第二代博客是一次有意的减法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;重新搭建 &lt;code&gt;rainzt.cn&lt;/code&gt; 时，我最想做的并不是增加多少功能，而是减掉一种过强的目的性。&lt;/p&gt;&lt;p&gt;第二代博客仍然会写自动化工具，也不会刻意回避商业项目。跨境电商自动化依旧是我会长期深耕的方向，真实项目里积累的经验，也恰恰是最值得整理的内容。不同的是，我不再要求每篇文章都承担展示产品和转化客户的任务。&lt;/p&gt;&lt;p&gt;我想写下批量库存工具为什么会从一张表格变成一套流程，自动上架系统如何一点点适配 Shopee 与 TikTok Shop，主图处理为什么不能只追求“批量生成”，还要考虑平台规则、视觉一致性和异常回退。我也想记录服务器运维、前端页面打磨和 AI 辅助开发中的判断：哪些方案最后保留下来，哪些看起来聪明却不适合真实使用。&lt;/p&gt;&lt;p&gt;这些内容可能没有一份产品说明那么干净。它们会有失败、有犹豫，也会有“后来才发现”的补充。但对我来说，恰恰是这些不够标准的部分，才构成了真正的开发经历。&lt;/p&gt;&lt;p&gt;除此之外，我也想给生活留一点位置。&lt;/p&gt;&lt;p&gt;不一定每次都要讲一项新技术，也不一定非要总结出一条可复用的方法。有时只是项目结束后的松弛，一次重新整理桌面的感触，或者某个晚上突然想清楚的一件小事。它们不能直接换来效率，却能让我在几年之后回头时，知道自己当时如何生活、如何理解手里的工作。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;让 AI 回到助手的位置&lt;a href=&quot;#让-ai-回到助手的位置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;忙着赶项目时，我也把写文章变成过一条自动化流水线。&lt;/p&gt;&lt;p&gt;把 README、功能清单、代码逻辑和截图交给 AI，它很快就能整理出标题、章节和结尾。事实基本正确，结构也比我临时组织得更完整。项目刚交付、人已经很疲惫的时候，这种方式确实替我省下了许多时间。&lt;/p&gt;&lt;p&gt;但这种方便用久了，我也会感觉到一种落差：文章把项目说明白了，却没有把我留下来。&lt;/p&gt;&lt;p&gt;AI 能从资料里提炼出“系统实现了批量上架”，却不知道某个按钮为什么改过三次；它能总结订单筛选规则，却没有经历过客户一句含糊描述背后的来回确认；它可以把功能写得很专业，却很难替我判断，哪一次失败最值得记录，哪一个看似不起眼的细节真正改变了后来做项目的方法。&lt;/p&gt;&lt;p&gt;所以我并不打算拒绝 AI，也不会把“全部手写”当作一种必须遵守的仪式。我仍然会用它查漏、整理资料、校对表达，甚至在没有头绪时帮我搭一个起点。&lt;/p&gt;&lt;p&gt;我只是想慢慢把它从“替我完整交稿”的位置，放回“陪我整理和修改”的位置。重要的经历由我回忆，真正的判断由我确认，那些只属于当时心境的句子，也尽量留给自己慢慢写。&lt;/p&gt;&lt;p&gt;效率可以帮助我完成更多事情，但有些内容只有在放慢以后才会浮上来。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;两种创作，可以并行&lt;a href=&quot;#两种创作可以并行&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目告一段落，不代表商业工具开发也告一段落。&lt;/p&gt;&lt;p&gt;以后我仍然会接触新的平台规则、新的表格格式和新的运营流程，也会继续把合适的重复劳动做成程序。商用代码解决的是客户此刻真实存在的问题，它需要稳定、准确、按时交付，也需要在变化的平台环境里不断维护。&lt;/p&gt;&lt;p&gt;而博客可以承担另一件事：保留那些不适合写进交付文档的东西。&lt;/p&gt;&lt;p&gt;这里可以有技术方案，也可以有开发感受；可以复盘一套自动上架系统，也可以只记录某次调试后的想法。商业项目追求明确结果，私人写作允许暂时没有结论。前者让我不断面对真实需求，后者让我不至于只剩下一张完成项目的清单。&lt;/p&gt;&lt;p&gt;它们并不冲突。&lt;/p&gt;&lt;p&gt;代码把复杂操作封装起来，让别人少做一些重复劳动；文字把已经过去的经历重新展开，让我看见自己是怎样走到现在。一个向外解决问题，一个向内保存思考，都是创作，只是面对的对象不同。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;重新开始更新&lt;a href=&quot;#重新开始更新&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;整理电脑时，我没有删除那些密密麻麻的项目文件夹。&lt;/p&gt;&lt;p&gt;有些工具以后还会继续迭代，有些可能完成使命后就停在当前版本。再过几年，其中一部分程序也许已经不适配新的平台，甚至连当时依赖的页面都不存在了。但如果把开发过程、做过的判断和那段时间的感受写下来，它们就不会只剩下一个无法运行的旧文件。&lt;/p&gt;&lt;p&gt;这大概就是我重新深耕第二代博客的原因。&lt;/p&gt;&lt;p&gt;我并不想把它重新经营成一面更精致的商品橱窗，也不急着证明自己能保持多高的更新频率。我只想在忙碌告一段落之后，重新留出一块可以慢慢写字的地方。&lt;/p&gt;&lt;p&gt;往后，工具还会继续做，需求还会继续变化，电脑里的文件夹大概也不会真正变少。&lt;/p&gt;&lt;p&gt;但这一次，我希望它们增长的同时，博客里也能留下另一条时间线：不只记录我完成了什么，也记录我为什么开始、如何走过，以及一路上究竟想过些什么。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>当 AI 能替我写完项目博文，我为什么还想敲下这些字</title><link>http://rainzt.cn/posts/why-i-still-write-in-the-age-of-ai/</link><guid isPermaLink="true">http://rainzt.cn/posts/why-i-still-write-in-the-age-of-ai/</guid><description>当 AI 十分钟就能写完一篇结构清晰的项目介绍，亲手写作还有什么意义？聊聊效率、表达、个人经验，以及我对 AI 未来的一点期待和不安。</description><pubDate>Wed, 22 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;翻了翻站点统计，博客上线差不多半个月，三篇文章里有两篇是项目作品。&lt;/p&gt;
&lt;p&gt;说句实话，这两篇项目介绍，大半功劳不在我。&lt;/p&gt;
&lt;p&gt;把项目的 README、核心脚本逻辑、几张运行截图丢给 AI，再告诉它一句“写成一篇博客风格的项目介绍文”，不出十分钟，一篇结构清晰、术语准确、连阅读时长都算得刚刚好的文章就出来了。改改措辞，核对几个技术细节，补一张封面图，直接就能发布上线。&lt;/p&gt;
&lt;p&gt;省事，真的省事。&lt;/p&gt;
&lt;p&gt;做跨境电商自动上架系统那阵子，我每天泡在 BigSeller、Shopee 店铺规则和表格批量处理里。功能跑通的那一刻，只想往椅背上一瘫，完全不想再把已经折腾过一遍的东西整理成文章。换作以前，项目背景、实现逻辑、使用说明，至少还要花一两个晚上慢慢磨；现在把文档交给 AI，一篇近两千字的初稿很快就能摆在面前。&lt;/p&gt;
&lt;p&gt;后来做 AI 无限画布工作流也是一样。从 Firefly 节点调试到 GPT Image 接入，从画布布局到批量出图逻辑，整个过程踩了不少坑。但真要写文章的时候，我的第一反应还是打开 AI：“帮我把这套电商主图生成工作流写成博客。”&lt;/p&gt;
&lt;p&gt;效率拉满，但总觉得哪里不太对。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/assets/images/wallpaper/wallpaper-42.webp&quot; alt=&quot;隔着窗看向远处，也像是在重新看自己的表达&quot; /&gt;&lt;figcaption&gt;隔着窗看向远处，也像是在重新看自己的表达&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;十分钟生成一篇“正确”的文章&lt;a href=&quot;#十分钟生成一篇正确的文章&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果只从结果看，AI 写项目介绍几乎挑不出什么大问题。&lt;/p&gt;&lt;p&gt;它知道应该先讲痛点，再讲架构；知道把一堆零散功能整理成几个模块；也知道在结尾补上一段愿景，让一个还在迭代中的项目显得完整而有方向。它能精准地写出：“这套系统打通了 BigSeller、Shopee 与 TikTok Shop，实现了批量上架自动化。”也能清晰地拆解：“无限画布工作流包含产品图导入、提示词编排、风格参考与批量生成。”&lt;/p&gt;&lt;p&gt;这些话都没错，甚至比我临时组织出来的表达更准确。&lt;/p&gt;&lt;p&gt;更麻烦的是，它不只写得快，有时还写得比我好。标题更像标题，小节更像小节，句子长短有节奏，连那些我脑子里只有模糊轮廓的想法，它也能替我补成一段听起来很成熟的论述。&lt;/p&gt;&lt;p&gt;从效率的角度，我实在找不到拒绝它的理由。&lt;/p&gt;&lt;p&gt;但“正确”慢慢变成了一种很奇怪的东西。文章什么都有：背景、功能、流程、优势、愿景，唯独少了一点只有我知道的东西。&lt;/p&gt;&lt;p&gt;它写不出我最开始做自动上架工具的起因，是运营时一天要上架几十款商品，重复操作到鼠标手犯了，才咬着牙想写个脚本解放自己。它也写不出调试接口时，一个参数没对齐卡了整整一下午，最后发现不是我理解错了，而是文档真的写错了。那种想生气又忍不住觉得荒唐的心情，不在 README 里，也不在代码注释里。&lt;/p&gt;&lt;p&gt;它能整理无限画布的模块，却不知道我第一次跑通全流程时，看着一整张画布上的主图方案依次生成，比自己出了爆款还开心。它更不知道我为什么放着现成的 AI 绘图工具不用，非要搭一套本地画布：因为市面上的工具很方便，但永远没法完全贴合跨境电商主图那些细碎、麻烦、又不能忽略的规则。&lt;/p&gt;&lt;p&gt;AI 能读取项目，却没有经历项目。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/assets/images/wallpaper/wallpaper-27.webp&quot; alt=&quot;AI 可以迅速铺开一整张信息画布，但经历仍然来自画布之外&quot; /&gt;&lt;figcaption&gt;AI 可以迅速铺开一整张信息画布，但经历仍然来自画布之外&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;真正不对劲的，不是 AI 替我写了&lt;a href=&quot;#真正不对劲的不是-ai-替我写了&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;今天对着站点归档发愣的时候，我突然反应过来：我好像已经很久没有“手写”过一篇完整的文章了。&lt;/p&gt;&lt;p&gt;不是因为没东西可写。恰恰相反，项目越做越多，能写的内容也越来越多。只是每次准备记录时，我都会自然地选择最快的路径：整理资料，交给 AI，核对事实，发布。&lt;/p&gt;&lt;p&gt;这个流程没有错。问题在于，当我把所有表达都外包出去，博客也很容易从“我经历过什么”变成“我完成过什么”。前者是成长记录，后者更像项目陈列柜。&lt;/p&gt;&lt;p&gt;想起写&lt;a href=&quot;https://rainzt.cn/posts/build-rainzt-blog/&quot;&gt;《从零搭建 Rainzt.cn》&lt;/a&gt;的时候，那是几篇文章里唯一一篇从头写到尾的。从本地 Astro 项目初始化，到宝塔面板 Nginx 配置，再到 HTTPS 证书踩的坑，每一步都是自己走过的，每一行命令也都是亲手敲过的。&lt;/p&gt;&lt;p&gt;写的时候没觉得它有多特别。现在回头看，文章里那些不够规整的叙述、细碎的报错和临时补上的提醒，反而比一篇标准的项目介绍更有温度。因为它不只是告诉别人“怎么搭博客”，也保留了当时的我怎么理解问题、怎么走弯路、怎么把站点一点点推上线。&lt;/p&gt;&lt;p&gt;真正让我不舒服的，从来不是 AI 参与写作，而是我差点只留下结果，没有留下自己。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;我对 AI 的感受，是兴奋也是不安&lt;a href=&quot;#我对-ai-的感受是兴奋也是不安&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我其实很喜欢现在的 AI。&lt;/p&gt;&lt;p&gt;它把很多过去需要先学习很久才能开始的事情，变成了“先做起来，再边做边学”。不熟悉前端，也可以一边描述需求一边搭页面；不会设计完整工作流，也可以先让 AI 帮忙拆模块；面对陌生代码库，不再需要从第一行硬啃到最后一行，AI 可以先给出地图，再陪着定位问题。&lt;/p&gt;&lt;p&gt;这种能力很容易让人上瘾。想法和成品之间原本隔着一大片重复劳动，现在那段距离被迅速压短。以前一个念头可能只停在备忘录里，现在真的有机会在当天变成能运行的东西。&lt;/p&gt;&lt;p&gt;但不安也来自同一个地方。&lt;/p&gt;&lt;p&gt;当文字、代码、图片甚至视频都能快速生成，完成一件作品不再天然等于投入了多少时间。过去我们常常通过“他写了多少”“他做了多久”判断一件事的分量，未来这些线索会越来越不可靠。一个人可能没有写过其中的大部分代码，却真正决定了产品该解决什么问题；也可能生成了上万字内容，却没有一句是自己认真相信的。&lt;/p&gt;&lt;p&gt;AI 让产出变得便宜，也让判断变得昂贵。&lt;/p&gt;&lt;p&gt;我现在越来越在意的，不是“这个东西是不是亲手一行行做出来的”，而是另外几个问题：为什么要做？你选择保留什么、舍弃什么？遇到冲突时相信哪条原则？结果出了问题，谁愿意承担责任？&lt;/p&gt;&lt;p&gt;这些问题暂时还不能交给一个“生成”按钮。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/assets/images/wallpaper/wallpaper-35.webp&quot; alt=&quot;人与技术真正有意思的地方，不是彼此替代，而是如何碰到一起&quot; /&gt;&lt;figcaption&gt;人与技术真正有意思的地方，不是彼此替代，而是如何碰到一起&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;未来的 AI，可能不会再像一个聊天框&lt;a href=&quot;#未来的-ai可能不会再像一个聊天框&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;现在我们习惯打开一个窗口，输入提示词，等待 AI 回答。可我觉得这种“我问一句，它答一句”的形态，只是很早期的界面。&lt;/p&gt;&lt;p&gt;接下来的 AI 会越来越像一层安静的工作环境。它知道我正在维护哪个项目，理解之前做过的决定，能读取文档、操作工具、检查运行结果，也能在任务持续几天甚至几周时保留上下文。它不只给建议，还会真的执行：修改代码、生成素材、整理数据、运行测试、发布版本，再把需要人判断的部分交回来。&lt;/p&gt;&lt;p&gt;到那时，“会不会写提示词”可能没现在想象得那么重要。真正重要的会变成：能不能把模糊的愿望说成清楚的目标，能不能给出边界和约束，能不能判断结果到底好不好，能不能在效率、成本、风险和体验之间做取舍。&lt;/p&gt;&lt;p&gt;这也是我做自动上架系统和无限画布时越来越明显的感受。工具真正成熟以后，人不应该一直盯着每个按钮和步骤；人更应该决定规则，处理例外，观察结果，再把新的经验沉淀回系统。AI 会吃掉大量机械执行，却不会自动替我们决定“什么值得被做”。&lt;/p&gt;&lt;p&gt;也许未来最稀缺的不是生产内容的能力，而是提出一个真问题的能力。&lt;/p&gt;&lt;p&gt;当所有人都可以快速生成一篇像样的文章、一张漂亮的图、一段能运行的代码时，真正拉开差距的会是经验、审美、取舍和长期积累。AI 可以组合已知答案，但一个人为什么在某天突然想做一个工具，为什么对某个细节执着，为什么愿意为一个看似很小的问题连续调试几天，这些东西仍然来自具体的生活。&lt;/p&gt;&lt;p&gt;AI 可以帮我描述今天，却不能替我活过今天。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;使用 AI，不等于放弃原创&lt;a href=&quot;#使用-ai不等于放弃原创&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我不太想把“亲手写”和“AI 写”分成非黑即白的两边。&lt;/p&gt;&lt;p&gt;原创也不一定意味着每个字都必须从键盘上纯手工长出来。摄影师不需要自己制造相机，程序员也不会因为使用开源库就失去作品署名。对我来说，更重要的是文章的经验从哪里来，观点是不是我真正认同的，最后呈现的内容由谁确认、谁负责。&lt;/p&gt;&lt;p&gt;如果 AI 写出了一段很漂亮的话，但我自己并不相信，只因为它听起来像正确答案就放进文章，那它很难算是我的表达。反过来，如果经历是我的，困惑是我的，判断和取舍也是我的，AI 帮我整理结构、调整节奏、删掉重复，我并不觉得这会让文章失去价值。&lt;/p&gt;&lt;p&gt;关键不是有没有用 AI，而是使用 AI 之后，我还在不在里面。&lt;/p&gt;&lt;p&gt;所以以后写项目介绍，我大概还是会继续让 AI 帮忙。技术结构、功能清单、流程说明，本来就适合交给它整理。它能让我少耗几晚时间，把精力留给真正需要解决的问题。&lt;/p&gt;&lt;p&gt;但我也想给自己留下一小块不追求效率的地方。写一点踩坑时的吐槽，记下功能跑通那一刻的心情，承认某些选择其实没有宏大的理由，只是我当时觉得这样更顺手。哪怕句子没那么漂亮、结构没那么标准，也至少能让我几年后再打开博客时，认得出那是自己留下的声音。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/assets/images/wallpaper/wallpaper-39.webp&quot; alt=&quot;技术会继续向前，而我也想在路上保留一点自己的声音&quot; /&gt;&lt;figcaption&gt;技术会继续向前，而我也想在路上保留一点自己的声音&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;给那剩下的 10%&lt;a href=&quot;#给那剩下的-10&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;博客当然可以是一块项目展示板。它可以有 AI 生成的、规整清晰的技术说明，也可以快速记录每次上线和版本更新。工具本来就是用来解决问题的，我做自动化、搭 AI 工作流，本来就是因为我相信效率能把人从重复劳动里释放出来。&lt;/p&gt;&lt;p&gt;但博客也应该容得下一些没那么“有用”的文字。&lt;/p&gt;&lt;p&gt;可能是一次失败后的复盘，可能是做完项目的碎碎念，也可能只是像今天这样，忽然对着几篇文章产生的一点怀疑。它们不一定能带来搜索流量，也不一定能成为教程，却能组成一条更真实的时间线。&lt;/p&gt;&lt;p&gt;AI 能帮我完成 90% 的项目文案。剩下那 10%，可能不够专业，也很难批量生产，但它恰好记录了我为什么开始、为什么坚持、为什么在某个瞬间感到开心或疲惫。&lt;/p&gt;&lt;p&gt;这 10% 不需要和 AI 对抗。它只是提醒我，在享受效率的时候，别把自己的感受也一起自动化掉。&lt;/p&gt;&lt;p&gt;以后我大概还是会一边用 AI 快速做项目，一边偶尔慢下来写点这样的东西。因为博客说到底，不只是给别人看的作品列表，也是写给未来自己的成长记录。&lt;/p&gt;&lt;p&gt;其实写到一半还是觉得Ai写的方便，所以上面这些还是Ai写的（苦笑）&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>AI 无限画布工作流：为电商主图生成搭建一套可编排的创作中枢</title><link>http://rainzt.cn/posts/ai-infinite-canvas-workflow/</link><guid isPermaLink="true">http://rainzt.cn/posts/ai-infinite-canvas-workflow/</guid><description>一套面向电商主图批量生成的本地可视化工作台，把产品图、提示词、参考风格、文案规则、GPT Image 节点和结果复盘组织进无限画布，并为后续视频工作流留下扩展空间。</description><pubDate>Tue, 21 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;电商主图的生产，往往卡在一种看似琐碎、实际高频的往返里：找产品图、写场景描述、补品牌规则、调整视觉风格、反复生成，再把满意的结果归档。单次操作并不复杂，但一旦要做批量素材、多人协作或持续迭代，素材、提示词和结果就很容易散落在不同工具与文件夹里。&lt;/p&gt;
&lt;p&gt;这套 AI 无限画布工作流，想解决的正是这件事。它是一套面向电商主图批量生成的本地可视化工作台，把创作过程从一串零散操作，变成一张可以看见、编辑、复用和追溯的生产地图。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;为什么要做这套画布&lt;a href=&quot;#为什么要做这套画布&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;传统图片生成通常以对话或单个表单为入口，适合快速尝试，却不擅长表达完整的创作关系。产品图从哪里来，使用了哪版场景提示词，参考了哪张风格图，哪次生成最适合上架，往往需要靠人工记忆和文件命名维持。&lt;/p&gt;&lt;p&gt;无限画布提供了另一种组织方式：每一个输入、规则、模型调用和结果都是一个节点；节点之间的连线，就是素材流动和决策发生的路径。画布不是装饰，它让工作流本身成为可阅读、可讨论的内容。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;AI 无限画布总览&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;912&quot; src=&quot;/_astro/workflow-canvas.7Ol9Au9u_fx5od.webp&quot; srcset=&quot;/_astro/workflow-canvas.7Ol9Au9u_1KWnKX.webp 640w, /_astro/workflow-canvas.7Ol9Au9u_2dWDcC.webp 750w, /_astro/workflow-canvas.7Ol9Au9u_Z1kYFXk.webp 828w, /_astro/workflow-canvas.7Ol9Au9u_oSBns.webp 1080w, /_astro/workflow-canvas.7Ol9Au9u_Z151Ifl.webp 1280w, /_astro/workflow-canvas.7Ol9Au9u_Zl8S5r.webp 1668w, /_astro/workflow-canvas.7Ol9Au9u_fx5od.webp 1920w&quot; /&gt;&lt;figcaption&gt;AI 无限画布总览&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;现在先把图片主图跑稳&lt;a href=&quot;#现在先把图片主图跑稳&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;当前版本聚焦图片，目标不是做一个只会出图的入口，而是把电商主图生产中真正需要反复使用的部分固定下来。&lt;/p&gt;&lt;p&gt;从产品图开始，可以连接场景描述、参考风格、品牌文案与商品卖点，再把这些信息汇入视觉提示词节点和 GPT Image 生成节点。生成完成后，结果图会直接回到画布中，成为下一轮调整、对比或复用的输入。&lt;/p&gt;&lt;p&gt;这种结构很适合电商素材：同一件商品可以快速尝试不同场景和构图；同一套品牌规则也可以被多个商品项目复用。创作不再从空白提示词开始，而是从已经沉淀下来的工作流继续。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;主图工坊&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;912&quot; src=&quot;/_astro/main-studio.COGrilPr_1Ltzy7.webp&quot; srcset=&quot;/_astro/main-studio.COGrilPr_Z1HFxLR.webp 640w, /_astro/main-studio.COGrilPr_Z1xRXKV.webp 750w, /_astro/main-studio.COGrilPr_1OJPlg.webp 828w, /_astro/main-studio.COGrilPr_Z1j9S1P.webp 1080w, /_astro/main-studio.COGrilPr_Z9Ju6y.webp 1280w, /_astro/main-studio.COGrilPr_ZkYlUD.webp 1668w, /_astro/main-studio.COGrilPr_1Ltzy7.webp 1920w&quot; /&gt;&lt;figcaption&gt;主图工坊&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;节点化，让规则可以复用&lt;a href=&quot;#节点化让规则可以复用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;画布中的节点不是单一的“输入框”。它们承载的是不同类型的生产信息：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;产品图片和商品信息，定义画面的主体。&lt;/li&gt;
&lt;li&gt;场景、风格和参考图，控制画面的氛围与视觉方向。&lt;/li&gt;
&lt;li&gt;文案规则和品牌约束，保证素材能够被实际使用。&lt;/li&gt;
&lt;li&gt;视觉提示词和图片生成节点，把前面的信息汇总为可执行的生成任务。&lt;/li&gt;
&lt;li&gt;预览、结果和执行状态节点，帮助快速判断一次任务是否值得保留。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;节点库把这些能力按用途整理，画布则允许它们按实际需求自由组合。对于一个成熟的主图方案，可以保存为模板；对于一次试验，也可以局部替换一个节点，而不影响整张画布的其他部分。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;节点库与工作流编排&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;912&quot; src=&quot;/_astro/node-library.7rH1q38R_ZnRstm.webp&quot; srcset=&quot;/_astro/node-library.7rH1q38R_2lAGjM.webp 640w, /_astro/node-library.7rH1q38R_dhfpV.webp 750w, /_astro/node-library.7rH1q38R_d8I9A.webp 828w, /_astro/node-library.7rH1q38R_Z1Hz92X.webp 1080w, /_astro/node-library.7rH1q38R_1wzKUW.webp 1280w, /_astro/node-library.7rH1q38R_Z1mNlGt.webp 1668w, /_astro/node-library.7rH1q38R_ZnRstm.webp 1920w&quot; /&gt;&lt;figcaption&gt;节点库与工作流编排&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;运行前校验，运行后复盘&lt;a href=&quot;#运行前校验运行后复盘&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一套可以进入日常生产的工作流，不能只在“生成”时出现。画布会在执行前检查必要输入与节点连接，让缺少产品图、提示词或生成配置的问题尽量提前暴露。&lt;/p&gt;&lt;p&gt;执行过程中的状态、日志和结果也会留在项目内。这样一来，某张主图为什么有效、使用了什么组合、下一次该从哪里继续优化，都不需要重新猜测。项目切换、工作流保存和结果归档，最终服务的是一种更稳定的创作节奏。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;从图片走向视频&lt;a href=&quot;#从图片走向视频&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;图片是这套系统的第一站，也是最需要先跑稳的一站。主图生成已经具备了清晰的输入、规则、模型执行和结果回收链路，而这套链路天然可以继续向视频扩展。&lt;/p&gt;&lt;p&gt;未来的视频画布会继续沿用同一套可编排思路：把商品资产、脚本、分镜、镜头参考、关键帧、配音与视频提示词放到同一张工作流中；生成后的片段再进入预览、筛选、拼接与复盘。最终输出不只是单张电商主图，而是一条能够适配不同平台节奏的商品短视频。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;愿景：把素材生产变成可编排的系统&lt;a href=&quot;#愿景把素材生产变成可编排的系统&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;AI 的价值不应该只停留在“更快生成一张图”。更值得建设的是一套能持续积累的生产系统：每次尝试都会留下可复用的节点、规则和路径；每个成功案例都能变成下一次创作的起点。&lt;/p&gt;&lt;p&gt;这张无限画布会先帮助图片主图生产变得更清晰、更可控，随后逐步连接到视频内容。让产品素材从静态图片到动态短片，都能在同一个工作流里被组织、生成、评估和持续优化。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>访客第一次打开博客时，我想先认真说一句欢迎</title><link>http://rainzt.cn/posts/welcome-toast/</link><guid isPermaLink="true">http://rainzt.cn/posts/welcome-toast/</guid><description>记录首次访问欢迎提示的实现：分时问候、所在地获取、会话内只显示一次，以及接口失败时不打扰阅读的回退方案。</description><pubDate>Sun, 19 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;很多个人博客会在访客进入时弹出提示。有的展示浏览器和操作系统，有的显示 IP、天气和访问次数，也有的连续弹出好几条通知。&lt;/p&gt;
&lt;p&gt;我喜欢“有人来时说一句欢迎”这个想法，却不希望它变成开门后的第一道障碍。于是这次做的欢迎提示很轻：只在同一浏览会话第一次打开网站时出现，从右下角进入，停留几秒后自己离开。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;首次访问时出现的欢迎提示&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;138&quot; src=&quot;/_astro/effect.DiLZ_-0r_ZSvMDm.webp&quot; srcset=&quot;/_astro/effect.DiLZ_-0r_ZSvMDm.webp 596w&quot; /&gt;&lt;figcaption&gt;首次访问时出现的欢迎提示&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;欢迎应该出现一次，而不是每页一次&lt;a href=&quot;#欢迎应该出现一次而不是每页一次&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Firefly 在站内跳转时会替换页面内容。如果每次切换路由都重新执行欢迎逻辑，访客从首页打开文章、再去工具页，会连续看到同一条提示。&lt;/p&gt;&lt;p&gt;我使用 &lt;code&gt;sessionStorage&lt;/code&gt; 保存一个会话标记：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;rain_welcome_seen_v1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;sessionStorage&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getItem&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;sessionStorage&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;setItem&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;只要当前标签页没有关闭，它就不会再次出现。重新打开一个新会话时，欢迎提示才会回来。&lt;/p&gt;&lt;p&gt;我没有使用长期的 &lt;code&gt;localStorage&lt;/code&gt;，因为那会让看过一次的访客以后永远见不到；也没有使用 Cookie，因为这个状态只和页面体验有关，不需要发给服务器。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;先显示，再尝试获取所在地&lt;a href=&quot;#先显示再尝试获取所在地&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;提示出现时，会先根据本地时间生成一句问候：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;早晨显示“早上好”；&lt;/li&gt;
&lt;li&gt;下午显示“下午好”；&lt;/li&gt;
&lt;li&gt;深夜则把语气放轻。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;随后才在后台请求所在地。如果接口顺利返回，文案会补充为“来自某地的朋友，很高兴遇见你”；如果请求超时、被浏览器拦截或接口暂时不可用，就保留普通欢迎语。&lt;/p&gt;&lt;p&gt;这一步的顺序很重要。第三方接口不应该决定组件能不能出现，更不能拖慢博客正文。请求设置了较短超时，并且使用 &lt;code&gt;AbortController&lt;/code&gt; 主动终止。定位只是锦上添花，失败不是页面错误。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;动画要让人看见，也要允许离开&lt;a href=&quot;#动画要让人看见也要允许离开&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;弹窗使用位移、缩放和透明度完成进入动画，时间控制得比较短。右上角保留关闭按钮，不要求访客一定等自动计时结束。&lt;/p&gt;&lt;p&gt;在手机上，它会避开底部悬浮导航；如果系统开启“减少动态效果”，位移动画会被取消，只做轻微淡入淡出。&lt;/p&gt;&lt;p&gt;这些处理看起来比“显示一段文字”复杂，但它们共同决定了提示会不会打扰人：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;不遮挡主要按钮；&lt;/li&gt;
&lt;li&gt;不抢占页面焦点；&lt;/li&gt;
&lt;li&gt;可以手动关闭；&lt;/li&gt;
&lt;li&gt;几秒后自动消失；&lt;/li&gt;
&lt;li&gt;同一会话不重复；&lt;/li&gt;
&lt;li&gt;接口失败不报错。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;建立欢迎提示组件&lt;a href=&quot;#建立欢迎提示组件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;组件放在：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/components/features/WelcomeToast.astro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;它不是侧栏内容，而是覆盖在页面右下角，所以直接写成全局功能组件：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;welcome-toast&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;welcome-toast&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;aria-live&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;polite&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;aria-atomic&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;data-state&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;idle&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;welcome-toast-icon&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;✦&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;welcome-toast-kicker&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;WELCOME&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;strong&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;welcome-toast-title&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;欢迎来到朝朝听雨&amp;lt;/&lt;/span&gt;&lt;span&gt;strong&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;welcome-toast-message&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;很高兴在这里遇见你。&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;welcome-toast-close&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aria-label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;关闭欢迎提示&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;×&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;welcome-toast&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;aria-live=&quot;polite&quot;&lt;/code&gt; 会让辅助技术在合适的时机读出提示，又不会强行打断正在阅读的内容。&lt;/p&gt;&lt;p&gt;显示与关闭只依赖 &lt;code&gt;data-state&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.welcome-toast&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pointer-events: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform: &lt;/span&gt;&lt;span&gt;translateY&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;scale&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.96&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;320&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform &lt;/span&gt;&lt;span&gt;&lt;span&gt;420&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cubic-bezier&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.22&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.36&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.welcome-toast&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-state&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;visible&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pointer-events: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform: &lt;/span&gt;&lt;span&gt;translateY&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;scale&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;在窄屏下要给底部导航留出空间：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (max-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;640&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.welcome-toast&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;right: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bottom: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; env(safe-area-inset-bottom));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;left: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;会话判断与定位回退&lt;a href=&quot;#会话判断与定位回退&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;初始化时先读 &lt;code&gt;sessionStorage&lt;/code&gt;，没有标记才延迟显示：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;sessionStorage&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getItem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;rain_welcome_seen_v1&quot;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;.welcome-toast-close&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;click&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;close&lt;/span&gt;&lt;span&gt;(), { &lt;/span&gt;&lt;span&gt;once&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;showTimer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;show&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;850&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;show()&lt;/code&gt; 中先写入会话标记并显示普通欢迎语，再发起定位请求：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;sessionStorage&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;setItem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;rain_welcome_seen_v1&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dataset&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;visible&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;https://v2.xxapi.cn/api/ip&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;signal&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;abortController&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;signal&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;接口返回结构可能变化，因此不要直接假设地址一定在某个字段。我的处理会依次尝试 &lt;code&gt;data.address&lt;/code&gt;、&lt;code&gt;data.location&lt;/code&gt;、&lt;code&gt;data.city&lt;/code&gt; 等候选值。任何异常都会进入 &lt;code&gt;catch&lt;/code&gt;，保留“很高兴在这里遇见你”的默认文本。&lt;/p&gt;&lt;p&gt;为了防止请求长时间占用，设置 3.5 秒超时：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;abortController&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbortController&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;timeout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;abortController&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;abort&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;3500&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;关闭组件或离开页面时，也要终止请求并清理计时器。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;挂载到全局布局&lt;a href=&quot;#挂载到全局布局&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在 &lt;code&gt;src/layouts/Layout.astro&lt;/code&gt; 顶部导入：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WelcomeToast&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@components/features/WelcomeToast.astro&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;然后在 &lt;code&gt;body&lt;/code&gt; 末尾、其他全局效果附近挂载：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;SakuraEffect&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;WelcomeToast&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;FancyboxManager&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;测试“只出现一次”时，普通刷新不会再次显示，因为当前标签页的 &lt;code&gt;sessionStorage&lt;/code&gt; 仍然存在。可以新开一个标签页，或者在开发者工具的 Application 面板中删除 &lt;code&gt;rain_welcome_seen_v1&lt;/code&gt; 后再刷新。&lt;/p&gt;&lt;p&gt;还需要主动测试断网或接口失败的情况。一个合格的欢迎提示应该在没有定位数据时照常出现和关闭，而不是在控制台抛出未处理异常。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;个性化不等于收集更多信息&lt;a href=&quot;#个性化不等于收集更多信息&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;位置欢迎很容易继续扩展：显示 IP、网络运营商、浏览器、系统版本，甚至根据天气改变文案。&lt;/p&gt;&lt;p&gt;我没有继续加。访客来到博客，是为了看内容，不是为了被告知网站识别出了多少信息。现在只使用接口返回的概略地区来组织一句话，获取不到就立刻退回普通欢迎，并且不保存这些数据。&lt;/p&gt;&lt;p&gt;这也符合我对第二代博客的想法：可以有技术细节，也可以有一点个人气息，但不需要把每一种能力都展示出来。&lt;/p&gt;&lt;p&gt;欢迎提示最终只停留几秒。它不会成为网站里最重要的功能，却能在第一次见面时替我说一句：你好，欢迎来到朝朝听雨。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>把一年、一个月和一周放进三条进度条</title><link>http://rainzt.cn/posts/schedule-progress/</link><guid isPermaLink="true">http://rainzt.cn/posts/schedule-progress/</guid><description>给博客侧边栏加入时间进度与下一节点倒计时，并记录日期计算、闰年处理和无刷新切页中的计时器清理。</description><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;做定制项目的时候，时间通常以交付日期存在：还有几天要上线，今天必须完成哪一部分，下一轮反馈什么时候回来。&lt;/p&gt;
&lt;p&gt;项目告一段落以后，我想在博客里放一种没那么紧张的时间提醒。它不催我完成任务，只把一年、一个月和一周走到哪里，用三条进度条安静地显示出来。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;时间进度与倒计时组件&quot; loading=&quot;lazy&quot; width=&quot;453&quot; height=&quot;521&quot; src=&quot;/_astro/effect.CD_m62wy_2aUARH.webp&quot; srcset=&quot;/_astro/effect.CD_m62wy_2aUARH.webp 453w&quot; /&gt;&lt;figcaption&gt;时间进度与倒计时组件&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;三条进度，代表三种尺度&lt;a href=&quot;#三条进度代表三种尺度&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;组件放在右侧栏，依次显示：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;本年已经过去的比例，以及还剩多少天；&lt;/li&gt;
&lt;li&gt;本月已经过去的比例，以及还剩多少天；&lt;/li&gt;
&lt;li&gt;本周已经走到哪一天，以及周末前还剩多少天。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;下方再单独留一块区域，显示距离下一个节日还有多久。最初这里只保留元旦、劳动节和国庆节，结果到了 2026 年 7 月，组件直接把“下一站”指向了国庆节，漏掉了 9 月 25 日的中秋节。&lt;/p&gt;&lt;p&gt;现在的节日表分成三类：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;国内节日与纪念日，例如春节、清明、劳动节、国庆节；&lt;/li&gt;
&lt;li&gt;农历传统节日，例如元宵、端午、七夕、中秋、重阳和除夕；&lt;/li&gt;
&lt;li&gt;常见国际节日，例如情人节、复活节、母亲节、父亲节、万圣夜、感恩节和圣诞节。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这样做以后，“全面”不再等于堆一串当年日期。公历固定节日、按星期计算的节日和农历节日分别使用不同算法，第二年仍然能够继续工作。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;日期计算最怕看起来差不多&lt;a href=&quot;#日期计算最怕看起来差不多&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;本月进度最简单：获取今天是几号，再除以当月总天数。&lt;/p&gt;&lt;p&gt;本年进度需要考虑闰年：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysInYear&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;400&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;366&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;365&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;周进度则要处理 JavaScript 中星期日等于 &lt;code&gt;0&lt;/code&gt; 的情况。我把星期一到星期日统一映射成 &lt;code&gt;1&lt;/code&gt; 到 &lt;code&gt;7&lt;/code&gt;，这样进度条和“还剩几天”的文案更直观。&lt;/p&gt;&lt;p&gt;另一个容易忽略的问题是夏令时。直接用两个本地时间相减，在采用夏令时的地区可能遇到一天不是完整 24 小时的情况。计算天数时，我先把年月日转换到 UTC 的零点，再用毫秒差换算，避免时区偏移让倒计时多一天或少一天。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;不需要每秒重新计算&lt;a href=&quot;#不需要每秒重新计算&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这块组件展示的最小单位是“天”，没有必要像时钟一样每秒甚至每分钟更新。&lt;/p&gt;&lt;p&gt;页面加载时计算一次，然后只安排一个定时任务，等到下一个本地零点重新刷新。组件被移除时，定时器也会清理：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scheduleMidnightUpdate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;disconnectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;clearTimeout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;midnightTimer&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这种写法对 Firefly 的无刷新页面切换更友好。它不会因为在首页和文章页之间来回跳转，就留下越来越多的后台任务。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;组件结构与进度条样式&lt;a href=&quot;#组件结构与进度条样式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;组件文件为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/components/widget/ScheduleProgress.astro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;每一行由百分比、说明文字和进度轨道组成：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;progress-row&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-kind&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;year&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;progress-percent&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;--%&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;progress-detail&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;progress-copy&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;本年还剩 -- 天&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;progress-track&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;我没有直接使用原生 &lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt;，而是用普通元素控制宽度。这样渐变、发光和过渡动画更容易统一：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.progress-track&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.42&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;overflow: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;&lt;span&gt;999&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--btn-regular-bg&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.progress-track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background: &lt;/span&gt;&lt;span&gt;linear-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;90&lt;/span&gt;&lt;span&gt;deg&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;8de6d1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--primary&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition: width &lt;/span&gt;&lt;span&gt;&lt;span&gt;650&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cubic-bezier&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.22&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.36&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;脚本更新时只需要修改文字与 &lt;code&gt;width&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;setProgress&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;percent&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;`.progress-row[data-kind=&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;]`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;.progress-percent&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;textContent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;percent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toFixed&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;%`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;.progress-copy&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;textContent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;.progress-track i&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;percent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;%`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;计算年、月、周进度&lt;a href=&quot;#计算年月周进度&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;核心更新函数可以按下面的顺序组织：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getFullYear&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getMonth&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;today&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getDate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysInMonth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getDate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;weekday&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getDay&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startOfYear&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startOfToday&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;today&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dayOfYear&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;startOfToday&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getTime&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startOfYear&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getTime&lt;/span&gt;&lt;span&gt;()) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;86400000&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;再分别传给 &lt;code&gt;setProgress&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setProgress&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;month&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;today&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysInMonth&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;`本月还剩 &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;daysInMonth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;today&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; 天`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setProgress&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;week&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;weekday&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;`本周还剩 &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;weekday&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; 天`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;三类节日要用三种算法&lt;a href=&quot;#三类节日要用三种算法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;固定公历日期最直接，月份仍然从 &lt;code&gt;0&lt;/code&gt; 开始：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fixedFestivals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;元旦&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;day&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;国内&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;情人节&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;day&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;14&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;国际&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;国庆节&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;day&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;国内&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;圣诞节&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;11&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;day&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;25&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;国际&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;母亲节、父亲节和感恩节不是固定几号，而是“某月第几个星期几”。我写了一个通用函数：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nthWeekdayOfMonth&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;weekday&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nth&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offset&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;weekday&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getDay&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offset&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;nth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mothersDay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nthWeekdayOfMonth&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;thanksgiving&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nthWeekdayOfMonth&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;上面的 &lt;code&gt;0&lt;/code&gt; 代表星期日，&lt;code&gt;4&lt;/code&gt; 代表星期四。复活节的规则更特殊，组件使用 Gregorian computus 算法单独计算。&lt;/p&gt;&lt;p&gt;农历节日没有硬编码成 2026 年的公历日期，而是借助浏览器自带的中文农历日历：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lunarFormatter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Intl&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;DateTimeFormat&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;zh-CN-u-ca-chinese&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;numeric&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;day&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;numeric&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLunarMonthDay&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;parts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lunarFormatter&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;formatToParts&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;month&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Number&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;parts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;part&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;part&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;month&quot;&lt;/span&gt;&lt;span&gt;)?.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;day&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Number&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;parts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;part&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;part&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;day&quot;&lt;/span&gt;&lt;span&gt;)?.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;脚本从今天开始向后扫描 400 天，将每天转换成农历月日，再和节日表匹配。这样中秋始终匹配农历八月十五，端午始终匹配五月初五，不需要每年手动改日期。除夕不能写死为腊月三十，因为有些年份腊月只有二十九天；我的判断方式是检查“明天是否为正月初一”，如果是，今天就是除夕。&lt;/p&gt;&lt;p&gt;最后把三类候选日期合并、去重并排序，取第一个不早于今天的日期：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;&lt;span&gt;unique&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;values&lt;/span&gt;&lt;span&gt;()]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;festival&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;festival&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startOfToday&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getTime&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getTime&lt;/span&gt;&lt;span&gt;())[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;2026 年 7 月 24 日之后，完整列表会先显示 8 月 19 日的七夕节，随后是 9 月 25 日的中秋节，不会再直接跳到国庆节。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;注册并放到右侧最下面&lt;a href=&quot;#注册并放到右侧最下面&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;与其他侧栏组件一样，需要完成三步：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;在 &lt;code&gt;WidgetComponentType&lt;/code&gt; 中加入 &lt;code&gt;&quot;scheduleProgress&quot;&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;SideBar.astro&lt;/code&gt; 导入组件并写入 &lt;code&gt;componentMap&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;rightComponents&lt;/code&gt; 最后加入配置。&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;scheduleProgress&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enable&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;sticky&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showOnPostPage&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;它被放在右侧配置最后，因此会出现在日历与其他启用组件之后。&lt;code&gt;position: &quot;sticky&quot;&lt;/code&gt; 让它属于跟随滚动区域；如果改成 &lt;code&gt;top&lt;/code&gt;，它会和时钟、统计等固定顶部组件排在一起。&lt;/p&gt;&lt;p&gt;测试时重点看月末、年末、星期日和闰年二月。日期组件最容易在边界条件出错，普通工作日显示正确并不能说明算法已经完整。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;进度条不是为了制造焦虑&lt;a href=&quot;#进度条不是为了制造焦虑&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;把时间做成百分比，很容易让人产生“今年怎么已经过去这么多”的紧迫感。所以我没有使用警告色，也没有加入“剩余生命”一类过重的文案。&lt;/p&gt;&lt;p&gt;三条进度都沿用博客主题色，数字保持小尺寸，下方倒计时只写“距离下一站”。它更像一个路标，而不是考核表。&lt;/p&gt;&lt;p&gt;有时我会觉得时间很快，电脑里的项目文件夹一批接一批，回过头才发现一个月已经接近尾声。把这些尺度放在侧边栏，至少能让我在打开博客时短暂地意识到：今天属于这一周，也属于这个月和这一年。&lt;/p&gt;&lt;p&gt;工具提醒我把工作做完，进度条则提醒我，时间本身也值得被看见。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>每天留一句话：我在侧边栏做了一个本地每日一言</title><link>http://rainzt.cn/posts/quote-of-the-day/</link><guid isPermaLink="true">http://rainzt.cn/posts/quote-of-the-day/</guid><description>不调用外部语录接口，也不需要数据库，用日期从十条本地名句中选出当天固定的一句。</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;重新开始写博客以后，我越来越在意“文字是不是自己的”。&lt;/p&gt;
&lt;p&gt;以前赶项目时，我习惯把资料交给 AI，让它快速整理出完整文章。效率很高，可当博客真正准备长期更新时，我又希望页面里能多留一点与文字有关的气息。每日一言正好给了我一个很小的位置：它不需要展开成一篇文章，也不负责讲清某项技术，只是在访客打开首页时，留下一句值得慢慢读的话。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;每日一言侧边栏效果&quot; loading=&quot;lazy&quot; width=&quot;446&quot; height=&quot;231&quot; src=&quot;/_astro/effect.Bm_SEfLm_Z1kw4jA.webp&quot; srcset=&quot;/_astro/effect.Bm_SEfLm_Z1kw4jA.webp 446w&quot; /&gt;&lt;figcaption&gt;每日一言侧边栏效果&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;为什么没有直接接语录 API&lt;a href=&quot;#为什么没有直接接语录-api&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;网上有很多现成的一言接口，请求一次就能得到一句随机内容。接入确实方便，但我最终没有用。&lt;/p&gt;&lt;p&gt;原因有三个。&lt;/p&gt;&lt;p&gt;第一，第三方接口的速度和稳定性不受我控制。侧边栏本来是静态页面的一部分，不值得为了十几个字增加一次网络依赖。&lt;/p&gt;&lt;p&gt;第二，接口返回什么内容无法预先检查，偶尔可能出现过长、重复或不符合博客气质的句子。自己维护内容，排版和语气都更可控。&lt;/p&gt;&lt;p&gt;第三，也是最重要的一点：我想让它逐渐变成自己筛选过的句子库，而不是另一个站点内容的转发器。&lt;/p&gt;&lt;p&gt;所以组件内放的是十条经过核对的经典名句，例如：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;千里之行，始于足下。&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;学而不思则罔，思而不学则殆。&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;纸上得来终觉浅，绝知此事要躬行。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;十条内容来自《道德经》《论语》《荀子》以及陆游、屈原的诗文。它们都保存在本地数组中，页面不需要为了十几个字再等待一次网络请求。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;今天固定，明天再换&lt;a href=&quot;#今天固定明天再换&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;“今日一言”不应该像轮播图一样十几秒换一次。我的规则很简单：语录库里共有十条，同一天无论刷新多少次都显示同一句，日期变化后再显示下一句。&lt;/p&gt;&lt;p&gt;实现时先把访客本地日期换算成一个稳定的天数，再对语录总数取余：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dayNumber&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;UTC&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getFullYear&lt;/span&gt;&lt;span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getMonth&lt;/span&gt;&lt;span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getDate&lt;/span&gt;&lt;span&gt;()) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;86_400_000&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quoteIndex&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dayNumber&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quotes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这里使用年、月、日，而不是当前的小时和分钟，所以刷新页面不会改变结果。天数每天只增加一，索引也会依次向后移动；十条全部出现一遍后，再从第一条重新开始。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;让它看起来像一句旁白&lt;a href=&quot;#让它看起来像一句旁白&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;视觉上我没有给它加复杂背景，只沿用侧边栏卡片：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;标题是“今日一言”；&lt;/li&gt;
&lt;li&gt;正文使用略微倾斜的字形；&lt;/li&gt;
&lt;li&gt;两侧引号用主题色降低透明度；&lt;/li&gt;
&lt;li&gt;作者放在右下角；&lt;/li&gt;
&lt;li&gt;鼠标经过时，正文才轻轻变成主题色。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;它的存在感比时间卡片低，也比统计组件更松。页面滚动到这里时，像在一组数字和工具之间短暂停了一下。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;把每日一言做成可注册的侧栏组件&lt;a href=&quot;#把每日一言做成可注册的侧栏组件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;组件文件放在：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/components/widget/QuoteOfTheDay.astro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;外层继续使用主题自带的 &lt;code&gt;WidgetLayout&lt;/code&gt;，这样圆角、标题、内边距和暗色模式不用重新造一套：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WidgetLayout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/components/common/WidgetLayout.astro&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;WidgetLayout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;quote-of-the-day&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;今日一言&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;daily-quote&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;blockquote&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;quote-mark&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;“&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;quote-text&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;慢一点，也是在向前走。&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;quote-mark&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;”&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;blockquote&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;cite&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;quote-author&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;—— Rain&amp;lt;/&lt;/span&gt;&lt;span&gt;cite&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;daily-quote&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;WidgetLayout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;文字数组和日期选择逻辑放在组件脚本中。每一项都保存正文与署名，后续也可以把署名换成书名、项目名或记录日期：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quotes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;千里之行，始于足下。&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;《道德经》&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;学而不思则罔，思而不学则殆。&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;《论语》&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;纸上得来终觉浅，绝知此事要躬行。&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;陆游&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DailyQuote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HTMLElement&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;midnightTimer&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;showTodayQuote&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scheduleMidnightRefresh&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;disconnectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;midnightTimer&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;clearTimeout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;midnightTimer&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showTodayQuote&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dayNumber&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;UTC&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getFullYear&lt;/span&gt;&lt;span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getMonth&lt;/span&gt;&lt;span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getDate&lt;/span&gt;&lt;span&gt;()) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;86_400_000&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dayNumber&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quotes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;author&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quotes&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;.quote-text&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;textContent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;.quote-author&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;textContent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`—— &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;author&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scheduleMidnightRefresh&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextMidnight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getFullYear&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getMonth&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getDate&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;midnightTimer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;showTodayQuote&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scheduleMidnightRefresh&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;nextMidnight&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getTime&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getTime&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;customElements&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;define&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;daily-quote&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;DailyQuote&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;如果项目启用了无刷新切页，注册前需要先判断元素是否已经存在，避免重复定义报错：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;customElements&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;daily-quote&quot;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;customElements&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;define&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;daily-quote&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;DailyQuote&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;注册到左侧栏&lt;a href=&quot;#注册到左侧栏&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;先在 &lt;code&gt;src/types/sidebarConfig.ts&lt;/code&gt; 中加入类型：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WidgetComponentType&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;profile&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;announcement&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;quoteOfTheDay&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;再在 &lt;code&gt;SideBar.astro&lt;/code&gt; 中导入并注册映射：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;QuoteOfTheDay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/components/widget/QuoteOfTheDay.astro&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;componentMap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;quoteOfTheDay&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;QuoteOfTheDay&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;我把它放在左侧资料与公告之后、音乐播放器之前：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;quoteOfTheDay&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enable&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;top&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showOnPostPage&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这里的顺序就是实际渲染顺序。想把它移到右边，不需要改组件，只要把这段配置从 &lt;code&gt;leftComponents&lt;/code&gt; 移到 &lt;code&gt;rightComponents&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;修改内容时要注意什么&lt;a href=&quot;#修改内容时要注意什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果句子里包含英文引号、反斜杠或换行，记得按照 TypeScript 字符串规则转义。句子过长时，侧栏会明显变高，因此我会尽量控制在一到两行。&lt;/p&gt;&lt;p&gt;日期索引会严格按顺序走完十条。想确认逻辑是否正确，可以在浏览器控制台中临时把日期改成连续几天，检查索引是否依次变化；同时反复刷新当天页面，确认正文始终一致。&lt;/p&gt;&lt;p&gt;我还额外设置了一个指向本地午夜的定时器。这样即使页面从晚上一直开到第二天，它也会在零点后主动更新，而不必依赖访客手动刷新。组件被移除时要清理这个定时器，避免无刷新切页后留下多余任务。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;文字库会跟着博客一起长大&lt;a href=&quot;#文字库会跟着博客一起长大&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这个组件现在保留十句话，不算很多，但每条都经过选择。&lt;/p&gt;&lt;p&gt;有些内容适合写成长文，有些只值得记成一行。以后做完新的自动化项目、踩过新的坑，或者某天只是突然想明白一件小事，我都可以把它补进去。几年之后再看，这个数组也许会变成一份非常简短的时间线。&lt;/p&gt;&lt;p&gt;每日一言真正吸引我的地方，不只是它会自动变化，而是它给零散文字留了一个固定入口。博客不只有完整、正式、可以被搜索到的文章，也可以有这些短暂停留，却仍然值得被记住的句子。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>连鼠标指针也换掉：给第二代博客补上一点自己的细节</title><link>http://rainzt.cn/posts/custom-cursor/</link><guid isPermaLink="true">http://rainzt.cn/posts/custom-cursor/</guid><description>一次很小的博客改造：用两枚本地 cur 文件区分默认与可点击状态，同时照顾输入框、触屏设备和加载失败时的回退。</description><pubDate>Sun, 12 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;做网站时，我通常会先处理最显眼的部分：横幅、文章卡片、侧边栏、配色和字体。鼠标指针几乎总被留到最后，因为它不影响内容，也不会决定页面能不能使用。&lt;/p&gt;
&lt;p&gt;可博客真正打开以后，鼠标又是桌面端最频繁出现的元素。它一直在页面上移动，经过导航、文章链接、播放器按钮和工具入口。默认箭头没有问题，只是和我已经重新整理过的第二代博客相比，显得过于“系统自带”。&lt;/p&gt;
&lt;p&gt;于是这次我给它换了两枚本地光标。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;自定义光标在页面中的效果&quot; loading=&quot;lazy&quot; width=&quot;197&quot; height=&quot;62&quot; src=&quot;/_astro/effect.C6YbBtXu_Z1ofjM3.webp&quot; srcset=&quot;/_astro/effect.C6YbBtXu_Z1ofjM3.webp 197w&quot; /&gt;&lt;figcaption&gt;自定义光标在页面中的效果&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;只做两种状态就够了&lt;a href=&quot;#只做两种状态就够了&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我没有做跟随鼠标的粒子、拖尾或点击烟花。博客页面已经有壁纸轮播、水波纹和樱花效果，再叠一层持续运行的鼠标动画，只会增加干扰。&lt;/p&gt;&lt;p&gt;最终只保留两种状态：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;普通浏览时使用默认光标；&lt;/li&gt;
&lt;li&gt;遇到链接、按钮和可点击控件时切换为指针光标。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;两枚 &lt;code&gt;.cur&lt;/code&gt; 文件放在 &lt;code&gt;public/mouse/&lt;/code&gt;，CSS 用变量统一管理：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--cursor-default&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/mouse/default.cur&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--cursor-pointer&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/mouse/pointer.cur&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;pointer&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这里的第二个值不是多余的。如果自定义文件加载失败，浏览器仍然能回退到系统的 &lt;code&gt;default&lt;/code&gt; 或 &lt;code&gt;pointer&lt;/code&gt;，页面不会因此失去正确的交互提示。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;默认与可点击状态使用不同的光标文件&quot; loading=&quot;lazy&quot; width=&quot;117&quot; height=&quot;82&quot; src=&quot;/_astro/detail.Cia-sYP0_leujL.webp&quot; srcset=&quot;/_astro/detail.Cia-sYP0_leujL.webp 117w&quot; /&gt;&lt;figcaption&gt;默认与可点击状态使用不同的光标文件&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;可点击，不等于所有元素都用手型&lt;a href=&quot;#可点击不等于所有元素都用手型&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最开始很容易写成：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cursor: &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/mouse/default.cur&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这样虽然省事，却会把输入框和文本区域也覆盖掉。用户准备选中文字或输入内容时，光标仍然是一枚普通箭头，反而削弱了浏览器原本清楚的提示。&lt;/p&gt;&lt;p&gt;所以我只给明确的交互元素使用指针光标，包括链接、按钮、下拉框、&lt;code&gt;role=&quot;button&quot;&lt;/code&gt; 和站内已有的 &lt;code&gt;.cursor-pointer&lt;/code&gt;。输入框、文本域与可编辑区域继续使用系统文本光标。&lt;/p&gt;&lt;p&gt;这类细节很小，但它决定了自定义样式是在增强界面，还是只是在覆盖默认行为。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;只在真正有鼠标的设备上启用&lt;a href=&quot;#只在真正有鼠标的设备上启用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;手机和平板没有悬停指针，没必要加载或应用桌面光标样式。我把规则放进了媒体查询：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt;&lt;span&gt; (hover: hover) &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; (pointer: fine) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/* cursor rules */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;只有同时支持悬停、并且具有精确指针的设备才会启用。触屏端保持原样，也不会为了一个用不到的效果增加额外逻辑。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;完整接入步骤&lt;a href=&quot;#完整接入步骤&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;首先准备两枚 &lt;code&gt;.cur&lt;/code&gt; 文件，并按下面的结构放进 &lt;code&gt;public&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└─ mouse/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ default.cur&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ pointer.cur&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;public&lt;/code&gt; 中的文件会原样复制到构建结果，因此页面访问路径分别是 &lt;code&gt;/mouse/default.cur&lt;/code&gt; 和 &lt;code&gt;/mouse/pointer.cur&lt;/code&gt;。如果文件名使用中文或空格，虽然也可能访问成功，但更容易在 CSS 转义和服务器迁移时出问题。&lt;/p&gt;&lt;p&gt;接着新建 &lt;code&gt;src/styles/mouse.css&lt;/code&gt;。下面是站内实际使用的完整规则：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt;&lt;span&gt; (hover: hover) &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; (pointer: fine) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--cursor-default&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/mouse/default.cur&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--cursor-pointer&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/mouse/pointer.cur&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;pointer&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cursor: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--cursor-default&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;link&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.cursor-pointer&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;submit&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;reset&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cursor: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--cursor-pointer&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;contenteditable&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cursor: &lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;最后在全局布局 &lt;code&gt;src/layouts/Layout.astro&lt;/code&gt; 的 frontmatter 中导入：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/styles/main.css&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/styles/variables.styl&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/styles/markdown-extend.styl&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/styles/mouse.css&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这一步必须放在全局布局，而不是某个单独页面。否则首页可能已经换了光标，进入文章页后又恢复系统样式。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;怎样确认文件真的生效&lt;a href=&quot;#怎样确认文件真的生效&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;构建成功只代表 CSS 语法没错，不代表 &lt;code&gt;.cur&lt;/code&gt; 一定可以读取。可以直接在浏览器打开：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;http://127.0.0.1:4321/mouse/default.cur&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;http://127.0.0.1:4321/mouse/pointer.cur&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;再打开开发者工具，检查 &lt;code&gt;body&lt;/code&gt; 的计算样式中是否出现自定义 URL。鼠标移到链接上时，计算结果应切换到 &lt;code&gt;pointer.cur&lt;/code&gt;；移到正文段落时回到 &lt;code&gt;default.cur&lt;/code&gt;；进入输入框时仍然是文本光标。&lt;/p&gt;&lt;p&gt;如果浏览器完全没有变化，优先排查：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;文件实际下载成了 HTML 错误页，而不是 CUR 二进制文件；&lt;/li&gt;
&lt;li&gt;CSS 写的是相对路径，进入二级路由后找不到文件；&lt;/li&gt;
&lt;li&gt;光标尺寸或格式不被当前浏览器支持；&lt;/li&gt;
&lt;li&gt;设备被媒体查询识别为触屏设备。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;我最终使用绝对路径，并给每条规则都保留系统回退值。这样就算某个浏览器不支持自定义文件，交互语义也不会丢失。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;越小的改动，越应该克制&lt;a href=&quot;#越小的改动越应该克制&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;自定义光标的代码量很少，真正需要判断的是它应该影响哪些地方。&lt;/p&gt;&lt;p&gt;如果把它做得太抢眼，鼠标每移动一次都在提醒访客“这里换过皮肤”，阅读就会被打断。现在这版更像一个不主动解释的彩蛋：不仔细看可能不会立刻发现，可一旦注意到，会知道这个网站连最小的交互状态也经过了选择。&lt;/p&gt;&lt;p&gt;第二代博客对我来说不是一个急着堆满功能的展示站。它可以有个性，但个性不一定都要大声出现。有些只需要跟着鼠标，安静地待在每一次点击之前。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>让博客在不同时间说不同的话：侧边栏实时时钟改造记录</title><link>http://rainzt.cn/posts/time-greeting/</link><guid isPermaLink="true">http://rainzt.cn/posts/time-greeting/</guid><description>记录我给第二代博客加入分时问候实时时钟的过程，以及从暗色整图卡片重新调整为白色信息区与鸣潮随机配图的几次取舍。</description><pubDate>Sat, 11 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近重新打磨第二代博客时，我开始留意一些并不影响功能，却会影响“这个网站像不像自己”的小地方。&lt;/p&gt;
&lt;p&gt;侧边栏原本放着站点统计、日历和音乐播放器，它们各自都很实用，但少了一点会随时间发生变化的东西。于是我加了一块实时时钟：早晨说早上好，中午提醒吃饭和休息，到了晚上则让页面的语气慢下来。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;时段问候组件的版式参考&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;402&quot; src=&quot;/_astro/effect.DOYj7sob_1vewwg.webp&quot; srcset=&quot;/_astro/effect.DOYj7sob_1vewwg.webp 448w&quot; /&gt;&lt;figcaption&gt;时段问候组件的版式参考&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;这项改动看上去只是多了几行时间和一句问候，真正做起来却经历了一次很明显的返工。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;第一版，做得太像一张海报&lt;a href=&quot;#第一版做得太像一张海报&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最初我把整张卡片都铺成鸣潮壁纸，在图片上叠暗色渐变，再放一个很大的时间。单独看并不难看，甚至有一点游戏界面的感觉。&lt;/p&gt;&lt;p&gt;可它放进右侧栏以后，问题立刻出现了：信息层级太重，和旁边干净的白色卡片不在同一个体系里。用户第一眼看到的是背景图，而不是时间；卡片像一个缩小的横幅，而不是侧栏部件。&lt;/p&gt;&lt;p&gt;这次偏差也提醒了我，参考一个效果时不能只识别“它有哪些元素”，还要识别这些元素之间的关系。原版真正舒服的地方，是上半部分把问候、时间、星期和日期排得很克制，下半部分才留给图片。它不是在图片上写字，而是把信息和装饰分成了两个区域。&lt;/p&gt;&lt;p&gt;所以第二版我把结构彻底拆开：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;上半部分使用博客卡片原有的浅色背景；&lt;/li&gt;
&lt;li&gt;时间保持最大字号，问候语放在它上方；&lt;/li&gt;
&lt;li&gt;星期和日期缩小后靠在时间右侧；&lt;/li&gt;
&lt;li&gt;右上角只留一个跟随时段变化的小图标；&lt;/li&gt;
&lt;li&gt;底部固定为横向图片区域。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这样放回右侧栏后，它终于像这个博客原本就有的一部分。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;图片必须是我的鸣潮图库&lt;a href=&quot;#图片必须是我的鸣潮图库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;版式稳定后，我又改了两次图片逻辑。&lt;/p&gt;&lt;p&gt;第一次为了加载速度，直接用了壁纸目录里的缩略图。卡片只有 280 像素宽，看起来似乎够用，但缩略图经过压缩后再裁切，人物细节明显发糊。最后还是换回同一批鸣潮壁纸的原图，让浏览器按卡片尺寸裁切显示。&lt;/p&gt;&lt;p&gt;图片池只读取站内已经整理好的鸣潮壁纸：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;wallpaper-01.webp&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;wallpaper-25.webp&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;wallpaper-47.webp&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;每次刷新会重新随机选择一张。这样它不会请求第三方随机图接口，也不会突然出现和站点风格无关的风景照。页面横幅、相册和时钟卡片使用的是同一套视觉素材，整个网站的气质更统一。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;时间更新不能留下重复计时器&lt;a href=&quot;#时间更新不能留下重复计时器&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Firefly 有无刷新页面切换。如果直接在脚本里写一个全局 &lt;code&gt;setInterval&lt;/code&gt;，组件被替换后计时器可能仍然活着，来回切几次页面就会出现多个任务同时更新。&lt;/p&gt;&lt;p&gt;我把它做成了自定义元素，在组件挂载时启动，在移除时清理：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;updateGreeting&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;alignToNextMinute&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;disconnectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;clearInterval&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;minuteTimer&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;clearTimeout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;alignmentTimer&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;第一次更新会对齐到下一分钟的边界，之后每分钟刷新一次。这样时间不会因为页面打开的秒数不同而慢慢偏移，也能适应站内的页面切换。&lt;/p&gt;&lt;p&gt;问候语按六个时段划分：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;深夜：提醒早点休息；&lt;/li&gt;
&lt;li&gt;早晨：迎接新的一天；&lt;/li&gt;
&lt;li&gt;上午：保持活力；&lt;/li&gt;
&lt;li&gt;中午：记得午休；&lt;/li&gt;
&lt;li&gt;下午：继续加油；&lt;/li&gt;
&lt;li&gt;晚上：适当放松。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;从零接入这个组件&lt;a href=&quot;#从零接入这个组件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果要在同一套 Firefly 项目里复现，主要改动四个文件：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/components/widget/TimeGreeting.astro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/components/layout/SideBar.astro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/types/sidebarConfig.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/config/sidebarConfig.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;先建立 &lt;code&gt;TimeGreeting.astro&lt;/code&gt;。组件上半部分负责信息，下半部分负责图片：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;time-greeting-widget&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class:list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;time-greeting-card card-base&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-info&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-message&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;正在获取时间...&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-meta&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-clock&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;--:--&amp;lt;/&lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-date&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-week&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;---&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-day&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;--&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-month&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;/--&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;time-greeting-image&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;time-greeting-widget&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;时段判断独立成函数，后续改文案时不需要碰 DOM 更新逻辑：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getGreetingPeriod&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;hour&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;hour&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;late-night&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;夜深了，早点休息！&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;hour&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;morning&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;早上好，新的一天！&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;hour&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;forenoon&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;上午好，充满活力！&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;hour&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;14&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;noon&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;中午好，记得午休！&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;hour&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;18&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;afternoon&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;下午好，继续加油！&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;evening&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;晚上好，放松一下！&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;鸣潮图片池不必手写二十多个完整路径，可以用编号生成：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;01&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;&lt;span&gt;Array&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;23&lt;/span&gt;&lt;span&gt;&lt;span&gt; }, (&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;25&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;padStart&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;0&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`/assets/images/wallpaper/wallpaper-&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.webp`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;imageUrl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;random&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;先用 &lt;code&gt;new Image()&lt;/code&gt; 预加载，成功后再设置 CSS 变量，可以避免图片下载过程中卡片突然闪白：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Image&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;onload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setProperty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;--time-greeting-image&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`url(&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;imageUrl&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;)`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dataset&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;imageReady&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;imageUrl&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;CSS 中让图片区读取这个变量：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.time-greeting-image&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-image: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--time-greeting-image&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-position: &lt;/span&gt;&lt;span&gt;center&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-size: &lt;/span&gt;&lt;span&gt;cover&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition: opacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;650&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;time-greeting-widget&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-image-ready&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;.time-greeting-image&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;组件完成后，在 &lt;code&gt;SideBar.astro&lt;/code&gt; 导入并加入映射：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TimeGreeting&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/components/widget/TimeGreeting.astro&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;componentMap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 其他组件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;timeGreeting&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TimeGreeting&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;然后在 &lt;code&gt;sidebarConfig.ts&lt;/code&gt; 的右侧栏中启用：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;timeGreeting&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enable&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;top&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showOnPostPage&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;最后别忘了把 &lt;code&gt;&quot;timeGreeting&quot;&lt;/code&gt; 加进 &lt;code&gt;WidgetComponentType&lt;/code&gt; 联合类型，否则 TypeScript 会在构建时提示配置值不合法。&lt;/p&gt;&lt;p&gt;本地执行 &lt;code&gt;npm run build&lt;/code&gt; 后，至少检查三种情况：刷新页面时图片是否变化；停留到下一分钟时数字是否更新；从首页进入文章页后组件是否按配置隐藏。只看静态截图并不能证明定时器与无刷新切页都正常。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;小组件也需要服从整体&lt;a href=&quot;#小组件也需要服从整体&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这次改造最有价值的部分，并不是写出了一个时钟，而是把一版“看起来挺酷”的方案主动推倒。&lt;/p&gt;&lt;p&gt;个人博客很容易不断往里加效果。可每一个效果都在争夺注意力，如果只追求单独截图好看，最后页面反而会越来越吵。现在这块时钟保留了鸣潮图片、分时图标和实时变化，也仍然愿意退回到侧栏应有的位置。&lt;/p&gt;&lt;p&gt;它每天会说不同的话，每次刷新会换一张图，但不会压过文章本身。对我来说，这才是这类小组件最合适的状态。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>博客页脚定制方案</title><link>http://rainzt.cn/posts/footer-config/</link><guid isPermaLink="true">http://rainzt.cn/posts/footer-config/</guid><description>记录我给第二代博客重做页脚的过程：如何安排技术徽章、社交入口、运行时间、备案提示与版权信息，并把它做成一个可以继续维护的 Astro 组件。</description><pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;以前我很少认真看页脚。&lt;/p&gt;
&lt;p&gt;搭建第一代博客时，我把它当成页面最后的一行版权文字：写上年份、站点名称，再放一个 RSS 链接，任务就算结束。后来文章越来越多，导航、侧栏、播放器和各种小组件陆续长出来，我才发现页脚其实是读者离开一篇文章之前，最后一次和站点发生关系的地方。&lt;/p&gt;
&lt;p&gt;它不适合塞满所有信息，却应该把最需要被记住的内容交代清楚：这个站点用了什么、作者还维护哪些入口、网站运行了多久、版权和备案信息应该去哪里确认。于是这次我没有继续往原来的 FooterConfig.html 里堆标签，而是把页脚重新拆成几个有明确职责的区域。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;先确定页脚的内容顺序&lt;a href=&quot;#先确定页脚的内容顺序&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我最后采用了下面这条从“氛围”到“信息”的顺序：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;一条轻量的装饰带，让正文和页脚之间有一个缓冲。&lt;/li&gt;
&lt;li&gt;技术徽章，说明站点由哪些工具组成。&lt;/li&gt;
&lt;li&gt;社交链接，只保留博客、GitHub、Bilibili 三个入口。&lt;/li&gt;
&lt;li&gt;运行时间，把“这个站点还活着”变成一个持续变化的数字。&lt;/li&gt;
&lt;li&gt;备案提示和版权信息，放在页面最底部，方便查找但不抢视线。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;这个顺序有一个很实际的好处：访客先看到站点气质，再看到可继续访问的入口，最后才是法律和版权信息。每一层都很短，手机上也不会变成一堵文字墙。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;Astro 组件应该负责什么&lt;a href=&quot;#astro-组件应该负责什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我把页脚放在 &lt;code&gt;src/components/layout/Footer.astro&lt;/code&gt;，让它只处理三类事情：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;在服务端输出稳定的静态内容，例如徽章、链接和版权年份；&lt;/li&gt;
&lt;li&gt;计算站点运行时间的初始值，避免页面加载时出现空白；&lt;/li&gt;
&lt;li&gt;在浏览器端每秒更新一次运行时间，其余内容不依赖 JavaScript。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这种拆分比把全部 HTML 放到配置文件里更容易维护。以后要调整颜色和间距，只改组件的样式；要换社交链接，只改 &lt;code&gt;socials&lt;/code&gt; 数组；要修改起始日期，则改站点配置，不需要在多个文件里搜索日期字符串。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;用数据数组控制技术徽章&lt;a href=&quot;#用数据数组控制技术徽章&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;徽章不是为了炫技，所以我没有给每个徽章引入一套很重的图标。当前只显示技术名称，并用不同的低饱和色做区分：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;techStack&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Astro&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;tone&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;violet&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Tailwind CSS&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;tone&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;cyan&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Twikoo&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;tone&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;orange&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;EdgeOne&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;tone&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;blue&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Umami&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;tone&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;green&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;渲染时只需要遍历数组：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;stack-row&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;techStack&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;`stack-badge &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tone&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这样做有两个好处。第一，增删技术栈不会复制粘贴一大段 HTML；第二，颜色属于 &lt;code&gt;tone&lt;/code&gt;，内容和视觉样式彼此独立。将来如果我把评论系统换掉，只需要替换一项数据。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;社交链接要设置“允许列表”&lt;a href=&quot;#社交链接要设置允许列表&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;之前的个人资料里还保留了微信和邮箱入口，但这次页脚只放三个链接：博客、GitHub、Bilibili。这里我用了一个明确的数组，而不是把所有 profile links 原样输出：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;socials&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;博客&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;material-symbols:home-outline&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://rainzt.cn/&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GitHub&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;simple-icons:github&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://github.com/Jarvis0227&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Bilibili&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;simple-icons:bilibili&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://space.bilibili.com/473321504&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这相当于给页脚加了一层“允许列表”。个人资料以后可以继续保存私密或备用联系方式，但它们不会因为配置被复用，就意外出现在公开页脚。外链统一加上 &lt;code&gt;target=&quot;_blank&quot;&lt;/code&gt; 和 &lt;code&gt;rel=&quot;noreferrer&quot;&lt;/code&gt;，避免读者离开博客后无法快速回来，也减少不必要的来源信息暴露。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;运行时间不是一张静态图片&lt;a href=&quot;#运行时间不是一张静态图片&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;站点运行时间的起点写在 &lt;code&gt;siteConfig.siteStartDate&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;siteStartDate&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;2026-07-08&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;组件先在构建时计算天、小时、分钟和秒，首屏直接得到完整文字；页面加载后再用一个很小的定时器更新数字：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;seconds&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startAt&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;days&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;seconds&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;86400&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hours&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;seconds&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;86400&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3600&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;minutes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;seconds&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3600&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;secs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;seconds&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这里特意保留了 &lt;code&gt;Math.max(0, ...)&lt;/code&gt;。如果服务器时间比起始日期还早，页面不会显示负数；如果以后修改起始日期，也不会让运行时间脚本直接失效。数字使用等宽数字，秒数跳动时文本宽度不会来回抖动。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;“萌备案”要和真实备案区分开&lt;a href=&quot;#萌备案要和真实备案区分开&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我在页脚里放了两条萌系占位文字：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;萌ICP备2026070801号&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;萌公网安备2026070801号&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;它们只是页面气氛的一部分，不是真实备案号。实际备案完成后，应该把文字和链接替换成工信部、公安备案平台提供的正式信息，不能把占位文字当作真实备案展示。这个区分也写进了我的维护笔记，避免未来换服务器或提交备案时忘记修改。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;CSS 重点是“收住”&lt;a href=&quot;#css-重点是收住&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;页脚最容易出现的问题不是不好看，而是太用力。我的处理方式有三点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;用 &lt;code&gt;max-width&lt;/code&gt; 限制内容宽度，让信息集中在正文附近；&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;gap&lt;/code&gt; 和 &lt;code&gt;flex-wrap&lt;/code&gt; 排列徽章与链接，不写死每一行的长度；&lt;/li&gt;
&lt;li&gt;颜色使用 &lt;code&gt;color-mix&lt;/code&gt; 混合当前文字色，让浅色和深色主题自动保持一致。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;徽章采用圆角胶囊，但边框和背景都非常淡；鼠标悬停时只向上移动一像素，不再叠加发光、缩放或大面积渐变。页脚应该像一本书的版权页，能被看见，但不应该抢走文章结尾的注意力。&lt;/p&gt;&lt;p&gt;响应式部分只做必要的调整：小屏幕缩短两侧装饰线、缩小徽章字号、让所有行自动换行。没有给移动端单独复制一套模板，所以桌面和手机看到的是同一份信息，维护成本也更低。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;如何验证它真的工作&lt;a href=&quot;#如何验证它真的工作&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;完成代码后，我会按下面顺序检查：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;执行 &lt;code&gt;npm.cmd run build&lt;/code&gt;，确认 Astro 内容图片、图标和页面路由都能通过构建。&lt;/li&gt;
&lt;li&gt;打开文章页面滚动到底部，检查页脚是否完整出现。&lt;/li&gt;
&lt;li&gt;等待十秒，确认运行时间的秒数持续变化。&lt;/li&gt;
&lt;li&gt;逐个点击博客、GitHub、Bilibili，确认没有多余的微信或邮箱入口。&lt;/li&gt;
&lt;li&gt;调整浏览器宽度，检查徽章和备案行是否自然换行。&lt;/li&gt;
&lt;li&gt;切换深色模式，检查文字对比度和边框是否仍然清楚。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;实际页面中的完整页脚效果&quot; loading=&quot;lazy&quot; width=&quot;1126&quot; height=&quot;378&quot; src=&quot;/_astro/footer-effect.BGavE1je_lHuMH.webp&quot; srcset=&quot;/_astro/footer-effect.BGavE1je_Z1eXeuF.webp 640w, /_astro/footer-effect.BGavE1je_zJmFH.webp 750w, /_astro/footer-effect.BGavE1je_eEmzD.webp 828w, /_astro/footer-effect.BGavE1je_PYAKj.webp 1080w, /_astro/footer-effect.BGavE1je_lHuMH.webp 1126w&quot; /&gt;&lt;figcaption&gt;实际页面中的完整页脚效果&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;最后这张图不是参考站点的截图，而是这次修改后我自己的页面截图。把它放进文章里，一方面是给以后回顾留下一个版本快照，另一方面也能在页脚样式再次调整后，快速对比哪些细节发生了变化。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;留给下一次改版的空间&lt;a href=&quot;#留给下一次改版的空间&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这次我没有把页脚做成一个“功能展示区”，也没有加入访问量、天气或一长串友情链接。它现在只承担站点身份、联系入口和基础信息三件事。以后如果博客真的需要新增内容，我会优先考虑两种方式：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;把内容加进现有的数据数组，让它继续保持可维护的结构；&lt;/li&gt;
&lt;li&gt;新增一个独立的、可关闭的区域，而不是把所有东西挤进同一行。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;对我来说，第二代博客最重要的变化，就是不再把每个角落都当成产品橱窗。页脚也是如此：它可以有一点可爱、有一点技术感，但最后还是要安静地把一段路送到终点。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从零搭建 Rainzt.cn：我的个人博客上线流程</title><link>http://rainzt.cn/posts/build-rainzt-blog/</link><guid isPermaLink="true">http://rainzt.cn/posts/build-rainzt-blog/</guid><description>记录这个个人博客从本地运行、内容配置、生产构建到宝塔 Nginx 部署和 HTTPS 上线的完整流程。</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;为什么要搭这个博客&lt;a href=&quot;#为什么要搭这个博客&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我想要一个真正属于自己的内容空间。它不只是用来发文章，也用来整理项目、记录折腾过程、放一些自己喜欢的页面和小工具。&lt;/p&gt;&lt;p&gt;最后，我把这套基于 Astro 与 Firefly 深度改造的站点命名为 Aemeath 主题。它保留了静态生成速度快、页面效果完整、文章用 Markdown 管理这些优点，部署后只需要一份 &lt;code&gt;dist&lt;/code&gt; 静态文件就能跑起来。对个人博客来说，这种结构足够轻，也足够自由，同时也终于有了更贴近自己审美的名字。&lt;/p&gt;&lt;p&gt;这个站点最终部署在 &lt;code&gt;rainzt.cn&lt;/code&gt;，服务器侧使用宝塔面板和 Nginx，HTTPS 证书也已经配置好。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;项目准备&lt;a href=&quot;#项目准备&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;开始之前，先确认本地已经准备好 Node.js 和 pnpm。这个博客使用的是 Astro 项目结构，内容、配置和静态资源都放在源码目录中维护，生产环境只需要部署构建后的静态文件。&lt;/p&gt;&lt;p&gt;项目主要结构大概是这样：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/content/posts/     文章目录&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/content/spec/      关于、友链、留言等特殊页面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/config/            站点配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public/                静态资源&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dist/                  构建后的生产文件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;package.json           项目脚本&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;wrangler.jsonc         Cloudflare Workers 静态资源配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;vercel.json            Vercel 输出目录配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;其中最常用的是 &lt;code&gt;src/content/posts/&lt;/code&gt;。以后新文章基本都放在这里。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;本地运行&lt;a href=&quot;#本地运行&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目使用 pnpm 管理依赖。进入项目根目录后，先安装依赖：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;开发环境启动：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;本地开发服务会跑在类似下面的地址：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;http://127.0.0.1:4333/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;开发环境和生产环境有一个重要区别：开发环境会显示草稿文章，方便预览和调试；生产构建会自动过滤 &lt;code&gt;draft: true&lt;/code&gt; 的文章。所以本地看到的文章数量可能比线上多，这是正常现象。&lt;/p&gt;&lt;p&gt;这个逻辑在 &lt;code&gt;src/utils/content-utils.ts&lt;/code&gt; 里：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PROD&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;draft&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;也就是说，生产环境只展示非草稿文章。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;配置站点信息&lt;a href=&quot;#配置站点信息&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;站点的大部分基础信息都在 &lt;code&gt;src/config/siteConfig.ts&lt;/code&gt; 里配置，例如：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;站点标题&lt;/li&gt;
&lt;li&gt;副标题&lt;/li&gt;
&lt;li&gt;站点地址&lt;/li&gt;
&lt;li&gt;导航栏标题和 Logo&lt;/li&gt;
&lt;li&gt;页面开关&lt;/li&gt;
&lt;li&gt;分页数量&lt;/li&gt;
&lt;li&gt;壁纸、字体、主题色等显示配置&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;导航菜单在 &lt;code&gt;src/config/navBarConfig.ts&lt;/code&gt; 里维护。比如首页、文章、工具、更新日志、友链、留言、我的、关于等菜单，都是从这里生成的。&lt;/p&gt;&lt;p&gt;如果某个菜单带有子菜单，它会渲染成下拉框。比如“文章”下面有归档、分类、标签；“我的”下面有相册、追番、番组计划。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;写文章&lt;a href=&quot;#写文章&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;文章使用 Markdown 或 MDX。最简单的一篇文章格式如下：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;文章标题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2026-07-08&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;文章简介&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;/assets/images/wallpaper/wallpaper-20.webp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;博客搭建&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;category&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;博客指南&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;draft&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;这里开始写正文。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;published&lt;/code&gt; 是发布时间。&lt;br /&gt;
&lt;code&gt;draft: false&lt;/code&gt; 表示会在生产环境发布。&lt;br /&gt;
&lt;code&gt;draft: true&lt;/code&gt; 表示只在本地开发时显示。&lt;/p&gt;&lt;p&gt;如果文章需要配图，可以像现在这样创建一个文章目录：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/content/posts/build-rainzt-blog/index.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/content/posts/build-rainzt-blog/cover.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/content/posts/build-rainzt-blog/step-1.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;正文里可以直接引用同目录图片：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;![&lt;/span&gt;&lt;span&gt;部署截图&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;./step-1.png&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;生产构建&lt;a href=&quot;#生产构建&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;确认文章和配置没问题后，执行生产构建：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这个命令不是只跑 Astro，它会连续做几件事：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scripts/generate-icons.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tsx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scripts/generate-lqips.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;astro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tsx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scripts/subset-fonts.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pagefind&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--site&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;分别对应：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;生成项目用到的图标常量&lt;/li&gt;
&lt;li&gt;为图片生成低质量占位图数据&lt;/li&gt;
&lt;li&gt;生成 Astro 静态站点&lt;/li&gt;
&lt;li&gt;处理字体子集&lt;/li&gt;
&lt;li&gt;生成 Pagefind 搜索索引&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;构建完成后，生产文件会出现在：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dist/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;部署到服务器的就是这个目录里的内容。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;本地预览生产版本&lt;a href=&quot;#本地预览生产版本&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;开发环境正常不代表生产构建一定正常，所以部署前最好跑一次预览：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;preview&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;也可以指定端口：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;preview&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;127.0.0.1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--port&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4334&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这个预览服务读取的是 &lt;code&gt;dist&lt;/code&gt;，更接近线上效果。如果这里正常，线上不正常，问题通常就在部署目录、静态资源缓存或服务器配置上。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;服务器环境&lt;a href=&quot;#服务器环境&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;线上使用的是宝塔面板加 Nginx。当前站点配置里，&lt;code&gt;rainzt.cn&lt;/code&gt; 和 &lt;code&gt;www.rainzt.cn&lt;/code&gt; 都指向同一个网站根目录。&lt;/p&gt;&lt;p&gt;Nginx 配置里核心部分是：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;/path/to/site;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;index &lt;/span&gt;&lt;span&gt;index.html;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; / {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try_files &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt; $&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;/ $&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;.html &lt;/span&gt;&lt;span&gt;=404&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;HTTPS 证书由宝塔管理。部署静态文件时，只需要操作网站根目录，不要动证书配置。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;部署流程&lt;a href=&quot;#部署流程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;部署时的思路很简单：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;本地执行 &lt;code&gt;pnpm build&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;把 &lt;code&gt;dist&lt;/code&gt; 打包上传到服务器&lt;/li&gt;
&lt;li&gt;备份旧站点目录&lt;/li&gt;
&lt;li&gt;清空网站根目录里的旧静态文件&lt;/li&gt;
&lt;li&gt;解压新的 &lt;code&gt;dist&lt;/code&gt; 到网站根目录&lt;/li&gt;
&lt;li&gt;检查 Nginx 配置并 reload&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;清空旧文件很重要。Astro 生成的 &lt;code&gt;_astro&lt;/code&gt; 资源带 hash，如果只覆盖不删除，旧 CSS、旧 JS 可能残留。浏览器或 Nginx 又会长期缓存这些带 hash 的资源，最后就容易出现“本地正常，线上某些交互异常”的情况。&lt;/p&gt;&lt;p&gt;部署前最好保留一份旧版本备份。这样就算新版本有问题，也可以快速回滚。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;线上验证&lt;a href=&quot;#线上验证&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;部署完成后，需要检查几个点：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;https://rainzt.cn/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;https://rainzt.cn/api/allPostMeta.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;https://rainzt.cn/_astro/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;首页能打开，说明 Nginx root 和静态文件没问题。&lt;br /&gt;
&lt;code&gt;api/allPostMeta.json&lt;/code&gt; 正常，说明文章元数据构建出来了。&lt;br /&gt;
&lt;code&gt;_astro&lt;/code&gt; 里的 CSS 和 JS 能访问，说明资源路径没问题。&lt;/p&gt;&lt;p&gt;如果刚部署完页面样式或下拉框还是旧状态，可以先强制刷新：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Ctrl + F5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;如果仍然不对，就要检查线上 HTML 引用的资源文件名是不是最新构建生成的文件。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;Git 管理&lt;a href=&quot;#git-管理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目已经初始化 Git。常用流程：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Add new post&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;注意 &lt;code&gt;dist/&lt;/code&gt;、&lt;code&gt;node_modules/&lt;/code&gt;、日志文件等不需要提交。源码、配置、文章、静态资源才是应该进入 Git 的内容。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;后续更新文章&lt;a href=&quot;#后续更新文章&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;以后发新文章可以按这个流程：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;在 &lt;code&gt;src/content/posts/&lt;/code&gt; 新建文章&lt;/li&gt;
&lt;li&gt;设置好 &lt;code&gt;published&lt;/code&gt;、&lt;code&gt;tags&lt;/code&gt;、&lt;code&gt;category&lt;/code&gt;、&lt;code&gt;draft&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;本地 &lt;code&gt;pnpm dev&lt;/code&gt; 预览&lt;/li&gt;
&lt;li&gt;确认无误后 &lt;code&gt;pnpm build&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;上传新的 &lt;code&gt;dist&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;访问线上确认&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;如果只是草稿，就保持：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;draft&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;等要发布时再改成：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;draft&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这个博客的核心链路可以概括成一句话：&lt;/p&gt;&lt;p&gt;用 Markdown 写内容，用 Astro 生成静态站点，用宝塔 Nginx 托管 &lt;code&gt;dist&lt;/code&gt;，用 HTTPS 把它稳定地挂到 &lt;code&gt;rainzt.cn&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;它不是最复杂的部署方式，但对个人博客来说很舒服：本地写作足够快，线上访问足够轻，出问题时也容易定位。后面无论是加文章、加工具页、改导航、换壁纸，基本都能围绕这套流程继续迭代。&lt;/p&gt;&lt;/section&gt;</content:encoded></item></channel></rss>