跳转至

来发信谷歌地图扩展 — 版本更新规则

本文件是本项目版本更新的总规范。每次更新前请先完整阅读本文件。 最后更新:2026-05-28(v0.10.108 从 repo 根迁移到 docs/rules/)

⚠️ 特别提示:第七章「改进前自查清单」是历次踩坑后总结的,改设置页 / 调度逻辑 / 文案前必读

一、项目概况

项目 说明
名称 来发信 谷歌地图数据采集专业版(Chrome 扩展)
技术栈 WXT + React + MUI + TypeScript
包管理器 pnpm
当前版本 0.8.37(见 package.jsonversion 字段)
构建输出 dist/chrome-mv3/

二、目录结构

05_google-map/
├── 更新规则.md          ← 本文件,更新规范
├── development-log.md          ← 问题 / 解决方案 / 注意点记录
├── CLAUDE.md            ← 给 AI 的项目说明(自动遵守本规则)
├── backup/              ← 【规则1】更新前的源码备份
├── src/                 ← 源代码
├── test/                ← 已构建版本的归档
├── dist/chrome-mv3/     ← 【规则3】构建输出,加载到浏览器的就是它
├── package.json         ← 版本号在这里改
└── wxt.config.ts        ← 扩展配置

三、三条核心规则

规则 1 — 更新前必须备份

在动任何代码之前,先把当前版本完整备份到 backup/

  • 备份目录命名:backup/v<当前版本号>_<日期>/,例如 backup/v0.8.37_20260521/
  • 备份内容:src/package.jsonwxt.config.tstsconfig.json
  • 一键备份命令(在项目根目录执行,先把 0.8.37 换成当前实际版本号):
V=0.8.37; D=$(date +%Y%m%d); mkdir -p "backup/v${V}_${D}" && cp -R src package.json wxt.config.ts tsconfig.json "backup/v${V}_${D}/" && echo "已备份到 backup/v${V}_${D}"
  • 备份目录只增不删,出问题随时可以回滚。

规则 2 — 全程记录开发日志

每次更新都要在 development-log.md 写一条记录,内容包括:

  • 本次改动:这次要做 / 做了什么
  • 遇到的问题:现象 + 原因 + 解决方案
  • 注意点:下次要避开的坑、约定、易错点

最新记录放最上面。日志模板见 development-log.md 顶部。

规则 3 — 新版本输出到专门文件夹,浏览器刷新即测

  • 构建输出统一在 dist/chrome-mv3/(由 wxt.config.tsoutDir 配置,是可见文件夹)。
  • 首次加载:Chrome 打开 chrome://extensions → 右上角开启「开发者模式」→ 点「加载已解压的扩展程序」→ 选择 dist/chrome-mv3/ 文件夹。
  • 改完代码后测试
  • 方式 A(推荐):pnpm dev —— 改代码后自动重新构建并热重载,浏览器无需手动刷新。
  • 方式 B:pnpm build 重新构建 → 到 chrome://extensions 点该扩展的「刷新」图标 → 刷新网页测试。
  • 版本发布pnpm zip 打包,把产物归档到 test/

四、标准更新流程

把上面三条规则串成一套固定动作,每次更新照着走。

  1. 读本文件 + development-log.md,了解历史坑点。
  2. 【规则1】 执行备份命令,备份到 backup/
  3. 【规则2】development-log.md 顶部新建一条记录,写下本次改动目标。
  4. 修改 package.jsonversion(如 0.8.37 → 0.8.38)。
  5. 修改源代码。
  6. pnpm compile —— TypeScript 类型检查,无报错再继续。
  7. pnpm build —— 构建到 dist/chrome-mv3/
  8. 【规则3】 浏览器加载 / 刷新,实测功能。
  9. 【规则2】 把过程中遇到的问题、解决方案、注意点补写进 development-log.md
  10. (需要分发时)pnpm zip 打包,归档到 test/

五、常用命令

命令 作用
pnpm install 安装依赖
pnpm dev 开发模式,自动热重载
pnpm build 构建生产版本 → dist/chrome-mv3/
pnpm zip 打包 zip → dist/laifaxin-chajian-ditu-<版本>-chrome.zip
pnpm compile TypeScript 类型检查(不输出文件)

