news 2026/9/14 2:20:16

手搓教程:工程师的确定性防线与AI协同方法论

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手搓教程:工程师的确定性防线与AI协同方法论

1. 这不是怀旧,是工程师的肌肉记忆在说话

“为什么现在 AI 这么发达了,还要坚持手搓教程?”——这句话最近在技术社区、设计群、甚至高校教学群里反复刷屏。它表面像一句调侃,实则戳中了当前技术实践里最真实的一道裂痕:一边是 Copilot 自动生成代码、Sora 一键生成视频、Claude 写出结构完整的教案;另一边,是无数人还在熬夜调试一个 npm install 失败的依赖、反复重装显卡驱动、对着 Blender 节点树发呆、在 Figma 里手动对齐 37 个图层的间距。我做技术内容创作十年,带过上百个从零起步的学员,也给大厂内部做过 AI 工具链培训,最常被问的问题不是“怎么用 AI”,而是“AI 给我生成的代码跑不起来,我该信哪一行?”——这恰恰就是“手搓”的不可替代性所在。

手搓不是对抗 AI,而是和 AI 建立信任契约的必经之路。就像你不会让一个刚学会认字的孩子直接去审阅法律合同,AI 的输出本质是概率模型的高置信度采样,它擅长组合已有模式,但无法真正理解“为什么这个 import 顺序会导致循环引用”、“为什么这个 CSS flex 的 shrink 值会让容器塌陷”、“为什么这个提示词里少了一个‘不要加水印’,结果生成的图片全带 logo”。这些“为什么”,必须靠亲手敲下每一行、观察每一次报错、修改每一个参数、对比每一轮输出,才能内化成你的判断力。我去年帮一家教育科技公司重构课程体系,他们最初想用 AI 全量生成 Python 教程,结果第一批学员反馈:“能看懂,但写不出;能运行,但改不了。”——因为教程里跳过了 pip 源配置失败时的报错日志分析、跳过了 virtualenv 环境隔离的底层原理、跳过了 print() 和 logging 的调试层级差异。这些“手搓细节”,才是把知识从“知道”变成“会用”的临界点。

所以,“坚持手搓教程”的核心,从来不是拒绝效率,而是守护技术实践的三个基本锚点:可追溯性、可干预性、可迁移性。AI 输出是一次性快照,而手搓过程是一条带时间戳的完整证据链;AI 结果是黑盒封装,而手搓步骤是你随时可以按下暂停键、插入断点、替换模块的沙盒;AI 教程往往绑定特定版本和平台,而手搓经验一旦形成肌肉记忆,就能在 Ubuntu 24.04、WSL2、M1 Mac 或国产信创环境里快速复现。这不是复古情怀,这是工程师面对不确定性世界时,给自己留下的最后一道确定性防线。

2. 手搓教程的底层逻辑:为什么它比 AI 生成更“稳”

2.1 手搓的本质是构建“认知脚手架”

我们常误以为学习 = 获取信息,但神经科学研究早已证实:人类大脑并非硬盘式存储,而是通过模式识别 + 错误修正 + 情境关联来固化技能。手搓教程的过程,恰好完美匹配这三要素。举个具体例子:教新手用 Python 爬取豆瓣电影 Top 250。AI 可以瞬间生成一段 requests + BeautifulSoup 的完整代码,但新手执行时大概率会遇到:

  • requests.exceptions.ConnectionError: Max retries exceeded(代理/证书问题)
  • AttributeError: 'NoneType' object has no attribute 'text'(选择器失效)
  • UnicodeEncodeError: 'gbk' codec can't encode character(编码异常)

AI 生成的代码不会告诉你这些错误从何而来,但手搓教程会强制你经历:第一次请求失败 → 查文档发现需加 headers → 第二次请求成功但解析为空 → 用浏览器开发者工具检查 DOM 结构变化 → 发现 class 名动态生成 → 改用更鲁棒的选择器 → 保存为 .txt 时乱码 → 查 Python 文件写入编码参数 → 最终得到干净数据。这一连串“失败-分析-调整-验证”的闭环,就是在大脑里搭建起关于 HTTP 协议、HTML 解析、字符编码的立体认知脚手架。而 AI 生成的“正确答案”,只提供了一个静态顶点,却抽掉了所有支撑这个顶点的横梁与斜撑。

