正确使用 requestAnimationFrame 需解耦调度与逻辑,避免在 update 内重复调用导致帧率失控,并在 React/Vue 中结合 useRef 和 useEffect/onUnmounted 清理帧请求以防内存泄漏。

动画帧调度不是靠 HTML 实现的
HTML 本身不提供动画帧调度能力, 常见错误是直接在 Vue 的响应式更新可能触发多次 推荐用一个受控标志位 + 帧 ID 双保险: 自定义元素内部用 生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力:
- 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面;
- 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”;
- 提及位于 `apps/console/src/featur 实操建议: 帧调度本身很简单,难的是让每一帧都真正“有效”——多数卡顿问题出在测量、渲染、样式三者的时间错位,而不是调度逻辑写错了。requestAnimationFrame 是 JavaScript API,HTML 只负责承载元素和结构。想在“组件内部”做帧调度,关键在于 JS 层如何绑定生命周期与帧循环,而不是写个 React 组件中用
requestAnimationFrame 的正确姿势useEffect 里无限调用 requestAnimationFrame 却没清理,导致内存泄漏或帧冲突。正确做法是:
useRef 存储当前帧 ID,每次更新前先 cancelAnimationFrame
animateStep),避免闭包捕获过期 state${progress * 100}%
Vue 3 setup 中避免
requestAnimationFrame 被重复启动watch 或 onMounted 回调,尤其在开发模式下热更新时。容易踩的坑包括:
onMounted 里直接调用 requestAnimationFrame,但没用 onUnmounted 清理watch 内部启动帧循环,却没判断是否已有活跃帧(导致多个并发动画)ref 存帧 ID,但没用 shallowRef 或 toRef 导致不必要的响应式开销const isAnimating = ref(false);
const frameId = ref(0);
<p>onMounted(() => {
const loop = () => {
if (!isAnimating.value) return;
// ... 动画逻辑
frameId.value = requestAnimationFrame(loop);
};
isAnimating.value = true;
frameId.value = requestAnimationFrame(loop);
});</p><p>onUnmounted(() => {
isAnimating.value = false;
cancelAnimationFrame(frameId.value);
});</p>
Web Components 里帧调度要绕开 Shadow DOM 的样式隔离陷阱
requestAnimationFrame 更新样式时,常遇到动画卡顿或无效,根本原因不是帧调度本身,而是:
offsetWidth、getBoundingClientRect() 等测量操作被浏览器延迟到下一帧,导致帧内计算结果滞后transition 和 JS 帧更新混用,产生竞态:比如 JS 刚设 transform,CSS 还在 transition 途中,下帧又覆盖了中间状态will-change: transform,导致频繁重绘无法 GPU 加速
requestAnimationFrame 回调开头,避免被 layout thrashing 干扰transform 和 opacity,避开触发布局的属性(如 width、top)contain: strict 减少重绘范围
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










