Getter 应作为懒初始化开关,首次读取时用 requestAnimationFrame 或 setTimeout(0) 调度宏任务,避免同步耗时操作、微任务阻塞渲染,后续读取直接返回缓存结果。

直接用原生 Getter 触发耗时宏任务,不是推荐做法;Getter 本意是同步、轻量的属性访问,若在其中塞入 setTimeout、requestAnimationFrame 或异步逻辑,会破坏其语义,引发不可预测的行为(如多次求值、调试困难、与 Proxy/响应式系统冲突)。真正有效的思路是:**用 Getter 作“懒初始化开关”,把耗时宏任务推迟到首次真正需要时才调度,且确保调度本身不阻塞渲染**。
用 Getter 控制首次触发时机
Getter 可作为“是否已初始化”的判断入口,只在第一次读取时启动宏任务,后续读取直接返回缓存结果。关键在于:宏任务调度必须脱离同步执行流,不能在 Getter 内部直接执行耗时操作。
- 错误写法(阻塞主线程、违背 Getter 原则):
get data() { return heavyCompute(); } - 正确模式(懒启动 + 宏任务解耦):
let _cachedData = null;<br>let _taskScheduled = false;<br>get data() {<br> if (_cachedData !== null) return _cachedData;<br> if (!_taskScheduled) {<br> _taskScheduled = true;<br> setTimeout(() => { _cachedData = heavyCompute(); }, 0);<br> }<br> return null; // 或 placeholder<br>}
结合 requestAnimationFrame 保障渲染优先级
若该数据用于首屏 UI 渲染(如动态标题、状态栏),应优先用 requestAnimationFrame 而非 setTimeout(0),因为它绑定屏幕刷新节奏,在浏览器准备绘制前执行,更利于首屏内容及时上屏。
- 适合场景:DOM 插入后需立即计算布局、获取
offsetHeight后再做动画起点定位 - 示例:
get layoutInfo() {<br> if (this._layoutCache) return this._layoutCache;<br> if (!this._rafPending) {<br> this._rafPending = true;<br> requestAnimationFrame(() => {<br> this._layoutCache = { width: this.el.offsetWidth, height: this.el.offsetHeight };<br> });<br> }<br> return null;<br>}
避免微任务污染,守住宏任务边界
切勿在 Getter 中返回 Promise 或调用 queueMicrotask——这会把耗时逻辑拖进微任务队列,导致渲染被延迟。浏览器必须清空全部微任务后才进入渲染阶段,而首屏正是争这一帧。
- 风险示例:
get asyncData() { return Promise.resolve().then(() => heavyCompute()); }→ 这会压入微任务,阻塞渲染 - 安全替代:
get asyncData() {<br> if (this._done) return this._result;<br> if (!this._scheduled) {<br> this._scheduled = true;<br> postMessage('trigger-heavy-task', '*'); // 或 setTimeout(..., 0)<br> }<br> return undefined;<br>}
配合框架生命周期做兜底
纯 Getter 懒触发适用于简单状态,但首屏优化常需协同组件挂载时机。例如 Vue 的 onMounted 或 React 的 useEffect(() => {}, []) 是更自然的宏任务起始点;Getter 可作为其内部状态的访问代理,而非独立触发器。
- Vue 示例:
setup() {<br> const dataRef = ref(null);<br> onMounted(() => {<br> setTimeout(() => { dataRef.value = heavyCompute(); }, 0);<br> });<br> return { data: computed(() => dataRef.value) };<br>} - 优势:逻辑归属清晰,不依赖属性读取时机,避免因模板未渲染导致 Getter 未被调用而数据永远不加载










