如何理解Vue的批处理和react有什么区别?
先有问题再有答案
如何理解Vue的批处理?
与浏览器的event loop有什么关系 ?
vue自动收集 批量更新的基本单位是什么?
和react批处理有什么区别嘛?
前置文章
如何理解vue的批处理
case1
<script setup>
const count = ref(0);
let render = 0;
onUpdated(() => {
console.log("update render:", render++);
});
const onBtn1 = () => {
for (let i = 0; i < 5; i++) {
count.value++;
}
};
</script>
在这段代码中,for循环五次增加count的值,但是并不会触发五次组件的重新渲染。在Vue.js的响应式系统中,如果在同一个事件循环(event loop)内多次改变一个响应式变量的值,那么Vue.js会在这个事件循环结束后,用一个微任务(microtask)去触发这个响应式变量的依赖更新,然后进行组件的重新渲染。
因此,尽管count.value在onBtn1函数中改变了五次,但是它们都在同一个事件循环内,所以Vue.js会在这个事件循环结束后,只触发一次更新,并且只会重新渲染一次组件。所以,console.log("update render:", render++);只会打印一次最终值。
case2
<script setup>
const count = ref(0);
let render = 0;
onUpdated(() => {
console.log("update render:", render++);
});
const onBtn2 = () => {
for (let i = 0; i < 5; i++) {
setTimeout(() => {
count.value++;
}, 1000);
}
};
</script>
在 onBtn2 函数中,count.value 的增加操作被放在了一个 setTimeout 函数里,设置了 1000ms 的延迟。同时,这个操作执行了 5 次。
由于 setTimeout 函数会让count.value++ 分别在5个不同的宏任务(macrotask)队列中执行,事件循环会处理完JavaScript主线程上的任务,然后再处理宏任务队列中的下一个任务。在这里,这将分别在大约1秒的时间间隔内递增5次 count.value。
因此,这段代码里的 console.log("update render:", render++); 将会被打印五次,因为每次 setTimeout 中的回调函数被执行时,count.value 的改变,会触发组件的重新渲染,从而触发 onUpdated。而多次count.value++操作都处于不同的宏任务队列中,所以组件将会有 5 次独立的更新和重新渲染操作。
case 3
const count = ref(0);
let render = 0;
onUpdated(() => {
console.log("update render:", render++);
});
const onBtn3 = () => {
setTimeout(() => {
for (let i = 0; i < 5; i++) {
count.value++;
}
}, 1000);
};
在 onBtn3 函数中,count.value 的增加操作被放在了一个 setTimeout 函数里,设置了 1000ms的延迟,并在这个 setTimeout 函数内通过 for 循环进行了五次递增操作。
setTimeout 函数会将回调函数放入 Event Loop 的宏任务队列,会等到 JavaScript 主线程上的代码执行完毕后,再去执行这个队列中的任务。
在一个单独的 setTimeout 任务中,尽管 count.value 被改变了五次,但这五次改变都发生在同一个宏任务队列中。根据 Vue 的异步更新策略,这些改变将被合并,只会触发一次组件的更新。因此,这段代码里的 console.log("update render:", render++); 将会只被打印一次。
vue & event-loop总结
react中的批处理
在 React 的同步生命周期方法或事件处理器中,多次连续的状态更新通常会被合并,所以只会引起一次重新渲染。这种行为称为状态更新的批处理(batching)。批处理提高了性能,因为它减少了不必要的重新渲染次数。
在某些情况下,这种批处理机制可能不会按预期工作,导致状态更新被单独处理,从而引起多次渲染。以下是一些批处理可能“失效”或不被应用的情况: 异步操作:只有同步代码中的状态更新会自动被批处理。在异步操作中(如 setTimeout、Promise、异步事件处理等)触发的状态更新不会被自动批处理,每个状态更新都可能引起一次单独的重新渲染。 非 React 事件处理器:由非 React 的事件管理(如直接添加到 DOM 元素上的事件监听器)触发的状态更新,不会被自动批处理,因为 React 无法捕获和控制这些更新。
react&vue 批处理差异
主要的差异在于两者的合并契机不同 vue是以event-loop为单位的自动合并。react是以内部的生命周期方法或事件处理器为单元的手动合并。
const count = ref(0);
let render = 0;
onUpdated(() => {
console.log("update render:", render++);
});
const onBtn3 = () => {
setTimeout(() => {
for (let i = 0; i < 5; i++) {
count.value++;
}
}, 1000);
};
这段代码vue只会更新一次。 如果换为react的版本(v16)
const [count, setCount] = useState(-1);
useEffect(() => {
console.log('test update', count);
}, [count]);
const onBtn3 = () => {
setTimeout(() => {
for (let i = 0; i < 5; i++) {
setCount(i);
}
}, 1000);
};
setTimeout虽然是在同一个时间循环内,但是因为不在react生命周期和合成事件中 所以每次调用setCount都会触发更新。
当然这个批处理的合并机制 在react 18中已经有所变更. 在react生命周期外也可以做到任务合并。
转载自:https://juejin.cn/post/7394366490395574307