今天我们简单了解下vue3.0的异步更新原理,了解一下effect
,watchEffect
的特点以及最主要queueFlush
函数的实现(函数名字本意就是:排队刷新
)
effect特点
import { effect, reactive } from './reactivity'; let state = reactive({ name: 'zf', age: 11 }) effect(() => { console.log(state.name); }) state.name = 'zf'; state.name = 'jw'; state.name = 'jg';
每次更新状态,都会重新运行effect。如果要是effect中包含渲染逻辑,可能会导致多次更新视图
。
watchEffect
import { effect } from "./reactivity"; export function watchEffect(effect, options) { return doWatch(effect, null, options); } let postFlushCbs = []; function queuePostFlushCb(cb){ postFlushCbs(cb); // 将effect放到数组中进行刷新 queueFlush(); } function doWatch(source, cb, options) { // 做watch let getter; if (isFunction(source)) { getter = () => source(); } let scheduler = (job) => queuePostFlushCb(job); const runner = effect(getter,{ // 创建一个effect lazy:true, computed: true, scheduler // 自定义scheduler }) runner(); }
watchEffect也是effect,只是自定义了scheduler
函数
queueFlush实现
let isFlushPending = false; // 是否正在等待刷新 let isFlushing = false; // 是否正在刷新 const p = Promise.resolve(); function nextTick(fn) { return fn ? p.then(fn) : p } function flushPostFlushCbs(){ if(postFlushCbs.length){ // 队列有值进行队列刷新 const cbs = [...new Set(postFlushCbs)]; postFlushCbs.length = 0; for(let i = 0; i < cbs.length;i++){ cbs[i](); } } } function flushJobs() { isFlushPending = false; // 开始执行任务 isFlushing = true; // 正在刷新 flushPostFlushCbs(); // 刷新队列 isFlushing = false; // 刷新完毕 } function queueFlush() { if (!isFlushPending && !isFlushing) { isFlushPending = true; nextTick(flushJobs); // 稍后刷新任务队列 } }
nextTick
本质原理就是个promise(微任务)
,这里会将effect 暂存
起来并进行去重
之后执行。