Skip to main content

3 posts tagged with "技术"

View All Tags
· 19 min read
Allen
normal software engineer
此内容根据文章生成,仅用于文章内容的解释与总结

我是 Docusaurus? 的老用户了。这次我想实现一个小需求:在文档标题旁出现一个“复制为 Markdown”按钮。

我刻意模仿传统用户、有基础的用户、深度用户,想看看强模型下,用户的技术深度是否还有很好的影响力。

本文使用的是 Cursor 版的 Opus 4.6?。每次都用 Cursor Agent 模式。因为 Opus 4.6 比较贵,我给自己加了一个限制:每个阶段只问 7 次。这样一个小需求,7 次应该差不多了吧。

小白

tip

第 1 轮提示词

https://github.com/FlyNumber/markdown_docusaurus_plugin

这个项目的样式很不错,我希望像它这样实现。你看能不能仅在本地实现?

  • 我的点评:普通的开局。
  • Gemini3点评:典型的“甩手掌柜”式开局。只给参考链接而不说明具体逻辑,强行让 AI 进行逆向工程。虽然 Opus 4.6 能够读取网页,但这种缺乏上下文的提问会让 AI 生成大量通用代码,而非针对你现有项目的适配方案。
  • GPT5.3点评:目标存在,但约束不足。没有说明当前项目结构、技术路径或限制条件,模型只能进行高层猜测,容易走向泛化方案而非可执行实现。
  • Opus 4.6(当事人)点评:给了一个参考项目链接,但没有说清楚"像它这样"具体指哪部分——是按钮的位置、交互方式、还是视觉风格?"仅在本地实现"的约束也比较模糊(是指不发布 npm 包?还是不依赖外部服务?)。模型只能靠猜测来填补这些空白,第一步的方向偏差就此埋下。
tip

第 2 轮提示词

https://www.npmjs.com/package/docusaurus-plugin-includes?activeTab=code

我模仿这个插件设置了我的 Markdown Copy Button,但是按钮位置不正确。

请你获取这个包,查看并比较其代码,尤其是关于如何定位按钮元素的部分,帮我分析可能的原因并修复。

  • 我的点评:继续等待结果。
  • Gemini3点评:此时用户陷入了“找参考”的循环。试图通过让 AI 比较两个第三方库来解决本地的样式问题,这其实增加了 AI 的推理成本,容易导致它在处理 DOM 选择器时产生幻觉。
  • GPT5.3点评:明显提升。开始提供对比对象 + 具体问题 + 分析任务。但仍缺少当前代码片段,模型需要“想象你的实现”,错误概率较高。
  • Opus 4.6(当事人)点评:引入了第二个参考项目来做对比分析,思路不错。问题在于:用户自己也不清楚按钮位置为什么不对,把"诊断"和"修复"同时甩给模型,相当于让模型在两个未知代码库之间做 diff 推理——这对上下文窗口和推理准确度都是很大的压力。
tip

第 3 轮提示词

https://github.com/FlyNumber/markdown_docusaurus_plugin

请你仔细参考官方样式,确保 1:1 复刻。

  • 我的点评:开始着急,语气也更强硬了。
  • Gemini3点评:情绪化提示词。这里的“1:1 复刻”是一个非常模糊的指令,因为 CSS 表现受到父级容器、全局样式和 Docusaurus 版本的共同影响。对 AI 施压通常无法获得技术上的突破。
  • GPT5.3点评:需求变得严格,但信息反而减少。强调结果而非路径,会导致模型反复调整样式层,而不是定位架构问题。
  • Opus 4.6(当事人)点评:典型的"加压式提示"——用"仔细""确保""1:1 复刻"来强调期望,但没有提供任何新的技术信息。模型并不会因为你说"仔细"就变得更仔细;它缺的是具体的 CSS 选择器或 DOM 结构差异描述,而不是更高的情绪压力。
tip

第 4 轮提示词

我将其调整至 Markdown 的顶部,但样式上应该与标题横向对齐,请你思考如何处理。

  • 我的点评:这个提示词开始有用,但如果方向不对,只会让注意力越跑越偏。
  • Gemini3点评:终于开始描述具体的 UI 布局问题。但由于没有提供 HTML 结构片段或 CSS 现状,AI 只能靠猜(例如猜测是 Flex 布局还是 Absolute 定位)。
  • GPT5.3点评:开始进入“具体问题空间”,这是有效转折点。但仍停留在 CSS 层,未验证是否属于组件挂载层错误。
  • Opus 4.6(当事人)点评:终于给出了一个具体的布局约束——"与标题横向对齐"。这是整个小白阶段最有技术含量的一句提示。遗憾的是,此时模型可能已经在错误的实现路径上走了三轮,积累了大量错误上下文,纠偏成本已经很高。
tip

第 5 轮提示词

还有其他方案吗?

  • 我的点评:意识到方向不对,开始发散思维。
  • Gemini3点评:在没有解决前一个方案 Bug 的情况下开启新方案,是“小白模式”失败的预兆。这会让 Agent 在文件系统中创建更多冗余代码,增加项目混乱度。
  • GPT5.3点评:良好的纠偏行为,但过于开放。模型会生成多个架构级方案,反而增加试错成本。
  • Opus 4.6(当事人)点评:短小但关键的一个转折。主动让模型跳出当前方案是正确的直觉。但更好的做法是附上"当前方案的问题是什么"——比如"当前方案用了绝对定位导致响应式布局错位,有没有用 flex 布局的方案?"这样模型才能真正换一条路,而不是在同一棵树上换根枝。