六、注意事项

  • 版本号只在 package.json 改,WXT 会自动同步到扩展 manifest。
  • 备份目录 backup/ 和归档目录 test/ 只进不出,不要删。
  • 每次只做一类改动,方便出问题时定位和回滚。
  • 本项目有历史 TS 报错,pnpm compile 不作为通过标准;以 pnpm build 成功出包为准。
  • 依赖用 pnpm 安装;pnpm 11+ 需根目录 pnpm-workspace.yamlallowBuilds 放行 esbuild 等构建脚本,否则 pnpm install/build 会报错退出。
  • 改了 wxt.config.ts(权限、host 等)后必须重新 build 并在扩展页重载。

七、改进前自查清单(必读 — 历次踩坑总结)

这些坑都是真实踩过的(v0.10.12 / v0.10.13 / v0.10.14 反复犯过),每次改进前花 2 分钟扫一遍能省 30 分钟返工。

🚨 A. 改「设置页 / UI 文案」前 — 反复踩坑最多

症状:望文生义命名设置项,导致与实际调度逻辑矛盾,用户读了反而误导。

铁律 1:读源码确认语义,不靠字段名猜

字段 字段名暗示 实际语义(已确认) 出处
maxConcurrentTasks "同时几个任务" activeTabs.size 全局上限,与任务数无关。1 个任务也能用满 N 个 Tab batch-controller.ts 顶部注释第 18-30 行
deepScrapeConcurrency "同时几个网站" worker 线程数全局上限。所有任务的 MapTaskData 共用一张表 engine-manager.ts getConcurrency + manageQueue
requestConcurrency "请求并发" v0.10.0 后废弃字段,调度器不读。仅保留兼容老 storage task-manager.ts 第 41 行注释
pagerConcurrency "翻页并发" 全局翻页 Fetch 并发上限,不是 per-task batch-controller.ts pumpPager
deepScrapeDomainConcurrency "单网站并发" 同一 hostname 最多同时几个 worker(防 DDoS 对方站) engine-manager.ts domainActive

铁律 2:v0.10.0 起一切都是「共享队列」架构

  • 任务(status='running' 的)→ 共享队列 ← worker 池(N 个槽位)
  • N 个槽位 = maxConcurrentTasks(地图)/ deepScrapeConcurrency(网站)
  • 任务数与槽位数完全解耦,调度器 round-robin 从所有 running 任务取 URL

改文案的 checklist: - [ ] label 是否暗示 per-task?(错)应改为「全局上限」「上限」 - [ ] helper 是否解释了「与任务数无关」? - [ ] 是否给了具体例子?(1 任务 vs 5 任务下的行为) - [ ] 改完后回看 batch-controller.ts / engine-manager.ts 顶部注释,文案与代码是否自洽?

📦 B. 加新功能 / 设置项前

铁律 3:写死的常量都是设置项的候选

任何 const FOO_BLACKLIST = [...] / const PATTERNS = [...] / 行内的 if/else 判断,问自己: - 用户可能想关掉吗?→ 加 Switch - 用户可能想加一些自定义项吗?→ 加 textarea,自动 merge 到内置 - 内置内容用户能看见吗?→ export const 出来,给「查看内置 N 项」按钮用

铁律 4:用户在某个粒度上设置 X,往往也想在更粗/更细的粒度上设置 X

  • 邮箱黑名单 → 也要支持域名级(v0.10.14 补的)
  • 单根域跟随 → 也要支持跨根域白名单(v0.10.14 补的)
  • 关键词白名单 → 顺手加黑名单(同价但不同语义)

铁律 5:默认值必须保持老版本行为(向后兼容)

新加的 boolean 字段默认 truefalse 要保持上一版的实际行为;text 字段默认 '' 走内置;不要让老用户升级后突然丢功能。

新功能 checklist: - [ ] 是否有"打包成一个函数 / 一段逻辑"导致用户没法部分关闭? - [ ] 是否有"硬编码列表"用户想改改不了? - [ ] 默认值是否 = 上一版行为? - [ ] 是否需要在更粗 / 更细粒度也提供?

🔧 C. Edit 工具操作

铁律 6:含中文的旧 JSX,不要手写 old_string 猜全角半角

