Vue的依赖收集和性能问题

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

什么是依赖收集

Vue能够实现当一个数据变更时视图就进行刷新而且用到这个数据的其他地方也会同步变更而且这个数据必须是在有被依赖的情况下视图和其他用到数据的地方才会变更。 所以Vue要能够知道一个数据是否被使用实现这种机制的技术叫做依赖收集。

每个组件实例都有相应的watcher实例 - 渲染组件的过程会把属性记录为依赖 - 当我们操纵一个数据时依赖项的setter会被调用从而通知watcher重新计算从而致使与之相关联的组件得以更新。

Vue2用defineProperty来劫持属性生成watcher实例来响应属性的变化。

注意Dep的target是watcher实例

依赖收集与观察者模式

在Vue依赖收集里谁是观察者谁是观察目标 显然 - 依赖的数据是观察目标Watcher - 视图、计算属性、侦听器这些是观察者Watcher

依赖收集分析

依赖收集的三个类
  • Dep发布者。扮演观察目标的角色每一个数据都会有Dep类实例它内部有个subs队列subs就是subscribers的意思保存着依赖本数据的观察者当本数据变更时调用dep.notify()通知观察者

  • Watcher观察者。扮演观察者的角色进行观察者函数的包装处理。如render()函数会被进行包装成一个Watcher实例

  • Observer观测类。辅助的可观测类数组/对象通过它的转化可成为可观测数据

每一个数据都有的Dep类实例

由于JavaScript是单线程模型所以虽然有多个观察者函数但是一个时刻内就只会有一个观察者函数在执行那么此刻正在执行的那个观察者函数所对应的Watcher实例便会被赋给Dep.target这一类变量从而只要访问Dep.target就能知道当前的观察者是谁。 在后续的依赖收集工作里getter里会调用dep.depend()而setter里则会调用dep.notify()

Watcher观察者

一个组件里可以有多个Watcher类实例Watcher类包装观察者函数而观察者函数使用数据。 观察者函数经过Watcher是这么被包装的

- 模板渲染this._watcher = new Watcher(this, render, this._update)

- 计算属性

computed: {
    name() {
        return `${this.firstName} ${this.lastName}`;
    }
}
/*
会形成
new Watcher(this, function name() {
    return `${this.firstName} ${this.lastName}`
}, callback);
*/

在Watcher类里做的事情概括起来则是

1、传入组件实例、观察者函数、回调函数、选项

先解释清楚4个变量deps、depIds、newDeps、newDepIds它们的作用如下

- deps缓存上一轮执行观察者函数用到的dep实例

- depIdsHash表用于快速查找

- newDeps存储本轮执行观察者函数用到的dep实例

- newDepIdsHash表用于快速查找

2、进行初始求值初始求值时会调用watcher.get()方法

3、watcher.get()会做以下处理初始准备工作、调用观察者函数计算、事后清理工作

①在初始准备工作里会将当前Watcher实例赋给Dep.target清空数组newDeps、newDepIds

②执行观察者函数进行计算。由于数据观测阶段执行了defineReactive()所以计算过程用到的数据会得以访问从而触发数据的getter从而执行watcher.addDep()方法将特定的数据记为依赖

③对每个数据执行watcher.addDep(dep)后数据对应的dep如果在newDeps里不存在就会加入到newDeps里这是因为一次计算过程数据有可能被多次使用但是同样的依赖只能收集一次。并且如果在deps不存在表示上一轮计算中当前watcher未依赖过某个数据那个数据相应的dep.subs里也不存在当前watcher所以要将当前watcher加入到数据的dep.subs里

④进行事后清理工作首先释放Dep.target然后拿newDeps和deps进行对比接着进行以下的处理

- newDeps里不存在deps里存在的数据表示是过期的缓存数据。相应的从数据对应的dep.subs移除掉当前watcher

- 将newDeps赋给deps表示缓存本轮的计算结果这样子下轮计算如果再依赖同一个数据就不需要再收集了

⑤当某个数据更新时由于进行了setter拦截所以会对该数据的dep.subs这一观察者队列里的watchers进行通知从而执行watcher.update()方法而update()方法会重复求值过程即为步骤3-7从而使得观察者函数重新计算而render()这种观察者函数重新计算的结果就使得视图同步了最新的数据

Vue的依赖收集是观察者模式的一种应用。其原理总结如图

    • 配置依赖观测
    • 收集依赖
    • 数据值变更

依赖收集带来的问题

依赖收集需要对数据元进行递归绑定。当数据元数据量过大例如明细表或透视表数十万条数据会严重影响前端的性能效率。

解决方法

①避开依赖收集

不使用this.xxx = data绑定数据

对数据使用Object.freeze进行冻结避免依赖收集Vue2对configurable为false的对象不进行数据劫持或者将数据绑定为class私有属性

②将数据对象约定为数组

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: vue