(三)手写 Vue2.0 源码 —— 异步更新原理

前言

这篇是对视图更新的性能优化,包含 nextTick 这一 api 的实现,涉及到 JS 事件循环相关概念。

目前的情况是 每次改变数据都会触发相应的 watcher 进行更新,数据每变动一次就会重新渲染一次,这样很浪费性能,所以我们要让数据全部变动完毕后再去统一更新视图

一、watcher 更新的改写

  • 每次watcher进行更新的时候 是否可以让他们先缓存起来 之后再一起调用
  • 异步队列机制

src/observer/watcher.js

把 update 更新方法改了,增加异步队列的机制

二、 queueWatcher 实现队列机制

新建 scheduler.js 文件 表示和调度相关, 先同步把 watcher 都放到队列里面去 ,执行完队列的事件之后再清空队列, 主要使用 nextTick 来执行 watcher 队列。

src/observer/scheduler.js

三、nextTick 实现原理

src/util/next-tick.js

  • MutationObserver() 构造函数 主要是监听dom变化 也是一个异步方法
js 复制代码
// src/util/next-tick.js

let callbacks = [];
let pending = false;

function flushCallbacks () {
	pending = false;  // 把标志还原成 false
	for (let i = 0; i < callbacks.length; i++) {
		callbacks[i]();
	}
}

let timerFunc; // 定义异步方法,采用优雅降级
if (typeof Promise !== 'undefined') {
	// 支持 promise
	const p = Promise.resolve();
	timerFunc = () => {
		p.then(flushCallbacks);
	}
} else if (typeof MutationObserver !== 'undefined') {
	// MutationObserver 主要是监听dom变化 也是一个异步方法
	let counter = 1;
	const observer = new MutationObserver(flushCallbacks);
	const textNode = document.createTextNode(String(counter));
	observer.observe(textNode, {
		characterData: true
	})
	timerFunc = () => {
		counter = (counter + 1) % 2;
		textNode.data = String(counter);
	}
} else if (typeof setImmediate !== 'undefined') {
	// 如果前面都不支持 判断 setImmediate 
	timerFunc = () => {
		setImmediate(flushCallbacks);
	}
} else {
	// 最后降级采用 setTimeout
	timerFunc = () => {
		setTimeout(flushCallbacks, 0);
	}
}

export function nextTick (cb) {
	// 除了渲染 watcher,还有用户自己手动调用的 nextTick,一起收集到数组
	callbacks.push(cb);
	if (!pending) {
		// 如果多次调用nextTick  只会执行一次异步 等异步队列清空之后再把标志变为false
		pending = true;
		timerFunc();
	}
}

新建 util/next-tick.js 代表工具类函数 因为 nextTick 用户也可以手动调用 主要思路就是采用微任务优先的方式调用异步方法去执行 nextTick 包装的方法

四、$nextTick 挂载原型

挂载在原型的nextTick方法 可供用户手动调用

src/render.js

最后把$nextTick 挂载到 Vue 的原型

最后效果

数据变化了 7 次,但是在数据变动完毕后再去更新视图

相关推荐
m0_7400437310 小时前
3、Vuex-Axios-Element UI
前端·javascript·vue.js
鹏北海10 小时前
微信扫码登录 iframe 方案中的状态拦截陷阱
前端·javascript·vue.js
狗哥哥10 小时前
Vite 插件实战 v2:让 keep-alive 的“组件名”自动长出来
前端·vue.js·架构
小黑的铁粉10 小时前
Vue2 vs Vue3
vue.js
AAA阿giao10 小时前
代码宇宙的精密蓝图:深入探索 Vue 3 + Vite 项目的灵魂结构
前端·javascript·vue.js
半桶水专家10 小时前
vue中的props详解
前端·javascript·vue.js
前端不太难11 小时前
RN 遇到复杂手势(缩放、拖拽、旋转)时怎么设计架构
javascript·vue.js·架构
白兰地空瓶11 小时前
一行 npm init vite,前端工程化的世界就此展开
前端·vue.js·vite
码力巨能编11 小时前
Markdown 作为 Vue 组件导入
前端·javascript·vue.js
仰望.11 小时前
vue 甘特图 vxe-gantt table 拖拽任务调整开始日期和结束日期的使用,拖拽任务调整日期
vue.js·甘特图·vxe-ui