面试官,你再试试问Vue响应式原理?(标题党)

前排劝退

这文章不会再详细介绍Vue响应式原理、源码,由于相似的优秀文章掘金一大推。node

要了解Vue响应式源码的话,建议去看看别人的详细文章,此文章默认你已经掌握了响应式原理,毕竟我记录下来也是为了应付面试官。(狗头)面试

由于每次面试官问Vue响应式原理,本身语言都组织很差,因此干脆提早在这演戏一下,把我认为要回答的东西写下来。数组


正文

要讲清楚响应式原理,必需要先搞清楚Vue源码中Observer Watcher Dep3个类具体的做用、以及他们之间的关系。markdown

若是咱们把这3个类的关系、链接讲清楚了,那么响应式的大概原理也就明白了。app

Observer,其主要做用是对数据进行深度监听、劫持,使得每次取值、设值都能监听到,从而执行一些自定义的方法。dom

WathcerwatchObserver 处理过的数据,负责渲染视图和更新视图的,在响应式里的它是一个渲染Watcher函数

Dep,是记录渲染Watcher,和通知渲染Wathcer去更新视图的。若是不记录,数据更改后,Dep不知道要通知谁。oop


数据观察者 Observer

咱们从响应式的入口,Observer开始讲起。post

若是你传入的data是一个对象,那么Observer会对你的这个对象的每个key值进行defineReactive,其实也就是对data进行深度递归,把data转化为gettersetter的形式,底层也就是Object.defineProperty性能

那么在getter和setter会作一些什么呢?

Getter

Observer会给每个属性,new一个Dep实例来进行管理属性依赖状况,当你对这个data(Object)取值时(也就是触发get时),Dep实例会(调用dep.depend方法)把当前渲染Watcher记住

Setter

当你对这个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方法。


依赖数据调度中心 Dep

在说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可能被多个属性所依赖


订阅者 Watcher

dep里面的subs数组里,存放的watcher是渲染Watcher,其做用就是渲染、更新视图。

当调用dep.notify的时候,会对这个属性全部的watcher调用update方法。

update方法的做用,就是去更新视图,这里为了节约性能会使用nextTick优化,相似一个防抖。

整个大概就是流程就是:

调用update以后,用nextTick优化。再调用更新的回调函数。

这个更新回调函数,生成新的render函数,render就会产生新的Vnode,vnode生成真实dom渲染视图。

至此就完成了数据更改到模板更新的过程。

本文使用 mdnice 排版

相关文章
相关标签/搜索