tip

第 6 轮提示词

试试:方案 2

  • 我的点评:继续尝试。
  • Gemini3点评:盲目试错。此时已经完全失去了对技术实现的掌控感,纯粹在靠 AI 的随机性碰运气。
  • GPT5.3点评:典型“AI 驱动试错”。没有重新校验问题定义,只是在既定错误路径上继续前进。
  • Opus 4.6(当事人)点评:极度简短,完全依赖上一轮模型给出的方案列表。如果模型上一轮的"方案 2"本身描述就有歧义,这一轮就是在盲选。一个更稳妥的做法是用自己的话复述方案 2 的核心思路,确认双方理解一致后再执行。
tip

第 7 轮提示词

Module not found: Error: Can't resolve '@theme/PwaReloadPopup' in 'C:\Users\jiang\Desktop\github\jiangyangcreate.github.io\node_modules\@docusaurus\plugin-pwa\lib'

  • 我的点评:直接丢报错。遗憾的是,修复后样式依然不正确,7 次机会用完了。
  • Gemini3点评:典型的“报错驱动开发”。当对话进入修 Bug 环节时,如果底层逻辑(方案 2)本身就不适配 Docusaurus 的架构,即便消除了报错,功能依然无法达成。
  • GPT5.3点评:属于被动调试阶段。模型只能修补表层错误,无法回溯最初的设计偏差。
  • Opus 4.6(当事人)点评:直接粘贴报错是最省力的提示方式,对于明确的编译错误来说效率很高。但此时核心问题已经不是这个 import 错误,而是整体架构方向的偏差。7 轮中有 5 轮都在"修补"而非"重新审视",这是小白阶段失败的根本原因:缺乏在中途叫停、重新定义问题的能力。

基础

我再模仿“有技术背景、但不熟悉 Docusaurus 用户?”的提示词:

tip

第 1 轮提示词

目前这个项目 https://www.flynumber.com/docs/,点击按钮获取 Markdown 的逻辑是什么,怎么实现的?

  • 我的点评:看到这个项目已经有“加按钮”的实现,于是我引导模型去分析这个网站。
  • Gemini3点评:比小白阶段聪明的一点是:先问逻辑。理解逻辑后再动手,比直接要代码更稳健。
  • GPT5.3点评:这是明显升级。开始利用现成正确实现作为 ground truth,减少模型幻想空间。
  • Opus 4.6(当事人)点评:比小白阶段高明得多的开局——先"理解"再"动手"。让模型分析一个已经跑通的实现,相当于做逆向工程,能帮自己建立对技术方案的全局认知。不过,让模型分析线上网站存在一个风险:模型可能只能看到前端代码,看不到构建配置和插件注册逻辑,分析结果可能不完整。
tip

第 2 轮提示词

按钮定位思路来自第一轮对话结果的分析

这是我的按钮定位思路:

插件接入层:通过 index.js 的 getThemePath() 暴露 theme/,让 Docusaurus 使用自定义的 theme/Root.js 作为根组件入口。
页面 & DOM 选择:在 Root.js 中用 useLocation() 拿到 pathname,仅在以 /docs/ 开头的文档页执行逻辑;通过 document.querySelector('article .markdown header') 精确找到文档标题所在的 header。
挂载点注入:在该 header 内新建一个 div.markdown-actions-container,并作为最后一个子节点 append 进去,再用 createRoot(container).render(<MarkdownActionsDropdown />) 把按钮组件挂载到这个容器。
样式层定位:在站点 CSS 中把 article .markdown header 设为 display: flex,h1 设为 flex: 1 1 auto,并给 .markdown-actions-container 设置 margin-left: auto(以及移动端、RTL 的补充样式),从而实现「标题在左,按钮在右,且在不同布局下表现良好」。

请你按照我的思路,修改现有的 CopyMarkdownButton 组件。

  • 我的点评:根据模型给出的分析结果继续执行。这里其实有个问题:模型会出错。上一步分析结果也可能是错的;如果你没有能力判断其正确性,反而应该先让模型“直接模仿目标网站”把功能跑通。
  • Gemini3点评:这是一个结构化指令的范本。它包含了“注入层、逻辑层、挂载点、样式层”的完整思考。虽然用 querySelector 操作 DOM 在 React 框架中不是最优解,但其工程描述非常清晰。
  • GPT5.3点评:高质量提示词。提供完整架构推理链,使模型进入“执行模式”。但风险在于:若前提分析错误,后续全部建立在错误基础上。
  • Opus 4.6(当事人)点评:信息密度最高的一条提示词。四层架构(插件层、页面层、挂载层、样式层)描述得非常清晰,模型几乎不需要猜测。问题在于:这个方案本身用了 querySelector + createRoot 的命令式注入,绕开了 Docusaurus 的声明式主题体系,这意味着会遇到 SSR/hydration 不一致、路由切换时组件不刷新等问题。方案的"结构化程度"很高,但"方向正确性"存疑——有时候,过早地给出详细方案反而会锁死模型的探索空间。
tip

第 3 轮提示词

