vue响应式源码解析,vue.js响应式原理
最近去面试的人都会有这种体验。去年面试官只问我怎么用vue。今年开始问vue的响应式原理。本文将详细介绍。
:
目录
1.Vue.js函数:2。Observer.js函数(数据劫持):3。Compiler.js函数:4。Dep.js函数:5。Watcher.js函数:整体分析Vue的基本结构如下图所示:(注:https://github.com/1512955040/MiniVue完整代码github地址)
在上图中,我们模拟了最小vue的整体结构。首先,我们创建一个vue类型,它负责将数据中的成员注入到vue实例中,并将它们转换成getter/setter。观察器的作用是劫持数据,监视数据中的属性,如果数据发生变化,就获取最新的值,并通知dep。编译器的作用是解析每个元素中的指令和差分表达式,并用相应的数据替换它们。Dep的作用是添加观察器,并在数据发生变化时通知所有观察器。Watcher中有一个更新方法来更新视图。我们用代码一个一个实现吧。
1.Vue.js功能:
1-1负责接收初始化参数(选项)
1-2负责将数据中的属性注入到vue实例中,并将其转换为getter/setter。
1-3负责调用observer来监控数据中所有属性的变化。
1-4负责调用编译器解析指令/差异表达式。
类图结构如下:
如上图,vue类中有三个属性,分别是$ options、$ el和$ data。这三个属性记录从构造函数传递的参数。_proxyData是vue类中的一个方法。
因此,以_开头的成员是私有成员。这个方法的作用是将数据中的属性转换成getter和setter,并注入到vue实例中。
Vue类{
构造函数(选项){
//1.通过属性保存选项中的数据。
这个。$options=options {}
这个。$data=options.data {}
这个。$el=typeof options.el===string ?document . query selector(options . El):options . El
//2.将数据中的成员转换成getter和setter,并注入vue实例。
这个。_proxyData(this。$data)
//3.调用observer对象来监视数据的变化。
新观察家(这个。$data)
//4.调用编译器对象来解析指令和差异表达式。
新编译器(this)
}
//将Vue的属性转换成getter和setter,注入到Vue实例中
_proxyData(data){
//遍历数据中的所有属性
Object.keys(数据)。forEach(key={
//将数据的属性注入vue实例的全局
Object.defineProperty(this,key,{
可枚举:真,
可配置:真,
get(){
返回数据[键]
},
set(newValue){
if(newValue===data[key]){
返回
}
数据[键]=新值
}
})
})
}
}
2.Observer.js功能(数据劫持):
2-1负责将数据选项中的属性转换成响应数据。
2-2数据中的一个属性也是一个对象,这个属性转换成响应数据。
2-3发送数据更改通知
类图结构如下:
如上图所示:
walk方法的作用是遍历数据中的所有属性,defineReactive方法是定义响应数据,即通过调用defineReactive方法将属性转换为getter和setter。
课堂观察者{
构造函数(数据){
this.walk(数据)
}
//walk方法//遍历数据中的所有属性
行走(数据){
//1.确定数据是否是对象。
如果(!数据数据类型!==object){
返回
}
//2.遍历数据对象的所有属性
Object.keys(数据)。forEach(key={
this.defineReactive(data,key,data[key])
})
}
///degineReactivce方法定义响应数据,并将属性转换为getter和setter。
defineReactive(obj,key,val) {
让那个=这个
//负责收集依赖项和发送通知
let dep=new Dep()
//如果将val传递到对象中,还要将getter和setter方法添加到对象内部的属性中。
这条路(瓦尔)
Object.defineProperty(obj,key,{
可枚举:真,
可配置:真,
get(){
//收集依赖项
dep . target dep . add sub(dep . target)
返回值
},
set(newValue){
if(newValue==val){
返回
}
val=新值
//如果将属性重新分配给对象,请将getter和setter方法添加到对象内部的属性中。
//比如:vm.msg=Hello World 历史数据修改后,vm.msg={a:Hwllo World}
//再次调用此方法将getter和setter方法添加到vm.msg.a中
that.walk(新价值)
//发送通知
dep.notify()
}
})
}
}
3.Compiler.js功能:
3-1负责编译模板,解析指令/差异表达式。
3-2负责页面的第一次呈现
3-3当数据改变时重新渲染视图。
类图结构如下:
如上图所示:
Options.el,el构造函数传递的vm,vm是vue的一个实例,以下都是VM操作DOM的方法。编译方法在内部遍历dom对象的所有节点,并且
判断这些节点是文本节点。如果是解析差异表达式的文本节点,如果是元素节点解析指令,isTextNode和isElementNode方法判断是否是文本节点。
是元素节点。CompileElement和compileText方法解析不同的表达式和指令。IsDirective此方法确定元素属性是否是指令。
4.Dep.js功能:
4-1收集依赖项并添加观察器。
4-2通知所有观察员
如上图所示:
在vue的响应机制中,观察者模式用于响应数据的变化。Dep的作用是收集依赖,在getter方法中收集依赖,在setter方法中通知依赖。每一
一个响应属性将创建一个Dep对象,该对象负责收集依赖于该属性的所有位置。所有依赖于该属性的位置都将创建一个观察器对象,所以
Dep是在此属性中收集的观察器对象。setter方法用于通知依赖关系。当属性改变时,调用nodify方法发送通知,然后调用watcher对象。
的更新方法。
该类的组织如下:
如上图所示:
Subs是一个在dep中存储所有观察器的数组。addsub方法添加观察器,而watcher,notify方法发布通知。
类Dep
构造函数(){
//存储所有观察器
this.subs=[]
}
//添加观察者
addSub(sub){
if(子子更新){
这个. subs.push(子)
}
}
//发送通知
通知(){
this.subs.forEach(sub={
子更新()
})
}
}
5.Watcher.js功能:
5-1当数据更改触发依赖关系时,dep通知所有观察器实例更新视图。
5-2实例化自己时将自己添加到dep对象。
如上图所示:
数据中的每个属性都应该创建一个Dep对象。在收集依赖关系时,所有对象的观察器都被添加到dep对象的subs数组中,并发送给setter对象。
知道,调用Dep对象的notify方法通知所有相关的watcher对象更新视图。
类图结构如下:
如上图所示:
对象更新视图,cb回调函数,指示如何更新视图。更新视图时,需要一个属性键(数据中的属性名),oldvalue是key的对应值。
类监视器{
构造函数(vm,key,cb) {
this.vm=vm
//数据中的属性名
this.key=key
//回调函数负责更新视图
this.cb=cb
//将watcher对象记录到Dep类的静态属性target
Dep.target=这
//触发get方法,其中将调用addSub
this.oldValue=vm[key]
Dep.target=null
}
//当数据更改时更新视图
update(){
设newValue=this.vm[this.key]
if(this.oldValue===newValue){
返回
}
this.cb(新值)
}
}
通过下图对整个过程做一个总结:
以上就是本文关于Vue模拟响应式原理底层代码实现的例子。关于Vue响应式原理的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望大家以后能多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。