这文章不会再详细介绍Vue响应式原理、源码,由于相似的优秀文章掘金一大推。node
要了解Vue响应式源码的话,建议去看看别人的详细文章,此文章默认你已经掌握了响应式原理,毕竟我记录下来也是为了应付面试官。(狗头)面试
由于每次面试官问Vue响应式原理,本身语言都组织很差,因此干脆提早在这演戏一下,把我认为要回答的东西写下来。数组
要讲清楚响应式原理,必需要先搞清楚Vue源码中Observer
Watcher
Dep
3个类具体的做用、以及他们之间的关系。markdown
若是咱们把这3个类的关系、链接讲清楚了,那么响应式的大概原理也就明白了。app
Observer
,其主要做用是对数据进行深度监听、劫持,使得每次取值、设值都能监听到,从而执行一些自定义的方法。dom
Wathcer
,watch
被 Observer
处理过的数据,负责渲染视图和更新视图的,在响应式里的它是一个渲染Watcher
。函数
Dep,是记录渲染Watcher
,和通知渲染Wathcer
去更新视图的。若是不记录,数据更改后,Dep不知道要通知谁。oop
咱们从响应式的入口,Observer
开始讲起。post
若是你传入的data是一个对象,那么Observer
会对你的这个对象的每个key值进行defineReactive
,其实也就是对data
进行深度递归,把data
转化为getter
和setter
的形式,底层也就是Object.defineProperty
性能
那么在getter和setter会作一些什么呢?
Observer会给每个属性,new一个Dep实例来进行管理属性依赖状况,当你对这个data(Object)取值时(也就是触发get时),Dep实例会(调用dep.depend方法)把当前渲染Watcher记住。
当你对这个data(Object)进行设值时(也就是触发了set方法),须要更新模板了,就让dep通知以前记住的watcher去更新(调用dep.notify)
到这里,咱们大概知道了Observer、Dep、Watcher的关系。
附上一张,偷过来的图.(狗头)
简单来讲,就是Observer进行数据劫持、Watcher更新视图,而Dep是Observer和Watcher的桥梁,记录了数据的更改由哪一个Watcher更新。
带着上面的总结,继续聊聊Observer。
上面咱们说到,若是data是一个Object,就defineReactive,使用Object.defineProperty
来劫持。
那么若是data是一个数组呢?
实际上在响应式里,对数组的处理,是调用了另一套机制---重写Array原型方法。
总得来讲就是,你在调用数组方法的时候,好比push,会先调用本身定义的push,再执行原生Array的push方法。
实现细节就是:
一、先拷贝数组的原型
let arrayMethods = Object.create(Array.prototype) 复制代码
二、接着在本身建立的原型对象中,重写7个会改变数组自己的方法
// 这七个方法均可以改变原数组 let methods = [ 'push', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'splice' ] methods.forEach(method=>{ arrayMethods[method] = function (...args) { // 而后当你调用methods里的方法的时候,进行对数据observe // 而后再调用原生数组的方法 Array.prototype[method].apply(this,args); } }) 复制代码
三、最后更改数据的原型链
data.__proto__ = arrayMethods;
复制代码
那么,当你执行data.push
的时候,就会先执行arrayMethods
里面的push,最后在arrayMethods里面,处理完本身的逻辑,再调用Array.push便可达到改写原生Array的效果。
由于这个机制,使得不用循环遍历数组的每一项进行观察,也是由于这个机制致使了直接调用数组的下标没法更新视图,须要调用set方法。
在说Observer
的时候,咱们说过在获取数据的时候,会调用dep.depend
进行依赖收集,其实就是把对应的Watcher push到subs数组中。
有了这个数组后,若是你在对一个属性设置,那么就调用dep.notify,其实就是把subs里的每个watcher去执行更新。
notify(){ this.subs.forEach(watcher=>watcher.update()) } 复制代码
dep 和 watcher 是多对多的关系
每一个属性 都有一个dep属性 ,dep 存放着watcher .
---dep中能够有多个watcher ,由于一个watcher可能被多个属性所依赖
dep里面的subs数组里,存放的watcher是渲染Watcher,其做用就是渲染、更新视图。
当调用dep.notify的时候,会对这个属性全部的watcher调用update方法。
update方法的做用,就是去更新视图,这里为了节约性能会使用nextTick优化,相似一个防抖。
整个大概就是流程就是:
调用update以后,用nextTick优化。再调用更新的回调函数。
这个更新回调函数,生成新的render函数,render就会产生新的Vnode,vnode生成真实dom渲染视图。
至此就完成了数据更改到模板更新的过程。
本文使用 mdnice 排版