简介这里我先问两个问题(答案会在文末解答):在Vue的数据响应系统中,Dep和Watcher各自共享什么任务?Vue的数据响应系统的核心是Object.defineproperty一定是最好的吧?有没有缺点和漏洞?1、响应系统中的Watcher是什么,作用是什么?响应系统中的Watcher是系统的观察者。它是响应系统中观察者模式的载体。当响应系统中的数据发生变化时,它可以知道并执行相应的功能,以达到一定的业务逻辑目的。.比如你是商家,要给不同的客户发一批货,那么watcher就是一个一个的快递员,发送的action就是数据的变化。您只需对发出的行为负责。如何找到并发送给客户是观察者的事。每个watcher和数据的关系要么是1对1,要么是多对多的关系(这个和watcher的类型有关),要么没有任何联系。watcher和业务逻辑之间只有一对一的关系。2.Watcher的类型在Vue源码中并没有体现出Watcher的类型。我这里加上Watcher的类型是为了更好的理解Watcher这个对象。Watcher按照常见的业务逻辑可以分为以下三类:普通Watcher:与数据一对一的关系。LazyWatcher:它和数据是一对一的关系,但是它是惰性求值的观察者,怎么体现呢?给它赋值不会改变它的值,只有获取到它的值时,才会更新最新版本的数据(在Vue中体现为computed方法,一般的求值都是通过方法求值).Render-typeWatcher:它和数据是一对多的关系(不管给子组件传递参数)。在一个组件中,render函数observer一定是最后生成的,所以当observer队列执行时,render函数observer是在一个组件中最后执行的。让我多谈谈什么是懒惰的观察者。惰性观察者在Vue中表示为计算属性。通常,此属性是一个函数。下面是一个例子:computed:{//getCount最终被处理成一个属性,然后这个方法存储在Watcher的一个属性中getCount(){returnthis.a+this.b;}}lazyobserver中有一个dirty属性,就是switch函数。只有当getCount的getter方法为true时,才会调用这个函数。如果惰性观察者引用的数据(a或b属性)发生变化,则将其放入观察者执行队列,然后在执行观察者时将dirty值赋值为true(代表下次访问getter方法)惰性求值方法会执行一次(dirty求值后会赋值为false))。直到下次需要获取数据时才进行求值,所以称为惰性求值。这种方法节省了不必要地执行函数的开销。3.Watcher和Dep的关系看完Vue源码的defineReactive方法,你会发现一个被观察对象中的每个属性都会有一个Dep依赖篮子来存放所有观察它的Watcher。defineReactive方法只初始化了每个属性的dep,并没有创建观察者(以区分初始化和创建观察者是分开的)。那么这个Dep是怎么添加观察者的呢?Vue使用了一个全局变量Dep.target,它是一个Watcher类型的变量,用来绑定Watcher和Dep。数据绑定用图表示如下:我们可以明确以下区别:$watch方法创建的observer没有设置immediate属性时,不会调用,而computed和render会调用方法。数据的Dep的subs数组存放的是数据绑定的观察者对象,观察者对象的deps数组存放的是与观察者相关的数据Dep。所以,dataDep和Watcher其实是多对多的关系。$watch和computedobservers在createdlifehookfunction之前创建和绑定,而renderobservers在mounted之前创建和绑定,所以同一个组件中,renderobservers的id会大于其他观察者的id(id为后续执行队列中升序排序的依据)。也就是说,同一个组件的观察者之间,当数据发生变化时,渲染函数观察者必须最后执行。这很容易理解。其他观察者修改数据是不可避免的。如果渲染函数观察者先执行,然后其他观察者更改数据,那么就没办法在页面上准确呈现数据,造成数据不一致。参考:前端vue面试题详解4.说说观察者执行队列机制vue是如何实现性能优化的?最值得注意的两点:观察者设置执行队列,分批执行。diff算法减少了渲染开销。第二个这里就不解释了,我们看看第一个是怎么回事?这个队列的长度是如何量化的呢?最大长度为100,源代码在那里。取一个事件循环时间段作为采集时间。(什么是事件循环?可以看看本博客系统其他优秀文章)其流程如下:未执行时:如果数据发生变化,则直接将对应的观察者压入队列(执行时会执行)根据id升序排序后执行)执行时,如果有新的观察者进来(观察者中有数据改变,然后数据绑定到观察者),按升序插入orderofid(这相当于在Insertingintoanorderedarray可以看作是插入排序的步骤之一,所以某种意义上就是排序)。5.回答上一个问题。Dep是负责存储所有绑定数据的观察者对象的容器。只要数据发生变化,就会通过这个Dep通知所有观察者修改数据。(每个数据都有一个唯一的Dep)。Watcher更倾向于一个action,即规定的业务逻辑或渲染函数,是一个执行者。ES5非常便携,这很好。但是ES6出现之后,肯定不是最好的,因为ES6有Proxy代理统一处理。(ES6使用代理来实现Vue数据响应系统(TypeScript))缺点:如果一条数据有1000个属性,那么这1000个属性必须使用Object.defineProperty,这样会在初始化页面时造成卡顿。如果使用代理,则只需执行一次。漏洞:如果我们获取到vm实例,用户可以在运行时通过修改对象属性的描述符来修改它,这会造成系统的不确定性。这是因为响应系统的模式要求数据描述符的配置必须设置为true,所以可以在运行时修改。
