最近去面试的人都会有这个体会,去年面试官只问我怎么用vue,今年开始问我vue响应式原理,本文就详细的介绍一下
整体分析Vue的基本结构如下图所示:(备注:完整代码github地址https://github.com/1512955040/MiniVue)
上图中,为我们模拟最小vue的整体结构,首先创建一个vue类型,它负责把data中的成员注入到vue实例中,并且转化成getter/setter,observer的作用是数据劫持,对data中的属性进行数据监听,如果数据发生变化会获取到最新的值,并通知dep。Compiler的作用是解析每个元素中的指令和差值表达式并替换成相应的数据。Dep的作用是添加观察者,当数据发生变化时通知所有的观察者。Watcher内部有一个Update方法负责更新视图,下面我们用代码的方式一一进行实现。
1.Vue.js功能:
1-1负责接收初始化的参数(选项)
1-2负责把data中的属性注入到vue实例,转化成getter/setter
1-3负责调用observer监听data中所有属性的变化
1-4负责调用Compiler解析指令/差值表达式
类图结构如下:
如上图所示:vue类中有三个属性,分别是$options,$el,$data,这三个属性记录构造函数中传过来的参数。_proxyData为vue类中的方法
所以以_开头的成员就是私有成员,这个方法的功能是把data中的属性转化为getter和setter注入到vue实例中。
class Vue{ constructor(options) { //1.通过属性保存选项中的数据 this.$options = options || {} this.$data = options.data || {} this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el //2.把data中的成员转化为getter和setter,注入到vue实例中 this._proxyData(this.$data) //3.调用observer对象,监听数据的变化 new Observer(this.$data) //4.调用compiler对象,解析指令和差值表达式 new Compiler(this) } //把Vue的属性转化为getter和setter,注入到Vue实例中 _proxyData(data){ //遍历data中所有属性 Object.keys(data).forEach(key=>{ //把data的属性注入到vue实例全局中 Object.defineProperty(this,key,{ enumerable:true, configurable:true, get(){ return data[key] }, set(newValue){ if(newValue===data[key]){ return } data[key]=newValue } }) }) } }
2.Observer.js功能(数据劫持):
2-1 负责把data选项中的属性转化为响应式数据
2-2 data中的某个属性也是对象,把该属性转化为响应式数据
2-3 数据变化发送通知
类图结构如下:
如上图所示:
walk方法的作用是遍历data中的所有属性,defineReactive是定义响应式数据,也就是通过调用defineReactive方法把属性转化为getter和setter。
class Observer{ constructor(data) { this.walk(data) } //walk方法遍历data中的所有属性 walk(data) { //1.判断data是否对象 if(!data || typeof data !=='object'){ return } //2.遍<span style="color:transparent">本文来源gaodai#ma#com搞*!代#%^码网%</span>历data对象的所有属性 Object.keys(data).forEach(key=>{ this.defineReactive(data,key,data[key]) }) } //degineReactivce方法定义响应式数据 把属性转化为getter和setter defineReactive(obj,key,val) { let that=this // 负责收集依赖,并发送通知 let dep=new Dep() //如果val传入对象的话也给对象里面的属性添加getter和setter方法 this.walk(val) Object.defineProperty(obj,key,{ enumerable:true, configurable:true, get(){ // 收集依赖 Dep.target && dep.addSub(Dep.target) return val }, set(newValue){ if(newValue==val){ return } val=newValue //如果给属性重新赋值成对象,给对象里面的属性重新添加getter和setter方法 //比如:历史数据vm.msg="Hello World" 修改之后vm.msg={a:'Hwllo World'} //再次调用此方法给vm.msg.a重新添加getter和setter方法 that.walk(newValue) //发送通知 dep.notify() } }) } }
3.Compiler.js功能:
3-1 负责编译模板,解析指令/差值表达式
3-2 负责页面的首次渲染
3-3 当数据变化后重新渲染视图
类图结构如下:
如上图所示:
el为构造函数传过来的options.el,vm是vue的实例,下面都是vm的方法,对DOM进行操作。compile方法内部遍历dom对象的所有节点,并且
判断这些节点是文本节点,如果是文本节点解析差值表达式,如果是元素节点解析指令,isTextNode和isElementNode方法判断是文本节点还
是元素节点。compileElement和compileText方法解析差值表达式和指令。isDirective这个方法判断元素属性是否是指令。
4.Dep.js功能:
4-1 收集依赖,添加观察者(watcher)
4-2 通知所有观察者
如上图所示:
在vue的响应式机制中,使用观察者模式来响应数据的变化,Dep的作用是收集依赖,在getter方法中收集依赖,在setter方法中通知依赖,每
一个响应式的属性都会场景一个Dep对象,负责收集所有依赖于该属性的地方,所有依赖于该属性的位置都会创建一个watcher对象,所以
Dep就是收集于该属性的watcher对象,使用setter方法去通知依赖,当属性发生变化时调用nodify方法去发送通知,然后调用watcher对象
的update方法。
类的机构如下图:
如上图所示:
subs是一个数组,存储dep中所有的watcher,addSub方法添加watcher,notify方法发布通知
class Dep{ constructor() { //存储所有的观察者 this.subs=[] } // 添加观察者 addSub(sub){ if(sub && sub.update) { this.subs.push(sub) } } //发送通知 notify(){ this.subs.forEach(sub =>{ sub.update() }) } }
<h
以上就是Vue模拟响应式原理底层代码实现的示例的详细内容,更多请关注gaodaima搞代码网其它相关文章!