我带过的学员里,凡是跳过手搓直接啃 AI 教程的,三个月后几乎全部卡在“调参”环节:面对一个训练不收敛的 PyTorch 模型,他们能熟练调用torch.optim.Adam,却说不清lr=0.001lr=0.01在梯度更新步长上的数学差异,更无法根据 loss 曲线形态判断是学习率过高、batch size 过小还是数据增强过度。因为他们没亲手写过 SGD 的基础实现,没手动计算过反向传播的链式求导,没在 Jupyter 里逐层打印过 tensor shape 的变化。手搓不是慢,它是把抽象概念锚定到具体操作上,让知识获得重量和质感。

2.2 AI 的“幻觉”与手搓的“容错边界”

AI 的强大源于海量数据的统计规律,但它的致命弱点也在于此:当遇到训练数据中罕见或矛盾的组合时,它会基于概率“编造”看似合理实则错误的答案。这种现象在技术领域尤为危险,因为它常披着专业术语的外衣。比如让 AI 生成“用 FFmpeg 合并 MP4 并保持最高画质”的命令,它可能给出:

ffmpeg -i "input1.mp4" -i "input2.mp4" -c:v libx264 -crf 0 -c:a aac -b:a 320k -shortest output.mp4

这条命令看起来很专业,但存在三个隐蔽陷阱:

  1. -crf 0是无损压缩,文件体积爆炸,且多数播放器不兼容;
  2. -shortest会截断较长的视频,而非静音填充;
  3. 未指定-vsync vfr-async 1,可能导致音画不同步。

而一个手搓教程会这样展开:

  • 先明确需求:“保持画质” ≠ “无损”,而是“视觉无损”,对应 CRF 18~22;
  • 对比-c:v copy(流拷贝,零质量损失但格式必须一致)和-c:v libx264(重编码,可控但有损耗)的适用场景;
  • 演示如何用ffprobe检查源文件帧率、码率、编码格式;
  • 提供分步验证:先用-t 10截取 10 秒测试,再用ffplay -autoexit快速预览;
  • 最后给出健壮命令:ffmpeg -f concat -safe 0 -i list.txt -c:v libx264 -crf 20 -c:a aac -b:a 192k output.mp4,并解释list.txt的格式要求。

手搓教程的“慢”,恰恰是把 AI 容易忽略的上下文约束、边界条件、验证手段全部显性化。它不承诺“一步到位”,而是教会你一套排查框架:当结果不对时,先确认输入是否合规(ffprobe),再检查参数是否冲突(ffmpeg -h encoder=libx264),最后验证输出是否达标(ffplay+ 目视比对)。这套框架,才是应对真实世界复杂性的核心能力。

2.3 手搓构建的是“可迁移技能树”,而非“一次性解决方案”

AI 生成的内容天然具有强场景绑定性。你问“怎么用 Vue3 实现购物车”,它给你一个带 Composition API 和 Pinia 的完整组件;但当你需要把这个购物车集成到 React Native 项目里,或者迁移到小程序平台时,AI 给的 Vue 代码就彻底失效。而手搓教程培养的,是从零开始解构问题的能力:购物车的核心状态是什么?(商品列表、数量、总价);关键交互有哪些?(增删改、库存校验、本地持久化);数据流如何设计?(单向数据流 vs 双向绑定)。这些抽象层的知识,不依赖任何框架语法,可以在 Vue、React、Svelte 甚至原生 JS 中复用。

我曾辅导一位传统制造业的工程师转型做工业视觉系统,他需要从零搭建一个缺陷检测流水线。AI 可以给他生成 YOLOv8 训练脚本,但他很快发现:产线相机分辨率是 4096×3072,而公开数据集多为 640×640;标注格式是 XML,而 YOLO 要求 TXT;部署目标是 Jetson Orin,需量化模型。这时,AI 生成的“标准答案”毫无价值。他最终靠手搓完成了三件事:1)用 OpenCV 手动编写图像预处理 pipeline,适配超大分辨率;2)写 Python 脚本将 XML 转为 YOLO 格式,并加入数据增强逻辑;3)用 TensorRT 手搓量化流程,对比 FP16/INT8 的精度-速度平衡点。整个过程耗时两周,但完成后,他不仅交付了项目,还沉淀出一套适配产线的标准化工具链——这才是真正的职业壁垒。

