Appearance
七、异步更新与nextTick
0、index.html中调用
js
let vm = new Vue();
setTimeout(() => {
vm.arr.push(123);
vm.arr.push(123);
vm.arr.push(123);
console.log(vm.$el.innerHTML);
vm.$nextTick(() => {
console.log(vm.$el.innerHTML);
});
}, 2000);1、Vue原型找那个扩展nextTick方法
js
// src\state.js
import { nextTick } from "./util"
export function stateMixin(Vue) {
Vue.prototype.$nextTick = function(cb) {
nextTick(cb);
}
}2、实现队列机制
js
// src\observer\watcher.js
class Watcher {
constructor(vm, exprOrFn, cb, options) {
......
}
.....
update() {
// 这里不能每次都调用get方法,get方法会重新渲染页面,需要用队列实现多次调用只刷新一次
queueWatcher(this);
}
}js
// 实现队列
// src\observer\watcher.js
import { nextTick } from "../util";
// 将需要批量更新的watcher存到一个队列中,稍后让watcher执行
let queue = [];
let has = {}; // 用于去重
let pending = false; // 防抖
function flushSchedulerQueue() {
queue.forEach(watcher => {
watcher.run();
watcher.cb();
});
queue = []; // 清空watcher队列为了下次使用
has = {}; // 清空标识的id
pending = false;
}
function queueWatcher(watcher) {
// 对watcher去重
const id = watcher.id;
if (has[id] == null) {
// 将watcher存到队列中
queue.push(watcher);
has[id] = true;
// 异步更新,等到所有同步代码执行完毕后再执行
if (!pending) {
// 内部调用
nextTick(flushSchedulerQueue);
pending = true;
}
}
// console.log(watcher.id);
}
export default Watcher;3、nextTick原理
Promise > MutationObserver > setImmediate > setTimeout
js
// src\util.js
const callbacks = [];
let pending = false;
let timerFunc;
function flushCallbacks() {
// 让nextTick中传入的方法依次执行
while (callbacks.length) {
let cb = callbacks.pop();
cb();
}
// 标识已经执行完毕
pending = false;
}
if (Promise) {
timerFunc = () => {
// 异步处理更新
Promise.resolve().then(flushCallbacks);
}
} else if (MutationObserver) {
// 可以监控dom变化,监控完毕后是异步更新
let observe = new MutationObserver(flushCallbacks);
/**
* 思路:
* 1、创建一个文本节点
* 2、监控这个文本节点
* 3、当文本节点里面的字符变化,就异步调用flushCallbacks进行更新操作
*/
// 先创建一个文本节点
let textNode = document.createTextNode(1);
// 观测文本节点中的内容
observe.observe(textNode, { characterData: true });
timerFunc = () => {
// 文本内容更新为2,触发异步调用flushCallbacks
textNode.textContent = 2;
}
} else if (setImmediate) {
// ie浏览器里面的api,性能比setTimeout要好些
timerFunc = () => {
setImmediate(flushCallbacks);
}
} else {
timerFunc = () => {
setTimeout(flushCallbacks);
}
}
// 内部会调用,用户也会调用,但是异步只需要一次
export function nextTick(cb) {
callbacks.push(cb);
// Vue3里面的nextTick原理就是Promise.resolve().then() 没有兼容性处理
if (!pending) {
timerFunc();
pending = true;
}
}