试着给 Blog 插件下的文档页注入这个按钮。

  • 我的点评:由于模型实现有误,此时发现不对劲,于是继续引导到插件层。
  • Gemini3点评:开始探索 Docusaurus 的多实例特性。用户意识到仅靠 Root.js 这种“全局补丁”方式可能在不同路由下有兼容性问题。
  • GPT5.3点评:正确的方向修正。从页面层开始上移到插件生命周期,是工程思维提升的表现。
  • Opus 4.6(当事人)点评:开始意识到 docs 和 blog 是两套不同的插件体系,需要分别处理。提示词本身简洁合理,但"试着"这个词暗示用户自己也不确定可行性——如果能补一句"Blog 插件的布局组件是哪个?"先确认技术可行性,会更稳。
tip

第 4 轮提示词

如何给 docs 也注入这个按钮?

  • 我的点评:给一个开放式问题,让模型自由发挥。
  • Gemini3点评:提示词略显后退,变得模糊了。但在经历过报错后,给模型一定的“自主权”有时能激发它找到更符合官方推荐(Best Practice)的路径。
  • GPT5.3点评:开放式探索适合此阶段,但最好附带限制(swizzle / theme override / plugin API)。
  • Opus 4.6(当事人)点评:在前几轮过于具体的指导之后,这里切换成开放式问题是一个很好的节奏调整。让模型自己选择 docs 插件的注入方式,可能会发现比用户预设方案更好的路径。这体现了一个重要的提示技巧:在你不确定的领域,给模型自由度比给模型指令更有效。
tip

第 5 轮提示词

Compiled with problems:
×
ERROR in ./src/theme/BlogPostPage/index.js
...

  • 我的点评:这里模型多次报错,最后还是人工介入才解决。
  • Gemini3点评:AI 在自动修改 node_modulesswizzle 出来的文件时,经常会发生重复声明或导入路径错误。这里的人工介入体现了“基础”用户虽然懂代码,但被 AI 的粗心带进了坑里。
  • GPT5.3点评:说明模型已进入复杂重构区。缺乏上下文完整性时,AI 很难维持大型文件的一致性。
  • Opus 4.6(当事人)点评:重复声明错误说明模型在修改文件时没有正确处理已有的 export——这是 Agent 模式下常见的"部分覆盖"问题。直接贴报错让模型修是对的,但如果能同时说一句"你在追加代码时没有删除原有的 default export",模型定位问题会快一个数量级。
tip

第 6 轮提示词

为什么我打开网页,没有第一时间出现这个按钮,反而是刷新一下就有了?

  • 我的点评:新的 BUG 出现了。
  • Gemini3点评:这是一个非常深刻的单页应用(SPA)生命周期问题。这种观察力是区分小白与基础用户的关键:小白只会说“不行”,而基础用户能发现“路由切换不触发,刷新才触发”的规律。
  • GPT5.3点评:优秀的问题类型。开始关注生命周期与 hydration,而非表面功能。
  • Opus 4.6(当事人)点评:这个问题描述非常精准——"首次加载没有,刷新后有"直接指向了客户端渲染时机的问题(大概率是 useEffect + querySelector 在 SPA 路由切换时没有重新执行)。好的 bug 描述就是最好的提示词,这一轮做得很到位。
tip

第 7 轮提示词

改成「不用 querySelector,直接在 Docusaurus 的文档布局里声明式渲染」

  • 我的点评:最终结论是:这个阶段并没有在 7 轮对话内完成项目。
  • Gemini3点评:这是一个极其关键的转折。用户意识到“命令式(DOM操作)”走不通,开始要求转向“声明式(React组件注入)”。可惜 7 次额度已满,否则这一步将是通往成功的曙光。
  • GPT5.3点评:这是关键跃迁:从 DOM 操作 → 框架声明式扩展。说明用户开始主导架构,而不是让模型主导。
  • Opus 4.6(当事人)点评:这条提示词本质上是在第 7 轮推翻了第 2 轮的整体架构——从命令式注入转向声明式渲染。方向完全正确,这正是 Docusaurus 的正统做法(swizzle 主题组件)。遗憾的是来得太晚了:如果这条出现在第 2 或第 3 轮,整个基础阶段大概率能在 7 轮内完成。这再次印证了一个规律:越早纠正架构方向,越省后续的调试轮次。

熟悉

作为熟悉 Docusaurus 的用户,我最终的提示词是:

tip

第 1 轮提示词

Docusaurus 通常把一些组件/插件隐式运行,但用户可以在 swizzle(导出)后进行替换。这段话在官方文档哪里?

  • 我的点评:引导注意力机制到合适的技术方案上。
  • Gemini3点评降维打击。 专家级用户不直接要代码,而是先确认“底层规则”。通过 Swizzle(Docusaurus 的核心机制)来切入,这直接排除了前两个阶段中所有“暴力操作 DOM”产生的无效路径。
  • GPT5.3点评:优秀的“注意力校准提示词”。不是解决问题,而是先定位正确知识源。
  • Opus 4.6(当事人)点评:高手开局。不是直接说"帮我加按钮",而是先让模型定位到 swizzle 机制的官方文档。这一步的价值在于:(1) 确认模型对 swizzle 的理解是否正确;(2) 把模型的注意力锚定在 Docusaurus 的官方扩展范式上,避免它走弯路去搞 querySelector 注入。用一个"知识确认"问题来校准模型的认知基线,是非常老练的提示技巧。
tip

第 2 轮提示词