手搓教程的价值,正在于它强迫你直面技术栈的“接缝处”:Python 和 C++ 的 ABI 兼容性、WebGL 和 Canvas 2D 的渲染差异、Linux 内核版本与驱动模块的匹配关系。这些接缝,正是 AI 难以覆盖的灰色地带,也是工程师创造价值的核心战场。

3. 手搓教程的实操方法论:从“跟着做”到“自己造”

3.1 三阶手搓法:拆解、验证、泛化

手搓不是盲目重复,而是一套可复用的方法论。我将其总结为“三阶手搓法”,已在多个技术领域验证有效:

第一阶:原子级拆解(Deconstruct)
目标:把一个完整功能打碎成最小可验证单元。
操作:拿到一个目标(如“用 Flask 搭建用户登录 API”),不急于写路由,而是先独立验证每个原子能力:

  • 能否用sqlite3.connect()正确连接数据库?
  • 能否用hashlib.pbkdf2_hmac()安全哈希密码?
  • 能否用jwt.encode()生成 token 并用jwt.decode()验证?
  • 能否用flask.request.get_json()正确解析 POST 数据?
    每个单元单独写.py文件测试,确保print("PASS")出现。这阶段要刻意回避“抄模板”,哪怕只是import sqlite3这一行,也要手动敲,感受模块加载路径。

第二阶:链路级验证(Validate)
目标:串联原子单元,构建端到端最小可行链路。
操作:将上一阶验证过的单元,用最简方式拼接。例如登录 API 的链路:

  1. 接收 JSON 请求 → 2. 查询数据库 → 3. 比对密码哈希 → 4. 生成 JWT → 5. 返回 JSON。
    关键要求:禁用任何第三方库封装。不用 Flask-SQLAlchemy,手写 SQL;不用 Flask-JWT-Extended,手写 jwt 逻辑;不用 request.json,用request.get_data()+json.loads()。目的不是重复造轮子,而是看清数据在每个环节的形态变化:HTTP body 字符串 → Python dict → SQL 查询字符串 → DB 返回 tuple → 密码哈希 bytes → JWT 字符串 → HTTP response body。

第三阶:场景级泛化(Generalize)
目标:在原始链路上叠加真实约束,形成可复用模式。
操作:给最小链路增加 1-2 个生产环境必需约束:

  • 加入请求频率限制(用 Redis 记录 IP 请求次数);
  • 加入密码强度校验(正则匹配大小写字母+数字+特殊字符);
  • 加入 token 过期自动刷新机制;
  • 加入统一错误响应格式(code/message/data)。
    此时才引入 Flask-RESTful 或 FastAPI 等框架,观察它们如何封装上述逻辑,并对比手搓实现与框架实现的差异。你会发现,框架的@jwt_required()装饰器,本质就是你手搓的verify_jwt_in_request()+get_jwt_identity()的组合;而abort(401)就是你手写的return jsonify({"code":401,"message":"Unauthorized"}), 401。这种“先见森林,再见树木”的过程,让框架不再是魔法,而是你认知版图上的一个坐标。

3.2 手搓中的“黄金十分钟”原则

很多初学者放弃手搓,是因为卡在某个报错上数小时。我的经验是:设置“黄金十分钟”硬规则。当你遇到报错,立即启动计时器:

  • 前 3 分钟:精读报错信息,定位关键词(如ModuleNotFoundError: No module named 'xxx'→ 检查 import 路径;TypeError: 'NoneType' object is not callable→ 检查变量是否被意外赋值为 None);
  • 中间 4 分钟:用最小化复现法(Minimal Reproducible Example)隔离问题。新建空文件,只保留引发错误的 3 行代码,确认是否复现;
  • 最后 3 分钟:搜索报错关键词 + 技术栈名(如"No module named 'torch'" site:pytorch.org),优先看官方文档的 Troubleshooting 章节,而非 Stack Overflow 的高票答案。

超过十分钟未解决?立即暂停,记录下:

  1. 当前环境(Python 版本、包版本、OS);
  2. 已尝试的 3 种方案及结果;
  3. 报错的完整 traceback(复制粘贴);
    然后转向下一个学习点,或寻求帮助。手搓不是死磕,而是建立“问题-证据-假设-验证”的科学习惯。我自己的笔记里,至今存着 2018 年调试 TensorFlow 1.x GPU 支持时的 17 页报错分析,那些被标记为“已解决”的条目,后来都成了我带新人时的避坑清单。

