ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

用预算闸门守护体积:motion 仓库 bundle-size 预算强制执行方案(Plan 035)深度解读

用预算闸门守护体积:motion 仓库 bundle-size 预算强制执行方案(Plan 035)深度解读 前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载本文以 plans 目录下的执行计划 plans/035-bundle-size-budget-enforcement.md 为骨架结合仓库中实际的预算检查脚本、package.json 脚本与 CircleCI 配置源码完整呈现 motionframer-motion motion-dom如何把包体积预算从一条无人执行的规则升级为 CI 与发布流程中不可绕过的阻塞闸门并重新校准基线。读完本文你将掌握体积检查脚本的工作原理、预算如何被定义与度量、prepack 与 CI 两条阻塞链路如何接线以及预算棘轮ratchet这一防回归机制的运作方式。对 motion 这样的动画库来说交付的字节数本身就是头号卖点。仓库早已具备相当成熟的体积工具链按入口粒度打包的rollup.size.config.mjs、每个包package.json#bundlesize中声明的 gzip 预算以及由dev/inc/bundlesize.mjs执行的预算核对脚本。然而在 Plan 035 诞生之前没有任何东西真正运行过这个检查CircleCI 上不存在 measure 任务发布路径prepare/prepack也只跑 rollup 的measure任务而不会调用预算脚本。结果是在计划基线提交42bfbe3ed上实测9 条预算中有 6 条处于失败状态主入口motion.div包从 v12.23.242025 年 10 月的 35.65 kB gz 漂移到了 38.56 kB gz10%整个过程没有任何信号暴露问题。预算上一次校准还停留在 2025 年 4 月提交596e0eee8。Plan 035 正是为此而设把预算重新校准到现实基线并把既有的检查脚本接入 CI 与发布流程让未来的体积增长变成刻意决策而非无声漂移。现状盘点工具齐全唯独无人执行Plan 035 首先对仓库的体积治理现状做了一次精确盘点逐条列出相关文件与脚本的当前行为这些都可以在仓库源码中逐一印证dev/inc/bundlesize.mjs—— 预算检查器本体。它读取每个包的package.json#bundlesize数组对其中列出的每个 dist 文件做 gzipzlib 默认压缩级别任何一条超标即以退出码 1 终止。关键限制在于它基于process.cwd()解析路径见 bundlesize.mjs 的packagePath拼接以及 bundlesize.mjs 的fullPath拼接因此今天它只可能在仓库根目录下运行。它还接受一个可选的包名参数framer-motion或motion-dom。根package.jsonpackage.json 的measure: turbo run measure --force node dev/inc/bundlesize.mjs是全仓库唯一调用预算检查的地方但没有任何东西去触发它package.json 的prepare: turbo run build measure只跑各包的 rollupmeasure任务不跑检查。packages/framer-motion/package.jsonprepack 为yarn build yarn measure无检查measure 为rollup -c ./rollup.size.config.mjs。packages/motion-dom/package.json有measurepackage.json但完全没有prepack。.circleci/config.yml现有setup/test/test-react/test-react-19/test-html五个任务。setup执行yarn install --immutable后yarn build并持久化整个 workspace其余任务一律attach_workspace且requires: setup。不存在 measure/体积任务。全文件使用 4 空格 YAML 缩进config.yml。基线实测数据提交42bfbe3ed计划作者在基线提交上通过yarn build yarn measure拿到了 9 条预算的实测对照表这是理解重校准意义的直接证据Bundle实测kB gz预算状态framer-motion size-rollup-motion.js38.5634.9❌framer-motion size-rollup-m.js6.316❌framer-motion size-rollup-dom-animation.js13.5817.85✅过松framer-motion size-rollup-dom-max.js26.8629.8✅过松framer-motion size-rollup-animate.js21.6119.1❌framer-motion size-rollup-scroll.js6.185.2❌framer-motion size-rollup-waapi-animate.js3.152.26❌motion-dom size-rollup-style-effect.js3.102.9❌motion-dom size-rollup-motion-value.js1.701.8✅这张表暴露了两个方向的失真一部分预算motion、m、animate、scroll、waapi-animate、style-effect因体积漂移而被击穿另一部分dom-animation、dom-max则因历史原因过松宽松预算同样失去了约束意义。因此重校准的方向不是简单放水而是所有条目一律收紧到当前实测水平。度量链路是怎样工作的从 size rollup 到 gzip 核对要理解预算闸门先要看清数字从哪来。仓库里存在两条前后衔接的链路第一步按入口生成 size bundle。两个包的rollup.size.config.mjs定义了体积专用构建framer-motion/rollup.size.config.mjs 声明了 7 个体积包入口都指向lib/下tsc 编译产物的真实源码入口例如motionlib/render/components/motion/size.js→dist/size-rollup-motion.jsmlib/render/components/m/size.js→dist/size-rollup-m.jsanimatelib/animation/animate/index.jsscrolllib/render/dom/scroll/index.jswaapi-animatelib/animation/animators/waapi/animate-style.jsdom-animation/dom-max以lib/render/dom/features-animation.js和features-max.js为多入口、共享 chunk 输出motion-dom/rollup.size.config.mjs 声明 2 个体积包lib/value/index.js→size-rollup-motion-value.js、lib/effects/style/index.js→size-rollup-style-effect.js。所有 size bundle 共用同一套体积插件管线resolve()解析依赖→replaceSettings(production)生产环境替换→terser压缩去注释并把react、react-dom、react/jsx-runtime列为 external。这模拟的是真实用户打包器如 webpack对该入口做生产构建时的产出规模而非库内 dist 的原始体积。第二步预算脚本做 gzip 核对。bundlesize.mjs 对每个产物执行zlib.gzip默认级别 ≈ level 6计算 gzip 后字节数与maxSize单位 kB内部乘以 1024 换算字节比较超标输出❌ package/file is X kB (Y allowed)并累计失败最终process.exit(1)未超标输出✅。该脚本的包选择逻辑见 bundlesize.mjs无参数时检查[framer-motion, motion-dom]两个包传参时只查单个包。值得注意的细节脚本的 gzipzlib 默认级别读数比 CLIgzip -9大约小 0.4%因此预算必须锚定脚本的读数而不是命令行 gzip 的读数——这是校准口径统一的关键详见计划维护说明。分步执行从脚本改造到双链路接线Plan 035 的全部改动收敛在 5 个文件范围内其余文件即使看起来相关也明确列为 out of scope例如两个包各自的rollup.size.config.mjs——测量本身没有问题以及任何源码文件——本计划只改变流程不改变字节体积回收由 Plan 036/037/038 负责。Step 1让bundlesize.mjs与工作目录解耦当前脚本用process.cwd()拼路径导致它只能在仓库根目录运行。要让prepack阶段能在包目录下被调用必须把路径解析改为基于脚本自身位置import { fileURLToPath } from url const repoRoot fileURLToPath(new URL(../.., import.meta.url))在 bundlesize.mjs 顶部加入上述代码后把两处process.cwd()约第 15 行的packagePath拼接与约第 37 行的fullPath拼接全部替换为repoRoot。验证命令从仓库根目录node dev/inc/bundlesize.mjs framer-motion应打印逐包体积表预算尚未重校准退出码 1 属预期同时cd packages/framer-motion node ../../dev/inc/bundlesize.mjs framer-motion必须打印同一张表——这证明了 cwd 无关性。Step 2把全部预算重校准到当前实测 × 1.01在仓库根目录运行yarn build yarn measuremeasure 步骤会以退出码 1 结束——此时应当读取打印出的实测值。对 framer-motion/package.json 和 motion-dom/package.json 中bundlesize数组的每一条将maxSize设为实测值 × 1.01向上取整到最接近的 0.05 kB。计划给出的预期落点motion 39、m 6.4、dom-animation 13.75、dom-max 27.15、animate 21.85、scroll 6.25、waapi-animate 3.2、style-effect 3.15、motion-value 1.75。这里有个工程上的重要约定必须使用你自己构建产出的实测值而不是计划表格里的数字——工具链噪声造成 ±0.05 kB 的波动完全正常照抄历史表格反而会引入新的偏差。验证命令node dev/inc/bundlesize.mjs→ 全部 ✅退出码 0。Step 3用 prepack 闸住发布流程npm 在打包上传前会自动执行包的prepack脚本这是发布链路中天然的最后防线。计划的接线方式packages/framer-motion/package.json的prepack改为prepack: yarn build yarn measure node ../../dev/inc/bundlesize.mjs framer-motionpackages/motion-dom/package.json新增prepack: yarn build yarn measure node ../../dev/inc/bundlesize.mjs motion-dom由于 Step 1 已经让脚本 cwd 无关这条命令才能在包目录下工作。注意measure在此执行的是包的 rollup 体积构建随后立刻被预算核对任何一条预算超标都会让prepack以非零码失败从而阻止yarn publish继续——体积失控的版本根本发不出去。验证命令cd packages/motion-dom yarn prepack→ 退出码 0先构建、后打印 ✅ 行framer-motion 同理。Step 4新增阻塞性 CircleCImeasure任务在 .circleci/config.yml 中仿照既有test任务保持文件的 4 空格缩进加入独立任务measure: docker: - image: cimg/node:20.11.1-browsers working_directory: ~/repo resource_class: large steps: - attach_workspace: at: ~/repo - run: name: Check bundle sizes command: yarn measure并在workflows: build: jobs:下挂载依赖- measure: requires: - setup这里的关键设计setup任务在执行yarn build后持久化了整个 workspacelib/tsc 产物size rollup 的消费输入已经就位因此measure任务只需attach_workspace并执行yarn measure——它只重跑 size rollup 加预算核对无需重新全量构建耗时可控。任务独立成 job 而非并入test是为了让体积回归这一信号在 CI 面板上单独可见、失败定位清晰计划的维护说明也提到若 CircleCI 分钟数成为顾虑可将其折叠进test任务作为额外步骤只是信号清晰度会下降。验证命令python3 -c import yaml; yaml.safe_load(open(.circleci/config.yml))→ 退出码 0确认 YAML 语法合法。执行纪律测试计划、完成标准与 STOP 条件作为一份面向执行者的操作计划Plan 035 定义了明确的质量闭环测试计划本计划属于构建/流程类工具链改造不涉及单元测试——每一步的验证命令本身就是测试。最终端到端检查在干净的无git stash残留工作树上运行yarn build yarn measure→ 退出码 0、每一行 ✅。完成标准全部可机器核验yarn measure退出码 0 且全部行 ✅cd packages/framer-motion node ../../dev/inc/bundlesize.mjs framer-motion退出码 0cwd 无关性grep -c bundlesize.mjs packages/framer-motion/package.json packages/motion-dom/package.json各返回 1prepack 已接线grep -c measure: .circleci/config.yml≥ 1 且 YAML 可解析git status确认没有超出 in-scope 清单的文件被改动plans/README.md 中的状态行已更新。STOP 条件触发即停止上报不得自行发挥改动任何东西之前yarn build就失败——说明基线已损坏重校准会把垃圾数字编码进预算某个实测体积与计划表格偏差超过 1 kB gz双向——说明计划与执行之间存在其他落地变更如cleanup/strip-unused-stats分支或 Plan 036/037此时应重读plans/README.md、针对新现实重新校准并记录Plan 007 已对同一个workflows:块落地冲突改动且合并不是机械式——需要人工仲裁。预算即棘轮这套机制的长期意义计划在维护说明中给出了这套治理体系最核心的长期契约预算从此成为棘轮ratchet。后续的 Plan 036/037/038分别针对 dev 警告 DCE、scale corrector 泄漏、motion-dom 重模块减重每一步收尾时都会重新收紧它们所改善的预算任何合法地让某个包变大的 PR必须在同一提交内上调对应预算——这一行 diff 就是本计划存在的意义它是评审者识别体积增长的第一信号。重校准的数值并不代表背书。重新校准为主 motion 包带来了约 3.7 kB gz 的历史漂移豁免这笔债由 Plan 036/037/038 负责偿还新预算不应被当作被认可的目标体积。gzip 口径差异脚本 zlib 默认级别≈ level 6读数比gzip -9小约 0.4%预算以脚本为准、不以 CLI 为准。CI 成本的折中measure独立成 job 是为了信号清晰若 CircleCI 分钟数紧张可合并进test任务。此外仓库中与体积治理同级的既有防线还包含 check-bundle.js它负责类型导出自包含、CJSm包不内联自己的LazyContext等结构性校验——体积预算闸门与这些类型/结构闸门共同构成 framer-motion 发布前的完整质量网。结语Plan 035 的完整价值可以概括为一句工程哲学度量本身不是治理只有把度量变成不可绕过的关卡治理才算生效。它通过四个精确步骤——脚本 cwd 无关化、预算重校准实测 ×1.01 向上取整、prepack 发布闸、CircleCI 阻塞任务——把 motion 仓库从6/9 预算静默失败修复为任何体积回归都在合并或发布前被机器强制拦截并以棘轮契约保证这条防线在未来的每一次体积优化中持续收紧而非松弛。对任何以体积即特性为卖点的库而言这套从工具、校准、接线到纪律的完整范式都值得直接借鉴。赞分享前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载相关推荐Front-End-Checklist 性能预算实战用 size-limit 与 Lighthouse CI 在 CI 中强制执行Front End Checklist 性能预算实战用 size limit 与 Lighthouse CI 在 CI 中强制执行 性能预算是作用于可度量指标OpenHuman 成本追踪与预算强制模块解析JSONL 用量记账、每日/月度预算闸门与 7 天成本看板OpenHuman 成本追踪与预算强制模块解析JSONL 用量记账、每日/月度预算闸门与 7 天成本看板 OpenHuman 是一个面向 Mac、Window人工智能AI 应用本地部署AI Agent交互助手深度研究gh_mirrors/we/webpack项目的性能预算bundle体积控制策略gh_mirrors/we/webpack项目的性能预算bundle体积控制策略 1. 性能预算前端工程的隐形红线 你是否曾遇到过这样的困境开发环境流畅的前端示例工程前端构建上一篇AIRI 接入 Azure AI Foundry聊天模型提供者配置与源码解析下一篇用 Ollama 本地运行 Qwen 系列模型从一条命令拉取到自定义 GGUF 与工具调用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表