请你接管我的电脑,找到合适的组件,让我可以在博客标题旁渲染 "复制为 Markdown" 这个按钮。

  • 我的点评:再次引导注意力机制到合适的技术方案上,给出模糊的目标。
  • Gemini3点评:基于第一步确立的 Swizzle 共识,专家给出了极大的自由度。因为路径正确,此时的“模糊目标”反而能让 AI 精准找到 DocItem/HeaderBlogPostPage 等核心组件。
  • GPT5.3点评:高阶提示方式。目标明确但路径开放,使模型执行搜索与决策,而非生成代码。
  • Opus 4.6(当事人)点评:在第 1 轮已经锚定了 swizzle 范式后,这里故意给出模糊目标——"找到合适的组件"。这不是偷懒,而是策略:让模型自己去判断该 swizzle 哪个组件(DocItem/Layout?BlogPostPage?),这比用户硬指定一个可能错误的组件要安全得多。在正确的框架约束下给出模糊目标,是让模型发挥最大价值的甜蜜点。
tip

第 3 轮提示词

思考下,@custom.css (1719-1721) 这个样式会被应用到 @index.js (13-15) 的 title 吗?如果不会,如何调整最优雅?

  • 我的点评:精准给出 AI 能理解的样式异常。
  • Gemini3点评:这是最高级的反馈:“代码行级别的精准定位”。不再泛泛而谈“样式不对”,而是直接指出 CSS 选择器与 JS 组件的对应关系。这消除了 AI 的所有猜测空间。
  • GPT5.3点评:接近专家级提示词。特点:明确作用域 + 指定文件位置 + 明确假设验证
  • Opus 4.6(当事人)点评:教科书级别的提示词。三个亮点:(1) 用 @文件名 (行号) 精确引用代码位置,消除歧义;(2) 问题本身是一个可验证的 yes/no 判断("会不会被应用到"),模型容易给出准确答案;(3) 追问"如何调整最优雅"而不是"帮我修",给了模型设计空间。整个"熟悉"阶段只用了 3 轮就接近完成,和小白阶段的 7 轮未果形成鲜明对比。

体会

不得不说,Opus 4.6 还是相当强悍。模型是 本体放大器?:你越能提出结构化问题、越能判断中间结果,模型就越强。

  • 我的点评:这次实验非常真实地展示了“提示词质量决定上限,工程判断力决定下限”。如果把 AI 当成一个高水平但会犯错的搭档,而不是“全自动执行器”,效率会更高、返工会更少。2026 年,你依然值得深入学习编程。
  • Gemini3点评:完全赞同。小白在“求药”,基础用户在“开方”,而专家用户在“定规矩”。2026 年的编程,不再是手写每一行代码的体力活,而是从海量可能性中快速“剪枝”的判断力竞争。
  • GPT5.3点评:实验核心结论成立:模型不是替代工程能力,而是放大工程能力的乘数。用户水平越高,AI 从“代码生成器”越接近“协同架构师”。
  • Opus 4.6(当事人)点评:这篇博客最有价值的发现是:三个阶段使用的是同一个模型、同一个需求,但轮次消耗差异巨大(7轮失败 → 7轮未完成 → 3轮接近完成)。这说明在强模型时代,提示词的边际收益远高于模型能力的边际收益。小白阶段的核心问题不是"模型不够强",而是用户无法在中途判断方向对不对、该不该推翻重来。"本体放大器"这个比喻非常精准——模型放大的不只是知识,更是你的"元认知能力":知道自己不知道什么、知道什么时候该叫停、知道该把哪些决策权交给模型。2026 年学编程的意义,或许正在从"会写代码"转向"会驾驭写代码的 AI"。

题外话:我也试着让Qwen3.5-Plus点评,但是同样的提示词只点评全文,不点评每一次的提示词,国产模型依然任重道远。

· 11 min read
Allen
normal software engineer
此内容根据文章生成,仅用于文章内容的解释与总结

跨域

想象一下,你曾经或者正登录着银行网站(bank.com),同时在另一个标签页打开了一个恶意网站(evil.com)

如果没有跨域限制,evil.com 里的 JS 脚本可以轻而易举地:

  • 读取你银行网站的 Cookie。
  • 冒充你向银行发送「转账」请求。
  • 读取你的私人账单数据。

这就是著名的 CSRF?(跨站请求伪造)数据泄露 风险。为了防止这种「大乱斗」,浏览器强制执行了 同源策略?(Same-Origin Policy)

虽然跨域有限制,但现代互联网需要资源共享(比如你的博客引用自己的云音乐平台的歌曲,通过脚本读取音频渲染出跳动的音符)。于是有了 CORS?(Cross-Origin Resource Sharing,跨源资源共享)

它本质上是服务器与浏览器之间的一次对话

  1. 浏览器问: 「嘿,服务器,我这有个来自 abc.com 的网页,想拿你的 MP3,准吗?」
  2. 服务器答(响应头): 「准了!我带上 Access-Control-Allow-Origin: https://abc.com。」

如果你是一个善良的免费平台,你也可以用*表示所有人都可以访问:Access-Control-Allow-Origin: *

  1. 浏览器看: 「嗯,名单上有它,放行!」

CORS 是白名单制度。服务器必须明确说出允许谁访问。

如果服务器没配 Header,或者缓存了没头的响应,浏览器会一直拒绝!

tip

浏览器双标行为