3.3 手搓教程的“四象限”选题策略

选对练习题目,事半功倍。我按“难度”和“价值”两个维度,将手搓选题分为四象限:

高价值(解决真问题)低价值(纯玩具)
高难度第一象限:必做
• 为公司老旧系统写 Python 迁移脚本
• 用 Rust 重写 Node.js 的性能瓶颈模块
• 手搓一个支持 WebP 的轻量图片压缩 CLI
第二象限:慎做
• 用 WebGL 实现太阳系行星运动
• 用 Three.js 渲染梵高《星空》3D 版
低难度第三象限:入门首选
• 手写一个支持 Markdown 的简易博客系统(含渲染、编辑、存储)
• 用原生 CSS 实现 Material Design 按钮组件(含 ripple 动效)
• 用 Bash 脚本自动化整理下载文件夹
第四象限:放弃
• 用 Python 打印 100 行“Hello World”
• 用 jQuery 实现一个会变色的 div

第一象限题目虽难,但直接产出业务价值,且能暴露真实架构矛盾(如遗留系统数据库字段命名混乱、API 文档缺失);第三象限题目简单,但覆盖了 Web 开发全栈核心概念(HTTP、DOM、CSS、存储),是建立信心的最佳入口。我建议新手从第三象限开始,每完成一个,就把它部署到 Vercel 或 GitHub Pages,让成果可见——这种即时反馈,比任何鸡汤都更能维持手搓动力。

4. 手搓与 AI 协同的实战工作流:让 AI 成为你的“高级学徒”

4.1 明确分工:AI 负责“广度”,手搓负责“深度”

手搓不是拒绝 AI,而是重新定义人机协作的边界。我的工作流中,AI 的角色非常清晰:它是我手搓过程中的“高级学徒”,而非“代课老师”。具体分工如下:

  • AI 负责广度探索:当我需要了解一个陌生领域时,让 AI 列出技术选项、对比优劣、生成调研提纲。例如想接入微信支付,我会问:“微信支付最新版 SDK 有哪些接入方式?H5、JSAPI、APP 三种模式的证书配置差异是什么?沙箱环境测试要点有哪些?” AI 会给出结构化摘要,节省我翻阅 20 页官方文档的时间。
  • 手搓负责深度验证:拿到 AI 的摘要后,我立刻动手:
    1. 下载微信支付官方 SDK,阅读README.mdexample/目录;
    2. 在沙箱环境创建测试商户,获取mch_idapi_keyapiclient_cert.pem
    3. 手写 Python 脚本调用unifiedorder接口,逐行调试签名生成逻辑(&key=后缀拼接、MD5 签名、XML 构建);
    4. 用 Wireshark 抓包,对比手搓请求与官方 demo 请求的 header 差异。

AI 提供了“地图”,而手搓是亲自踩点、测绘、标记陷阱。没有地图,踩点效率低;只有地图,踩点永远是纸上谈兵。

4.2 手搓驱动的 AI 提示词工程

要让 AI 成为好学徒,必须用“手搓思维”设计提示词。避免模糊指令如“帮我写一个登录页面”,而应给出明确的约束和验证标准:

“你是一个资深前端工程师,正在为银行内部系统开发登录页。请生成 HTML/CSS/JS 代码,要求:

  1. 使用原生 JavaScript,禁用任何框架(React/Vue);
  2. 表单提交前必须校验:用户名 6-20 位字母数字、密码含大小写字母+数字+特殊字符、两次输入密码一致;
  3. 校验失败时,在对应 input 下方显示红色错误提示,不提交表单;
  4. 成功提交时,用 fetch 发送 POST 请求到/api/login,请求体为 JSON 格式;
  5. 添加 loading 状态:提交按钮文字变为‘登录中...’并禁用;
  6. 代码必须包含完整注释,说明每个校验正则的含义(如/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$/);
  7. 最后提供一份‘手搓验证清单’,列出你需要手动检查的 5 个关键点(如:检查 fetch 的 CORS 配置、验证密码强度正则是否覆盖所有情况)。”

