尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

motion 仓库 Plan 037 实战:让布局动画的 scale correctors 不再泄漏进最小 `m` bundle

发布时间:2026/9/30 1:49:32

资讯中心
01
ARTICLE

motion 仓库 Plan 037 实战:让布局动画的 scale correctors 不再泄漏进最小 `m` bundle

motion 仓库 Plan 037 实战:让布局动画的 scale correctors 不再泄漏进最小 `m` bundle
前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载导读本文围绕 motion 仓库A modern animation library for React and JavaScript中的实施计划 plans/037-scale-correctors-tree-shake-leak.md 展开深入剖析一个典型的死代码泄漏进最小产物问题m是 size-first 的极简 motion 组件但核心渲染路径静态引入了布局动画专用的scaleCorrectors注册表导致默认的 borderRadius/boxShadow 修正器约 780 B min / 0.3 kB gz被打进每一个m.div。读完本文你将掌握问题的完整成因与影响量化、addScaleCorrector注册机制的底层原理、五步实施方案的每一步代码与验证命令以及sideEffects: false下的 tree-shaking 陷阱和后续维护约定。背景m的 size-first 设计与超预算现状在 motion 仓库中m是最小化的 motion 组件入口对应 packages/framer-motion/src/m.ts其设计哲学是动画、拖拽、布局等高级功能应当通过LazyMotion或 feature 包按需加载而不是常驻核心包。然而在计划起草时commit42bfbe3ed2026-06-11mbundle 已经超出其 6 kB 的预算实测 6.31 kB gz而预算超标的根源之一正是本文主角布局动画layout animation的 scale correctors 被核心渲染路径急切构造并打进了最小产物。这一发现来自仓库plans/README.md中记录的 2026-06-11 交互式 bundle-size audit计划 035–038 四连击其中Plan 035将 bundle-size 预算变成阻断性 CI gatePlan 036修复process.env?.NODE_ENV破坏 dev warning DCE 的问题Plan 037本文主题停止 scale correctors 泄漏进mPlan 038对 motion-dom 最重的非竞争模块做一次文件体积审计。该 audit 的基线测量结论是motion.divbundle 为 38.56 kB gz远超 34.9 kB 预算9 个预算中 6 个不达标且没有任何机制强制它们。Plan 037 属于该系列中的 P2中优先级、S–M小到中工作量、MED-LOW中低风险的一环。问题根源一条静态导入链把布局代码拖进核心图从isForcedMotionValue到scaleCorrectors计划文档锁定的核心文件是 packages/motion-dom/src/render/utils/is-forced-motion-value.ts。该文件用于判断某个属性键是否属于被强制驱动的 motion value——即无论外部如何设置都要由动画系统接管的值。其逻辑如下import { transformProps } from ./keys-transform import type { MotionNodeOptions } from ../../node/types import { scaleCorrectors, addScaleCorrector, } from ../../projection/styles/scale-correction export { scaleCorrectors, addScaleCorrector } export function isForcedMotionValue( key: string, { layout, layoutId }: MotionNodeOptions ) { return ( transformProps.has(key) || key.startsWith(origin) || ((layout || layoutId ! undefined) (!!scaleCorrectors[key] || key opacity)) ) }注意第 3–6 行的静态导入isForcedMotionValue无条件引用了scaleCorrectors和addScaleCorrector。由于isForcedMotionValue是核心渲染路径的一部分它会被打进每一个使用 motion 的 bundle——包括最小化的m。症结注册表模块急切构造默认修正器真正的问题出在 packages/motion-dom/src/projection/styles/scale-correction.ts。该模块在模块作用域急切地构造了默认的 borderRadius/boxShadow 修正器import { isCSSVariableName } from ../../animation/utils/is-css-variable import { cornerRadiusProps } from ../../utils/border-radius import { correctBorderRadius } from ./scale-border-radius import { correctBoxShadow } from ./scale-box-shadow import type { ScaleCorrectorMap } from ./types export const scaleCorrectors: ScaleCorrectorMap { borderRadius: { ...correctBorderRadius, applyTo: [...cornerRadiusProps], }, borderTopLeftRadius: correctBorderRadius, borderTopRightRadius: correctBorderRadius, borderBottomLeftRadius: correctBorderRadius, borderBottomRightRadius: correctBorderRadius, boxShadow: correctBoxShadow, } export function addScaleCorrector(correctors: ScaleCorrectorMap) { for (const key in correctors) { scaleCorrectors[key] correctors[key] if (isCSSVariableName(key)) { scaleCorrectors[key].isCSSVariable true } } }注当前仓库源码中borderRadius.applyTo使用共享常量cornerRadiusProps其定义在 packages/motion-dom/src/utils/border-radius.ts注释说明这是为了让 projection mixer、scale corrector、WAAPI px-value set 与 view-transition crop pass 共享同一份四个圆角 longhand 列表避免各自携带副本。于是形成了一条危险的依赖链isForcedMotionValue (核心渲染路径) └─ scaleCorrectors (急切初始化) ├─ correctBorderRadius ── pixelsToPercent └─ correctBoxShadow ──── complex 解析器 mixNumber其中correctBorderRadius定义在 packages/motion-dom/src/projection/styles/scale-border-radius.ts它通过pixelsToPercent把像素圆角换算成相对目标盒各轴尺寸的百分比从而在布局投影过程中避免逐帧重绘correctBoxShadow定义在 packages/motion-dom/src/projection/styles/scale-box-shadow.ts它用complex.parse解析 shadow 值按 x/y 轴缩放偏移、按平均缩放修正模糊blur与扩散spread量再通过complex.createTransformer重组字符串。为什么这些代码在m里是死代码关键在于这些 corrector 只有投影节点projection node会读取而投影系统根本不在m的依赖图里。从源码看packages/motion-dom/src/projection/node/create-projection-node.ts 第 28 行import { scaleCorrectors } from ../../render/utils/is-forced-motion-value并在文件后部计划文档标注约 2092–2095 行迭代它const { correct, applyTo, isCSSVariable } scaleCorrectors[key]packages/motion-dom/src/render/html/utils/scrape-motion-values.ts 通过isForcedMotionValue消费它——这条路径正是把它拖进m的元凶packages/framer-motion/src/render/html/use-props.ts 同样通过isForcedMotionValue消费。而投影节点如何进入 bundlepackages/framer-motion/src/motion/features/layout.ts与packages/framer-motion/src/motion/features/drag.ts都导入HTMLProjectionNode其工厂函数就位于create-projection-node.ts。因此可以推断出干净的边界能够真正运行布局动画的 bundledomMax、完整motion、vanilla 的animateLayout必然包含create-projection-node.ts不能运行布局动画的 bundle单独的m、domAnimation必然不包含它。换句话说在模块作用域急切构造默认 corrector这一行为把本应只属于投影系统的代码硬生生拉进了所有 bundle。影响量化~780 B min 的死代码计划文档给出了 source-map 归属分析针对42bfbe3ed时的dist/size-rollup-m.js模块体积scale-box-shadowbox-shadow 解析 投影数学312 B minscale-correction注册表模块本身245 B minscale-border-radius222 B min合计≈ 780 B min约 0.3 kB gz对一个预算 6 kB、当前 6.31 kB 的产物而言这 ~0.3 kB gz 的回收足以让m重回预算线内——这正是计划很可能把它拉回预算之下的依据。解决方案设计把急切构造改为按需注册计划的修复思路并不复杂但非常讲究注册表保持导出但初始化为空对象把默认 corrector 的字面量平移到一个新文件default-scale-correctors.ts在create-projection-node.ts投影系统真正存在的模块模块作用域内显式调用addScaleCorrector(defaultScaleCorrectors)完成注册。这样谁能运行布局动画谁就注册默认 corrector从结构上被保证了投影节点在注册就在投影节点不在m注册就不存在而空注册表只是一个空对象字面量可以被完全摇掉。关键设计约束sideEffects: false下的注册必须是调用表达式这是一个反直觉但至关重要的细节。packages/motion-dom/package.json 声明了sideEffects: false。这意味着 bundler 有权完全丢弃那些只有副作用、没有导出的裸导入语句——例如import ./defaults。因此注册动作不能写成裸副作用导入而必须是create-projection-node.ts中紧接 import 块之后的一行顶层调用表达式addScaleCorrector(defaultScaleCorrectors)理由create-projection-node.ts是因为它的导出HTMLProjectionNode工厂被包含进 bundle 的模块内部的顶层调用表达式属于可执行代码而非可丢弃的导入在sideEffects: false语义下不会被移除。计划文档明确强调不要使用裸副作用导入否则 bundler 有权把整行扔掉默认 corrector 将永远不被注册布局动画的 borderRadius/boxShadow 修正会静默失效。公共 API 保持不变计划刻意保持导出面不动只改变注册表在哪里被填充addScaleCorrector已从 packages/motion-dom/src/index.ts 导出当前源码第 211 行并经 packages/framer-motion/src/index.ts 与 packages/framer-motion/src/projection.ts 二次导出scaleCorrectors本身也从motion-dom/src/index.ts当前第 213 行导出。两者在修复后必须继续正常工作——用户例如注册自定义 CSS 变量 corrector依赖这个公开注册机制。五步实施方案附代码与验证计划文档给出了完整的执行流程以下是每一步的代码与验证要点。Step 1清空急切注册表修改 packages/motion-dom/src/projection/styles/scale-correction.ts删除correctBorderRadius/correctBoxShadow导入把scaleCorrectors初始化为空映射export const scaleCorrectors: ScaleCorrectorMap {}addScaleCorrector保持原样一行不动。注意is-forced-motion-value.ts不在修改范围——其逻辑与 re-export 在注册表以空态起步后依然正确空表时!!scaleCorrectors[key]为false行为退化为纯transformProps/origin/opacity判断。Step 2新建default-scale-correctors.ts创建 packages/motion-dom/src/projection/styles/default-scale-correctors.ts计划文件路径packages/motion-dom/src/projection/styles/default-scale-correctors.ts把旧的字面量平移过来import { correctBorderRadius } from ./scale-border-radius import { correctBoxShadow } from ./scale-box-shadow import type { ScaleCorrectorMap } from ./types export const defaultScaleCorrectors: ScaleCorrectorMap { borderRadius: { ...correctBorderRadius, applyTo: [ /* 四个圆角 longhand与旧 map 逐字一致 */ ], }, borderTopLeftRadius: correctBorderRadius, borderTopRightRadius: correctBorderRadius, borderBottomLeftRadius: correctBorderRadius, borderBottomRightRadius: correctBorderRadius, boxShadow: correctBoxShadow, }计划强调applyTo数组必须与旧 map 逐字一致即borderTopLeftRadius、borderTopRightRadius、borderBottomLeftRadius、borderBottomRightRadius四者可复用 packages/motion-dom/src/utils/border-radius.ts 的cornerRadiusProps。Step 3从投影节点注册修改 packages/motion-dom/src/projection/node/create-projection-node.ts扩展第 28 行的导入addScaleCorrector与scaleCorrectors从同一模块 re-export再导入defaultScaleCorrectors并在 import 块之后恰好加一行addScaleCorrector(defaultScaleCorrectors)要求必须是顶层调用表达式见上文sideEffects: false约束对create-projection-node.ts的改动只允许是 import 块加这一行——该文件高达 2,465 行计划称其为 god module91 倍于仓库中位数且存在进行中的在飞改动PR #3748 /worktree-style-effect领域diff 必须保持机械最小化。验证yarn build→ exit 0。Step 4证明泄漏消失且行为完好构建后依次执行验证目的命令成功预期体积测量yarn measure构建后m下降约 0.25–0.3 kB gz泄漏 grepgrep -c borderTopLeftRadius packages/framer-motion/dist/size-rollup-m.js0修复前为1完整包仍注册grep -c borderTopLeftRadius packages/framer-motion/dist/size-rollup-motion.js≥1domMax 仍注册grep -c borderTopLeftRadius packages/framer-motion/dist/size-rollup-dom-max.js≥1motion-dom 单测cd packages/motion-dom yarn test全部通过布局定向 jestnpx jest --config packages/framer-motion/jest.config.json --testPathPatternlayout全部通过CLAUDE.md memory 中列出的两个既有 JSDOM 局限失败可忽略但需先在干净 checkout 上确认其以同样方式失败投影 E2Emake test-html所有 Cypress HTML projection spec 通过计划特别指出make test-html是真正的门禁多个 fixtures例如shared-mix-finish.html会通过投影系统动画 borderRadius如果注册点放错位置这些 fixture 会直接暴露渲染未被修正的问题。Step 5条件性收紧m预算仅当 Plan 035预算阻断 gate已完成时执行运行yarn measure把size-rollup-m.js及其余改善项的预算设为 actual × 1.01 并向上取整到最近的 0.05 kB。验证node dev/inc/bundlesize.mjs framer-motion→ exit 0。测试计划用最小导入断言空注册表契约计划的测试扩展聚焦 packages/motion-dom/src/projection/styles/tests/scale-correction.test.ts新增三组断言空表断言只导入scale-correction模块时scaleCorrectors不包含任何默认 key。注意一个陷阱如果测试文件传递性地加载了任何会触发create-projection-node的模块注册就会抢先执行所以该用例的导入必须保持最小注册断言addScaleCorrector(defaultScaleCorrectors)后borderRadius含 4 项applyTo、四个圆角 key、boxShadow全部被填充——这把平移后的字面量锁死防止将来漂移既有用例不动现有的correctBorderRadius/correctBoxShadow用例保持原样。这里有一个巧妙之处现有测试直接导入correctBorderRadius/correctBoxShadow而非经过 map所以即使注册表清空这些用例也完全不受影响——计划文档明确预判了它继续通过、无需改动。验证npx jest --config packages/motion-dom/jest.config.json --testPathPatternscale-correction→ 全部通过含 ≥2 个新用例。完成标准与 STOP 条件可机器检查的 Done criteria计划要求全部成立yarn build退出码 0grep -c borderTopLeftRadius packages/framer-motion/dist/size-rollup-m.js→ 0grep -c borderTopLeftRadius packages/framer-motion/dist/size-rollup-dom-max.js→ ≥1motion-dom framer-motion 布局 jest 套件通过scale-correction 套件含新用例make test-html通过dist/size-rollup-m.jsgz ≤ 6.1 kB按node dev/inc/bundlesize.mjs framer-motion输出除 in-scope 列表外无任何文件被修改git statusplans/README.md状态行已更新。三个 STOP 条件遇则停止上报不得自行发挥投影/布局测试出现 borderRadius 或 boxShadow 渲染未修正例如布局动画期间圆角被视觉性拉伸——说明某个具备投影能力的入口图没有包含create-projection-node.ts注册点放错了上报是哪个入口而不是四处撒注册调用LazyMotion 测试在 forced motion values 上失败异步 feature 加载时首帧渲染用空注册表刮取 props加载完成后的 re-render 必须重新刮取。若测试显示 domMax 加载后 borderRadius 仍停留在普通 style说明该路径的 re-render 不会重刮——上报不得绕行修补create-projection-node.ts在 HEAD 上围绕 import 块存在冲突的在飞改动PR #3748 /worktree-style-effect领地使这一行添加不再机械。Scope 边界什么不能碰计划对看起来相关但绝不能动的文件做了明确区分is-forced-motion-value.ts逻辑与 re-export 在空表起步后均正确不改scale-box-shadow.ts/scale-border-radius.ts内部实现Plan 008 拥有scale-box-shadow.ts的单次解析重构本计划只改map 在哪里被填充motion-dom/src/index.ts与 framer-motion 导出面无任何导出变更需要create-projection-node.ts其余 2,400 行在飞工作正触碰它diff 必须压缩到 import 块加一行。in-scope 文件仅四个scale-correction.ts、新建的default-scale-correctors.ts、create-projection-node.ts仅 import 一行注册、scale-correction.test.ts扩展。维护约定长期契约与协作计划文档以契约形式固化了两条长期约束核心渲染代码可以 READscaleCorrectors但永远不得填充它只有投影 feature 代码或用户经公开addScaleCorrector才能注册 corrector。未来任何isForcedMotionValue改动若重新导入 corrector 实现都会静默复现泄漏——因此对size-rollup-m.js的grep完成标准是廉价的 reviewer 检查计划建议在回归过一次后把它收编进scripts/check-bundle.js与 Plan 036 的断言并列与 Plan 008 的协作008 改 corrector内部实现单次解析037 改其注册位置两者文件不重叠无论谁后落地后落地方重跑 scale-correction jest 套件即可无其他交互。最后计划预告effects/VisualElement 统一worktree-style-effect即 PR #3749最终会给这个机制一个更干净的家——按 feature 作用域注册本计划的形态defaults 模块 注册表可以直接移植过去。也就是说这次修复不仅是 0.3 kB 的回收更是为未来按功能切分注册时机铺路的一次结构性调整。小结Plan 037 是一个教科书级的 tree-shaking 泄漏修复案例它揭示了一条核心渲染路径 → 急切初始化的注册表 → 布局专用解析器的隐性依赖链通过把注册时机从导入即构造推迟到投影节点所在模块显式调用在不动任何公共 API、不改任何 corrector 内部实现的前提下回收了mbundle 约 0.3 kB gz 的死代码。其背后的sideEffects: false陷阱、STOP 条件驱动的执行纪律、以及核心只读、投影才注册的长期契约对任何维护多产物、多入口、预算敏感的动画库或组件库都有直接借鉴意义。相关实施细节与验证命令均可在此计划的源文档 plans/037-scale-correctors-tree-shake-leak.md 及上述源码路径中继续追查。赞分享前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载相关推荐Motion库安全最佳实践防范XSS与动画相关漏洞Motion库安全最佳实践防范XSS与动画相关漏洞 在现代Web应用开发中动画效果是提升用户体验的重要手段但也可能成为安全隐患的入口。Motion库作为R前端UI组件Tamagui Motion 动画驱动缺陷修复方案Motion Driver Bug Fixes PlanTamagui Motion 动画驱动缺陷修复方案Motion Driver Bug Fixes Plan 导读本指南基于 Tamagui 仓库内的 fiUI组件设计系统前端跨平台如何在5分钟内创建专业AI视频OpenMontage开源视频制作系统完全指南如何在5分钟内创建专业AI视频OpenMontage开源视频制作系统完全指南 你是否想过普通人也能像专业导演一样制作高质量视频OpenMontage开源智人工智能AI Agent音视频媒体生成工作流自动化上一篇Stable Diffusion 三采样器实测20步够不够DDIM和PLMS差在哪下一篇Jetpack Compose实战SociaLite聊天界面开发完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。