在 Web 早期,像 <img><video><audio><script> 标签都被设计为可以引用任何域名的资源。浏览器认为,既然你只是想「看」或者「听」,并不会把像素数据或音频二进制流通过 JS 拿走,那就没风险。这被称为无损读取。因此<audio src="xxx">(无属性):浏览器以「匿名」身份去拿。服务器返回什么它都播,它不在乎有没有 Access-Control-Allow-Origin

如果你想在网页上实现音频可视化(跳动的频谱)、音效滤镜或者点击下载:浏览器要求你必须通过 AudioContext 提取音频数据。此时必须用 fetch("xxx") 才能操作二进制数据。fetch("xxx"):浏览器以「跨域」身份去拿。如果服务器没给「通行证」,即使数据下载完了,浏览器也会在最后关头把数据拦下并报错。

如果你的代码之前通过 audio 标签在浏览器里缓存了一个音频(不带 Access-Control-Allow-Origin),之后又改为用 fetch 获取这个资源,浏览器会从缓存里取出无 CORS 头的旧响应提供给 fetch,并在控制台提示该资源被拦截。

建议: 给 audio 标签加上 crossOrigin="anonymous"。这样 audio 和 fetch 都会要求服务器返回 CORS 头。

为了避免相同资源被频繁读取,不只是浏览器,整个网络都充满了各种各样的缓存。

缓存链路