这样的提示词,迫使 AI 输出可验证、可审计、可手搓的代码。它生成的“手搓验证清单”,正是我下一步动手的检查表。AI 不再是答案提供者,而是协作伙伴,它的输出必须经得起我的手搓检验。

4.3 手搓日志:构建个人技术决策数据库

我坚持十年的习惯:每次手搓项目,都维护一份handroll.log文本日志。它不是代码,而是决策记录。例如最近一次手搓一个 RSS 聚合器:

2024-06-15 14:20:00 [决策] 选择 feedparser 而非 xml.etree.ElementTree 原因:RSS 2.0/Atom 1.0 规范差异大,feedparser 自动处理 namespace 和 encoding,ElementTree 需手动处理 UTF-8 BOM 和 CDATA。 验证:用 10 个不同来源 RSS 测试,ElementTree 在 3 个 Atom 源上解析失败。 2024-06-15 16:33:00 [踩坑] SQLite WAL 模式在并发写入时锁表 现象:多线程抓取时出现 database is locked 解决:改用 `PRAGMA journal_mode=WAL;` + `PRAGMA synchronous=NORMAL;`,并添加 retry 逻辑。 教训:WAL 模式提升读并发,但写仍需序列化,高并发场景必须加锁或改用 PostgreSQL。 2024-06-16 09:15:00 [泛化] 为支持未来扩展,将 feed 解析逻辑抽象为插件接口 新增 abstract class FeedParserBase,定义 parse() 方法; 当前实现 FeedParserRSS2、FeedParserAtom; 预留 FeedParserJSONFeed 接口。

这份日志,远比代码更有价值。它记录了我在权衡、试错、验证中形成的判断力。当新项目需要类似功能时,我不再 Google,而是打开handroll.log搜索关键词。AI 可以告诉我“SQLite 支持 WAL 模式”,但只有我的手搓日志,能告诉我“在 500 并发下 WAL 模式仍需配合 retry 才稳定”。这种基于真实场景的决策知识,才是工程师最核心的资产。

5. 常见误区与避坑指南:那些年我们踩过的手搓深坑

5.1 误区一:“手搓 = 从零开始写所有代码”

这是最大的认知偏差。手搓不是复古主义,而是有选择地深入关键路径。例如开发一个电商后台,你不需要手写 HTTP 服务器(用 Flask/FastAPI),但必须手搓:

  • 商品 SKU 的库存扣减逻辑(考虑超卖、回滚、分布式锁);
  • 订单状态机的流转规则(待支付→已支付→发货→完成,每个状态的触发条件和副作用);
  • 支付回调的幂等性校验(用订单号+时间戳+签名三重校验)。

这些是业务核心,决定了系统生死。而 Web 框架、数据库 ORM、前端构建工具,都是成熟基础设施,手搓它们是浪费生命。我的原则是:凡是有 1000+ Star 的开源项目解决的问题,优先用;凡是没有标准答案、需要结合业务定制的问题,必须手搓。曾有个团队坚持手写 Redis 客户端,结果花了三周实现基础功能,却在连接池复用上栽跟头;而另一个团队直接用redis-py,把省下的时间全投入订单幂等性设计,上线后零资损。

5.2 误区二:“手搓教程必须完美,否则就是失败”

新手常因一个报错就放弃手搓,认为“自己太菜”。真相是:手搓过程中的 80% 时间都在调试,而调试本身就是学习的主干道。我统计过自己 2023 年的手搓项目,平均每个项目产生 127 条报错,其中 63% 来自环境配置(Python 版本冲突、CUDA 驱动不匹配)、22% 来自文档过时(官方教程用旧 API)、15% 来自逻辑错误。关键不是避免报错,而是建立高效的报错处理 SOP:

  1. 报错分类:是环境问题(E)、文档问题(D)、逻辑问题(L)?
  2. 证据固化:截图 terminal、保存pip list输出、记录nvidia-smi结果;
  3. 最小复现:删除所有无关代码,只保留报错相关 5 行;
  4. 版本锁定:用pip freeze > requirements.txt固化当前可运行环境。