源码里这些字符常常是全角: - () U+FF08/FF09(不是 ASCII ()) - U+FF1A(不是 ASCII :) - U+FF0C(不是 ASCII ,) - U+3002(不是 ASCII .) - U+FF1B(不是 ASCII ;

症状:Edit 报 String to replace not found

正确做法(按性价比排序): 1. 小改动:先 awk 'NR==<行号>' 把目标行打印出来直接复制粘贴 2. 大块替换:用 Python 切片,根据唯一锚点定位区间,整体替换:

cat > /tmp/new.txt << 'EOF'
... 新内容 ...
EOF

python3 << 'PYEOF'
from pathlib import Path
p = Path("src/path/to/file.tsx")
src = p.read_text(encoding='utf-8')
start = src.find("唯一锚点1")
end = src.find("唯一锚点2", start)
new_block = Path("/tmp/new.txt").read_text(encoding='utf-8')
p.write_text(src[:start] + new_block + src[end:], encoding='utf-8')
PYEOF
  1. 从不:连续猜 3 次 ASCII vs 全角 —— 浪费 token。

🗄️ D. storage / 数据兼容

铁律 7:storage 字段加只能加,不要删

  • 废弃字段 → 加 // v0.x.y deprecated, no longer read 注释,保留在 interface
  • 改语义 → 加新字段,老字段保留(写迁移代码可选)
  • 删了字段 → 老用户升级时 storage 里仍有,新代码读出来是 undefined,可能引发崩溃

🧪 E. 验证标准

  • pnpm build 必须过(生产构建出包 = 真验证)
  • pnpm compile 仅查"新增的 TS 错误",已有历史错误不算
  • 改了 settings UI 必须在浏览器实际打开看一眼(文字溢出 / 换行 / 折叠是否对)
  • 改了调度逻辑必须用 2+ 个任务跑一遍,验证共享队列行为(不是只跑 1 个任务)

🗒️ F. 历次大事件(避免重蹈)

版本 事件 教训
v0.10.0 多任务调度重写,从 per-task 改共享队列 这是基线架构,所有"并发"字段都是全局
v0.10.12 maxConcurrentTasks label 写成"= 任务数" 改文案前没读 batch-controller.ts 顶部注释
v0.10.13 scraper.ts 把 7 类规则打包写死 用户能想关的逻辑,全部独立开关 + 可编辑
v0.10.13 Edit 中文标点踩坑(3 次) Python 切片代替 old_string
v0.10.14 再次把 deepScrapeConcurrency 写成"网站数" 与 v0.10.12 同坑 → 必须查源码
v0.10.15 长跑后浏览器累积孤儿 Tab 卡死 引入 watchdog(5min 巡检 + 3 次累积自动重启)
v0.10.16 popup loading 状态死循环(无超时) 铁律 8:任何"等异步结果"的 loading 必须有超时 + 兜底入口,否则 = 用户卡死无法操作
v0.10.17 主面板登录按钮 force=false 跳过开窗 铁律 9:「主动点击」≠「静默尝试」,用户点击的入口必须 force=true 绕过缓存
v0.10.18 filter chip 视觉变了但 jsstore 查询是空 铁律 10:UI 视觉与业务查询必须同步。"TODO: 暂留空"是定时炸弹
v0.10.19 列宽 stale closure + columns prop 引用不稳定 铁律 11:setState 用 functional 避免 stale;prop 传数组/对象用 useMemo 保稳定
v0.10.20 useMemo deps 含 object/array 但调用方传不稳定引用 → memo 形同虚设 铁律 12:useMemo deps 含 array/object 时,用字符串签名 arr.map(...).join(',') 做稳定 key;不能假设调用方一定 useMemo
v0.10.20 hasEmailOnly 在 taskId 分支「先分页再过滤」分页错乱 铁律 13:分页 + 过滤必须「先过滤再分页」,反过来数学上不等价
v0.10.20 修 ISSUE-0010 写了 useMemo 但没回到浏览器实测 → 没修到根 铁律 14:写 fix 后必须实测复现。"修了"≠"修好了",必跑 happy path 验证
v0.10.21 设置页双滚动条 v0.10.18 修不彻底,二次撞 → 父级 main-layout 已 overflow:auto,settings 又加 overflow → 双条 铁律 15:修嵌套滚动 bug 必须查父级链。每一层父级的 overflow / height 都要看清楚
v0.10.21 同上 — 子组件不应假设自己是页面根 铁律 16:「让父级负责滚动」是更稳的模式。子组件高度自适应内容,比硬撑高度自滚动更不易与父级冲突

📋 改进流程的扩展版

把第四章「标准更新流程」第 1 步细化为:

  1. 读源码语义(A 节)—— 任何要改的设置/调度,先打开对应源码看顶部注释和实际逻辑
  2. 扫本清单(A→E)—— 2 分钟
  3. development-log.md 最近 3 条记录,避免和最近的改动冲突
  4. 然后才开始改代码