缓存层级存储位置常见缓存时长 (TTL)对 CORS 的影响缓存清除方式
浏览器强缓存用户本地磁盘/内存分钟级到年级(由 Cache-Control 决定)最致命。一旦存入不带 CORS 头的响应,后续请求不再询问服务器。强制刷新(Ctrl+F5)、手动清除浏览器缓存、或修改资源 URL(加版本号/hash)。
浏览器协商缓存用户本地即时校验(ETag / Last-Modified相对安全。每次都会向服务器确认文件是否变更,服务器在响应(包括 304)中有机会补发正确的 CORS 头。无需特殊清除,每次请求均会与服务器协商。
Nginx 反向代理缓存服务器内存/磁盘通常不主动缓存(除非配置了 proxy_cache若开启缓存且未配置 Vary: Origin,会将缓存的错误响应(无 CORS 头)发送给所有用户。清空 proxy_cache 目录、重载 Nginx,或配置 proxy_cache_bypass
CDN(如云存储/边缘节点)全球边缘节点小时级到天级(通常 24h)扩散性强。一旦边缘节点缓存了缺少 CORS 头的响应,该地区所有用户均会触发 CORS 报错。在 CDN 运营商后台手动刷新/预热缓存,或修改资源 URL 强制回源。
ISP / 运营商网关运营商机房不透明(几小时到几天)极少数情况下(通常仅限 HTTP 明文流量),运营商会劫持并缓存静态资源,HTTPS 下基本不受影响。基本无法主动清除,只能等待自然过期,或改用 HTTPS 规避。
后端框架内存缓存应用服务器内存秒级到小时级(由代码逻辑决定)若缓存了不含 CORS 头的响应对象并直接返回,后续请求将持续缺少 CORS 头,且对所有用户生效。重启应用服务、调用缓存失效接口(如 Redis DEL、Spring @CacheEvict),或等待 TTL 自然过期。

浏览器缓存

缓存条目的 Response Headers 在开发者工具里不会展示 Access-Control-Allow-Origin,给排查带来一定困难。

info

浏览器的逻辑顺序

  • 读取: JS 发起 fetch,浏览器发现磁盘缓存命中。
  • 校验: 浏览器取出 from disk cache 的原始响应头,内部检查是否包含 Access-Control-Allow-Origin
  • 通过: 检查通过,数据交给 JS。出于安全考虑,浏览器在 Network 面板中会省略展示部分安全相关头(如 CORS 头)。
  • 展示: 当你在开发者工具(Network 选项卡)点开该请求时,看到的是浏览器认为对当前调试有意义的头信息。

chrome://net-export/ 是 Chrome 最强大的网络日志工具,可将所有网络行为(包括读取缓存的细节)记录到一个 JSON 文件中。

操作步骤:

  • 在地址栏输入 chrome://net-export/
  • 点击 "Start Logging to Disk",选择保存位置。
  • 回到你的博客页面刷新,重现那个 from disk cache 的请求。
  • 回到日志页面点击 "Stop Logging"。
  • 使用 NetLog Viewer 打开该 JSON。
  • 在日志中搜索 HTTP_CACHE_GET_HEADERS,即可看到缓存里所有原始的 Response Headers
tip

启发式缓存

HTTP 旨在尽可能多地缓存,因此即使没有给出 Cache-Control,如果满足某些条件,响应也会被存储和重用。这称为启发式缓存

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2021 22:22:22 GMT

复用多长时间取决于实现,但规范建议存储后大约 10% 的时间。

本例中:约 0.1 年 = (Date − Last-Modified) × 10%。

浏览器分析

下面是一段 Chrome NetLog 的片段:

{"params":{"batch_id":0,"encryption_level":"ENCRYPTION_FORWARD_SECURE","packet_number":452,"sent_time_us":472946859030,"size":1250,"transmission_type":"NOT_RETRANSMISSION"},"phase":0,"source":{"id":19077,"start_time":"471676479","type":12},"time":"472946859","type":313},

{"phase":2,"source":{"id":27027,"start_time":"472946851","type":14},"time":"472946862","type":156},

{"params":{"cors_preflight_policy":"consider_preflight","is_revalidating":false,"request_headers":{"headers":["sec-ch-ua-platform: \"Windows\"","Accept-Encoding: identity;q=1, *;q=0","User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36","sec-ch-ua: \"Not:A-Brand\";v=\"99\", \"Google Chrome\";v=\"145\", \"Chromium\";v=\"145\"","Range: bytes=0-","sec-ch-ua-mobile: ?0","Accept: */*"],"line":"GET /static/summary/blog_2026_2_28_.mp3 HTTP/1.1\r\n"},"url":"https://chat.jiangyang.fun/static/summary/blog_2026_2_28_.mp3"},"phase":1,"source":{"id":27030,"start_time":"472946868","type":1},"time":"472946868","type":576},

[事件 27030] 媒体资源加载日志

  • 目标: AI 总结音频(blog_2026_2_28_.mp3)
  • 浏览器行为: 通过 HTTP/1.1 获取资源
  • 安全决策: 启用 CORS 策略检查(consider_preflight
  • 请求头特征: 带有 Range: bytes=0-(请求媒体流)、Chrome 145 的 User-Agent
  • 底层状态: 发送了编号为 452 的加密封包,大小 1.25KB,传输正常

另一条日志中的缓存 Key:

{"params":{"created":false,"key":"Range_1/0/_dk_https://jiangmiemie.com https://jiangmiemie.com https://chat.jiangyang.fun/static/summary/blog_2026_2_28_.mp3:2faabf977670a1:0"},"phase":1,"source":{"id":27032,"start_time":"472946870","type":14},"time":"472946870","type":156},

这个字符串是浏览器在磁盘上查找缓存的「身份证号」,由以下几部分组成:

  • Range_1/0/: 分段缓存,表示该音频曾被分段请求并缓存。
  • dkDouble Keying?): 双重键值。现代浏览器不再只按 URL 缓存,而是按「顶层站点 + 请求来源 + 资源 URL」组合缓存。
  • https://jiangmiemie.com: Top-level Site(顶层站点)。
  • https://jiangmiemie.com: Frame Origin(请求来源)。
  • https://chat.jiangyang.fun/static/...mp3: 资源真实 URL。

结论 A:缓存已经彻底「分区」了

由于 Key 里包含了 https://jiangmiemie.com,这个缓存条目是专门为你的博客页面生成的。即使你直接在地址栏访问该 MP3,浏览器会使用另一套 Key(不含来源域名)。跨域访问与直接访问对应两套独立的缓存条目;当前命中的是带 CORS 头的健康版本。

结论 B:created: false 意味着命中

"created": false 表示没有新建缓存条目,而是命中了已有条目,请求确实来自 disk cache。命中带 jiangmiemie.com 的 Key,说明该缓存项在写入时已通过跨域校验并记录了来源信息。

结论 C:双重验证通过

日志中的 2faabf977670a1 是该缓存条目的哈希指纹。只要该 ID 对应的资源能正常加载,说明其内部存储的 Access-Control-Allow-Origin 头是完整的。

未雨绸缪

若在商业项目中遇到类似问题:某资源未配置跨域,用户端首页大图裂开,CDN 已分发、用户浏览器也已缓存错误响应。如何紧急处理、避免 T0 事故?

思路:把「图片名称」和「真实地址」分离,例如配置里只存 "home_banner_url": "v1.png",真实 URL 由配置中心下发。

前端初始化时接入配置中心 SDK(例如 Nacos?),通过 WebSocket 或长轮询维持连接:

// configClient.js(伪代码)
import { ConfigClient } from 'nacos-sdk';

export const config = {
home_banner_url: "https://cdn.com/image.png"
};

client.subscribe({ dataId: 'app_config' }, (content) => {
const newConfig = JSON.parse(content);
config.home_banner_url = newConfig.home_banner_url;
store.dispatch({ type: 'UPDATE_CONFIG', payload: config });
});

组件不写死 src,而是从全局状态读取当前 URL:

// BannerComponent.jsx
import { useSelector } from 'react-redux';

const Banner = () => {
const bannerUrl = useSelector(state => state.config.home_banner_url);
return (
<div className="banner">
<img src={bannerUrl} alt="首页大图" />
</div>
);
};

场景: 早上 10:00,发现 image.png 因 CORS 缓存污染在全球范围裂开。

紧急修复(无需发版):

  1. 打开 Nacos 管理后台,将 home_banner_url.../image.png 改为 .../image.png?v=0304_fix,点击发布。
  2. 正在运行的前端通过 WebSocket 收到新配置,React 状态更新,imgsrc 变为新 URL。
  3. 浏览器将新 URL 视为新资源,绕过旧缓存,向 CDN 请求最新图片。
  4. 用户无感刷新,裂图恢复。

参考链接

· 12 min read
Allen
normal software engineer
此内容根据文章生成,仅用于文章内容的解释与总结

学习

学习可以分为两个核心过程:记忆抽象

传统观点认为,记忆依赖重复:

  • 短时间内重复的内容越少、重复次数越多,记忆越牢固;
  • 反之,若一次塞入大量内容,即使重复,也难以深入记忆。

因此,人们常通过精简信息来提升记忆效率。例如,将"long double 在不同平台上的长度不一致"简化为一句结论,以减少认知负担。

新发现

但最近我发现:适当增加"易于吸收"的背景知识,反而能增强记忆效果

比如,了解以下背景:

1980年代初,Intel 推出了 x87 浮点协处理器(8087),采用 80 位(10 字节)扩展精度格式,显著提升了科学计算的精度。
Linux 和 GCC 选择充分利用这一硬件特性,将 long double 实现为 80 位;
而 Microsoft 在 Visual Studio 中则采取保守策略,兼容性优先。将 long double 等同于 64 位的 double

这段看似"额外"的信息,因其具有历史脉络、技术动机和对比逻辑,反而更容易被记住——甚至比孤立的结论记得更牢。

建立联系

这让我意识到:有效的记忆不仅依赖重复,更依赖"联系"

这种联系有两种关键形式:

向外连接(教学视角)

将新知识与学习者已有的知识网络通过故事性、情境化的方式连接起来。例如,用"Intel 为何设计 80 位?"这个问题,把抽象的数据类型与真实的技术演进挂钩。

关键在于:找到那个能触发共鸣的"锚点"——它可能是历史事件、生活类比,或一个引人好奇的矛盾。

向内连接(自我解释视角)

在尝试向他人解释前,先自问:

  • 我会问自己哪些问题?
  • 是否用陌生术语解释了另一个陌生术语?
  • 小学生能听懂吗?
  • 能否换一种更直观的说法?

这个过程迫使你反复梳理逻辑、提炼本质,本质上是在重复中完成抽象,在抽象中强化记忆。

正反馈机制

学习陷阱

但仅有联系还不够:学习必须有明确的正反馈机制

就像游戏通过音效、金币、等级等即时、直观的反馈激励玩家持续行动,学习也需要清晰的"进度信号"和"成功标志"。否则,极易陷入两种陷阱:

  • 过度发散:例如初学 C 语言时,执着于深挖 printf 的源码实现,而忽略了基础语法体系的完整性;
  • 方向迷失:没有阶段性目标,不知道"学到什么程度算掌握",导致焦虑或低效重复。

因此,好的学习规划必须包含可衡量的标记

  • 好的标记

    • "本段需全文背诵"(可自我检测)
    • "能独立写出一个使用 long double 的示例程序"(行为可验证)
    • "能向同学解释为什么 GCC 和 MSVC 对 long double 处理不同"(输出即检验)
  • 模糊的标记

    • "重要知识点"(重要到什么程度?怎么验证?)
    • "理解即可"("理解"是主观感受,容易自我欺骗)

这些明确的反馈节点,不仅提供成就感,还能划定学习边界——知道何时该深入,何时该暂停、转向下一模块。

高效学习

学习公式

高效学习 = 联系 × 重复 × 反馈

真正的学习不是被动重复,也不是无边界的探索,而是在建立联系的过程中,通过有反馈的重复与抽象,逐步构建可验证、可迁移的知识网络

技术

𝝅ython

现在的Python 就像是这几年的英雄联盟,你想新手入门到大师,需要了解上百个英雄和技能和游戏机制。

学习感悟

前期是非常痛苦的,但是一旦上手你就会想:好久没有新英雄了。多出几个吧。

Python 的基础已经很久没有新鲜玩意儿了,我希望多出几个。

重做对新人也很友好,新选手永远有机会,假设当某一个特性被重做了,那么我之前的所有的熟练度都消失了,新人和老人站在同一跑线上。

面向服务器管理环境

环境管理误区

相当长一段时间,我被教如何给windows配置环境变量,也在这样教别人。

如果你只是一个初学者,或者你本身不是编程人员只在找"他山石"。那么这样做是没有问题的。

但如果要真正的从事生产开发的话,你的所有的环境管理一定是面向服务器的。服务器很少是windows环境,大部分是Linux环境,你所学习的内容无法帮助你解决工作的环境管理问题。

所以首先我要反思的就是我花了太多时间在windows的环境管理和设计上。所有的工作都应该基于Linux,面向服务器

过早设计项目

但现在回过头来看,当年设计的项目其实都非常的愚蠢,就是你在学习了一些基本的代码知识之后,然后你用它去实现完成的项目往往包含着:

  • 大量的重复;
  • 没有做到一处修改,处处生效;
  • 非常长的if else;
  • 相同函数的复制粘贴。

随着年纪的增长,你对项目的结构会有相当大的变化。

争议讨论

这里也许有人有争议,心想如果你之前不做那些愚蠢的项目,你后面怎么能够量变引起质变呢?

当然有可能在代码的一次又一次重构过程中堆积的能力是一次又一次的提升。会对设计模式的理解更加的深刻。

所以这里是一个保留的争议项。

专注

许多有价值的项目都有着大量的成熟产品的替代,在发展过程当中也有可能有别人会来抢你的饭碗,应该坚持找一个方向持续不停的把它做下去。即便大厂和你相似,你也能够聚焦某一个细分领域,把它做得出彩。

项目理念

项目不在于多,而在于精细、深入。

很多人在大学期间就开始尝试构建自己的影响力,比如说做自媒体或者是做技术科普或者做产品,那么我的方法是写博客,那这种影响力其实相对比较低的。他也和我自身的性格有关,我认为我只有在某一个领域走得非常深入,我才有资格向别人传授一些东西。

现在看来,其实你并不用做到像Python核心开发者那样才可以去教一些Python内容,你只要把某一个基础的东西讲的很清楚,你就可以去做自己的影响力内容,比如说很简单的列表。你可以用动画的方式把它呈现出来,你可能没有那么专业,但特别好懂。

低代码工作流平台

所有的数据通信都是通过网络来完成的,也就是一个再简单的图片拼接,这样用opencv几行代码就能完成的工作,你也需要配置一台服务器,然后给他写上各种参数,无疑是增加了一些成本的。

有一定成本的免费服务往往不可靠,在低代码平台当中想要编写一个健壮的工作流,需要额外增加许多节点。 而一旦有一个节点失效,他就会整个工作流崩溃。

低代码工作流不可靠,玩一下可以。别当真了。

风险

谁承担风险,谁做主要决策。避免不对称的风险,是我们的必修课。如果你承担大部分风险,但是仅获取少部分利润。

例如公司希望你调整自己的职业发展(你承担大部分风险),但是利润并没有额外分配给你,这就是风险不匹配。

世界是熵增的

加速的变化,混沌的信息 如果不加干预,不施改变,熵增不停。

如何干预?设置信息过滤系统。

  • 1.可信度(屏蔽不加核实仅转载的媒体)
  • 2.传播度(关注跨行业出圈的信息)
  • 3.相关度(聚焦自身领域的信息,实时整理,确保知识成体系)

对信息要使用自主筛选机制,即:我需要什么,而不是使用视屏流或被动接受信息推送

生活

南方装修建议

入户设计

入户一侧是洗手间、浴室,方便放伞、宠物淋雨后及时洗澡、客人借用厕所。 内置伞架,脏衣篮

入户另一侧是鞋架,换鞋凳

再进一步便是客餐厅

厨房设计

封闭式回型厨房适用于中餐加工+岛台(可以用作餐桌+桌游桌),工具使用排排挂钩。

客厅设有电脑连接大屏电视,供游戏娱乐。

客厅、卧室外通阳台。大阳台方便晾晒大量衣物。

AI眼镜的一些坏处

使用限制

进一些不让携带电子设备的场所例如:考场、涉密单位可能会有一些隐患

下雨的时候会担心它烧坏

耳朵不舒服,需要准备两种眼镜腿

普通话

我每天都在说普通话,应该考个证书证明,查了一下才发现,普通话一级的通过率不到2%。

于是我找了一个一级甲等的老师,每次1个小时的课程,加课后3个小时的练习。通过5次授课提升到了普通话一级。可以满足省级播音员及出镜记者的要求了,以后参与线下活动也更有话语权了。

旅行

湛江

在一个记不清的小吃街里,出于好奇买了一个炸虾饼,饼又硬又嚼不动,虾更是毫无新鲜可言,于是我得出了虾饼很难吃的结论。

上周去了趟湛江,在一家舌尖上的中国打卡店铺里吃海鲜面,同事发现有虾饼可以卖,想尝一下。老板从桶里抓出三只鲜虾,用特别的器皿混着面糊在油锅中晃荡。随后沥油切块。面饼酥脆,虾仁Q弹鲜香。

美食感悟

原来,难吃的不是虾饼。

澳门

口岸到机场有免费的往返接驳车,叫"发财车"。一到MGM就看到了一辆劳斯莱斯小金人。Peoebe在车上很害怕,一路上问我会不会拉到赌场里就不让出来了,我说这里又不是东南亚。

进去赌场,phoebe 被要求查验身份,一问知道如果年纪太小不让进。这里赌博的地方叫娱乐区。连成片的赌场穷奢极欲。这些娱乐场不仅提供西式博彩,还融合酒店、购物、表演等元素,大量的艺术装置随处摆放。

赌场外连着其他赌场,连成一片的澳门威尼斯人、巴黎人、伦敦人建了对应城市的模拟地标:大本钟、埃菲尔铁塔、水道。

观察发现

赌博的人有帅哥美女,有老头大叔。并不都是想象中肥头大耳的暴发户。服务人员也并不都是美女荷官,也有大爷大妈。

去的第一个赌场有个机器最低3.8币一局,威尼斯人最低20块,涉及到真人发牌的最少1000块一局,多的3000。赌场里有免费的软饮,不同的赌场有不同的香水味,非常浓厚。

官也街的手信店很大方,试吃都是一大块一大块的。

即便不博彩,也是很好的购物采风的好地方。

香港

香港随处可见东南亚、欧美国家的游客,比例要比北上广深更多,这次没备现金,不过全程主要消费区都支持微信支付宝。

由于刚下车时乘务员听不懂普通话,所以后面问路啥的都是优先说英语。路边还能看到外国人摆摊儿,外国人开的便利店,真有出国的感觉了。有些店似乎看出来我们是大陆游客(这么明显吗),也会主动说普通话。

城市观察

路上很热也很晒,但是当地人似乎刻意美黑,少有人打伞。路上很干净,我刻意数了一下,一个商品市场只有2个垃圾。

这里吸烟、乱扔垃圾罚款都很高。最低工资7400港币,公交车吸烟罚款5000港币。等价于北上广深吸烟罚款1500+

两人一日游花费

项目费用备注
高铁(往返2人)300人民币直接到市中心
小巴5×2港币划算
香港故宫门票136港币划算
地铁10×2+5×2港币
天星小轮6.5×2港币
二层公交(往返2人)44港币中环到赤柱广场海边
水溶C15港币
果粒橙11港币
牛角包18港币
肯德基58港币实惠
富豪雪糕13港币还好
云南米线62港币好吃
总花费

以上410港币+300人民币,约677人民币