有一次调试 PyTorch 分布式训练,卡在NCCL初始化失败。按 SOP,我先分类为环境问题(E),然后发现nvidia-smi显示 GPU 可用,但torch.cuda.is_available()返回 False。最小复现后,锁定到LD_LIBRARY_PATH未包含 NCCL 库路径。这个过程耗时 4 小时,但之后所有类似问题,我都能在 10 分钟内定位。手搓的价值,正在于把“未知错误”转化为“已知模式”。

5.3 误区三:“手搓是个人行为,无法规模化”

手搓常被质疑“效率低,不适合团队”。实际上,手搓的终极形态是构建可复用的手搓资产。我在上一家公司推动的“手搓基建”包括:

  • 手搓模板库(Handroll Templates):不是代码生成器,而是经过验证的手搓起点。例如“微服务鉴权模板”,包含:
    • JWT 签发/验证的手搓实现(含密钥轮换逻辑);
    • OpenAPI 3.0 规范的鉴权 securityScheme 定义;
    • Postman Collection 的测试用例(含 token 过期、签名篡改等边界 case);
    • Kubernetes Helm Chart 的 RBAC 配置示例。
    新服务接入时,工程师不是从零开始,而是基于模板手搓业务逻辑,效率提升 3 倍。

  • 手搓验证清单(Handroll Checklists):每个核心模块配套一份清单。如“数据库迁移手搓清单”:

    1. ✅ 检查 migration 文件 timestamp 是否递增;
    2. ✅ 手动执行sqlalchemy-migrate upgrade并验证 schema;
    3. ✅ 在测试库运行SELECT COUNT(*) FROM alembic_version
    4. ✅ 检查 migration 脚本是否包含op.create_index()索引语句;
    5. ✅ 验证 rollback 是否能恢复到前一版本。
      这份清单,让初级工程师也能安全执行高危操作。

手搓不是反流程,而是把隐性经验显性化、标准化。当团队每个人都有自己的手搓日志,这些日志汇聚成组织的“集体潜意识”,这才是技术团队真正的护城河。

5.4 实操避坑:五个血泪教训总结

提示:以下避坑点均来自真实项目,附带具体复现步骤和修复方案。

坑 1:Mac M1/M2 芯片上的 Python 包编译失败

  • 现象pip install numpy卡在clang: error: unsupported option '-fopenmp'
  • 根因:Apple Clang 不支持 OpenMP,而某些包默认启用;
  • 手搓修复
    # 临时禁用 OpenMP 编译 export CC=/usr/bin/clang export CXX=/usr/bin/clang++ pip install --no-binary=numpy numpy # 或安装 Apple 提供的 openmp brew install libomp export OPENMP_LIBRARY_PATH=$(brew --prefix libomp)/lib export LDFLAGS="-L$OPENMP_LIBRARY_PATH -lomp" pip install numpy

坑 2:Docker 中的时区不一致导致日志时间错乱

  • 现象:宿主机时间正确,容器内date显示 UTC,应用日志时间比实际晚 8 小时;
  • 根因:Alpine 镜像默认无 tzdata,且未挂载宿主机时区;
  • 手搓修复
    # Dockerfile 中添加 RUN apk add --no-cache tzdata ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

