Vue3.0异步更新原理
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';
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
每次更新状态,都会重新运行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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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); // 稍后刷新任务队列
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
nextTick本质原理就是个promise(微任务),这里会将effect暂存起来并进行去重之后执行。