坑 3:Vue3 Composition API 中的响应式丢失

  • 现象:`const data = reactive({ count: 0 }); const obj = { ...data }; obj.count++ 不触发视图更新;
  • 根因:展开运算符破坏了 Proxy 响应式代理;
  • 手搓验证
    // 错误:破坏响应式 const obj = { ...data }; // 正确:保持响应式 const obj = reactive({ ...toRaw(data) }); // 或更佳:直接使用 data.count++

坑 4:Git 大文件误提交后无法清理历史

  • 现象:误提交 500MB 视频文件,git push失败,git filter-branch报错;
  • 手搓修复(BFG 方案)
    # 下载 BFG wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar # 清理历史中的大文件 java -jar bfg-1.14.0.jar --delete-files "*.mp4" my-repo.git # 强制推送 cd my-repo.git git reflog expire --expire=now --all git gc --prune=now --aggressive git push --force

坑 5:Chrome 扩展 content script 注入时机问题

  • 现象document.getElementById('app')返回 null,但元素确实存在;
  • 根因:content script 在 DOMContentLoaded 事件前注入,而 Vue/React 应用在mounted钩子后才渲染;
  • 手搓修复
    // manifest.json 中声明 run_at: "document_idle" { "content_scripts": [{ "matches": ["https://*/*"], "js": ["content.js"], "run_at": "document_idle" // 关键!等待页面空闲时注入 }] } // 或在 content.js 中监听 Vue mounted const checkVue = () => { if (window.vueApp && window.vueApp._isMounted) { // 执行操作 } else { setTimeout(checkVue, 100); } }; checkVue();

这些坑,AI 不会主动告诉你,因为它们高度依赖具体环境和版本组合。只有手搓,才能把这些碎片化的“环境特异性知识”,沉淀为可传承的经验。

6. 手搓的未来:不是回归过去,而是定义新的技术素养

手搓教程的复兴,不是技术倒退,而是对“技术素养”定义的升级。十年前,我们会说“懂 Python 就是会写脚本”;五年前,我们说“会调用 API 就是会开发”;今天,真正的技术素养必须包含三层能力:理解层(Why)、构建层(How)、治理层(What If)

  • 理解层:能说清git rebasegit merge的底层对象模型差异,而非只会背命令;
  • 构建层:能手搓一个简化版的npm install,理解node_modules的扁平化算法和 peer dependency 冲突解决;
  • 治理层:能评估当团队规模扩大时,手搓的 CI/CD 脚本是否应迁移到 Argo CD,以及迁移的 ROI 计算模型。

AI 正在接管“构建层”的重复劳动,但它无法替代人类在“理解层”的深度思考和“治理层”的价值判断。手搓教程,正是训练这三层能力的综合健身房。它不追求速度,而追求确定性;不崇拜工具,而敬畏原理;不满足于“能用”,而执着于“明白”。

我最近在带一个 AI 工程师转型做系统架构的学员。他精通 LLM 微调,但第一次接触 Kafka 时,被acks=allmin.insync.replicas=2的组合搞懵了。我让他手搓一个三节点 Kafka 集群,故意关掉一个 broker,观察 producer 的行为。当他亲眼看到acks=all时 producer 阻塞、acks=1时消息丢失、min.insync.replicas=2时集群拒绝写入,那些抽象的文档描述 suddenly clicked。那一刻,他不再需要记忆参数,而是理解了分布式系统中“一致性”与“可用性”的永恒权衡。

所以,当有人再问“为什么现在 AI 这么发达了,还要坚持手搓教程?”,我的回答是:因为 AI 能生成答案,但只有手搓,才能让你成为那个提出问题的人。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 2:20:04

Arnis 世界生成工具:把真实城市搬进 Minecraft 的完整指南

Arnis 世界生成工具:把真实城市搬进 Minecraft 的完整指南 【免费下载链接】arnis Generate any location from the real world in Minecraft with a high level of detail. 项目地址: https://gitcode.com/GitHub_Trending/ar/arnis Arnis 是一款免费开源的…

作者头像 李华
网站建设 2026/9/14 2:19:36

Codex资源建模:Plus/Pro/Credits分层使用指南

1. 项目概述:Codex 套餐不是“买会员”,而是配置你的AI工程生产力流水线Codex 这个名字,最近半年在开发者、数据工程师、自动化运维和低代码平台搭建者圈子里出现频率陡增——但它绝不是另一个“ChatGPT Plus”式的聊天会员。我去年底开始深度…

作者头像 李华
网站建设 2026/9/14 2:18:46

AI芯片设计真实生存图谱:从EDA入门到tape-out的五层断层

1. 项目概述:这不是劝退帖,而是一份“芯片设计真实生存图谱”“AI芯片设计从入门到放弃”——这个标题在技术社区里一出现,总能精准戳中一批人的神经。它不像“三天学会Python”那样浮夸,也不像“零基础转行大厂”那样带点鸡汤味&…

作者头像 李华
网站建设 2026/9/14 2:18:24

响应式可过滤展示页的工程化实践:数据模型、渲染与网格布局

简介:一份面向前端初学者和作品集创作者的迷你实战案例,由Haiyong创建并提供技术支持,目标是使用HTML、CSS与JavaScript构建一个响应式、可过滤的游戏与工具展示页面。该页面以收录100个小游戏和实用工具为方向,采用卡片式布局展示…

作者头像 李华
网站建设 2026/9/14 2:18:02

步进、闭环步进还是伺服?电机选型核心对比与实战建议

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华