Vue 源码解析上Word格式文档下载.docx
《Vue 源码解析上Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Vue 源码解析上Word格式文档下载.docx(9页珍藏版)》请在冰点文库上搜索。
![Vue 源码解析上Word格式文档下载.docx](https://file1.bingdoc.com/fileroot1/2023-5/10/720661e0-a0cf-4368-9c07-0dfc37b509ee/720661e0-a0cf-4368-9c07-0dfc37b509ee1.gif)
data:
function(){
return{
times:
1
};
},
created:
varme=this;
setInterval(function(){
me.times++;
},1000);
}
});
运行后,我们可以从页面中看到,count后面的times每隔1s递增1,视图一直在更新。
在代码中仅仅是通过setInterval方法每隔1s来修改vm.times的值,并没有任何DOM操作。
那么Vue.js是如何实现这个过程的呢?
我们可以通过一张图来看一下,如下图所示:
图中的模型(Model)就是data方法返回的{times:
1},视图(View)是最终在浏览器中显示的DOM。
模型通过Observer、Dep、Watcher、Directive等一系列对象的关联,最终和视图建立起关系。
归纳起来,Vue.js在这里主要做了三件事:
∙通过Observer对data做监听,并且提供了订阅某个数据项变化的能力。
∙把template编译成一段documentfragment,然后解析其中的Directive,得到每一个Directive所依赖的数据项和update方法。
∙通过Watcher把上述两部分结合起来,即把Directive中的数据依赖通过Watcher订阅在对应数据的Observer的Dep上。
当数据变化时,就会触发Observer的Dep上的notify方法通知对应的Watcher的update,进而触发Directive的update方法来更新DOM视图,最后达到模型和视图关联起来。
接下来我们就结合Vue.js的源码来详细介绍这三个过程。
Observer
首先来看一下Vue.js是如何给data对象添加Observer的。
我们知道,Vue实例创建的过程会有一个生命周期,其中有一个过程就是调用vm.initData方法处理data选项。
initData方法的源码定义如下:
!
-源码目录:
src/instance/internal/state.js-->
Vue.prototype._initData=function(){
vardataFn=this.$options.data
vardata=this._data=dataFn?
dataFn():
{}
if(!
isPlainObject(data)){
data={}
process.env.NODE_ENV!
=='
production'
&
&
warn(
'
datafunctionsshouldreturnanobject.'
this
)
}
varprops=this._props
//proxydataoninstance
varkeys=Object.keys(data)
vari,key
i=keys.length
while(i--){
key=keys[i]
//therearetwoscenarioswherewecanproxyadatakey:
//1.it'
snotalreadydefinedasaprop
//2.it'
sprovidedviaainstantiationoptionANDthereareno
//templateproppresent
props||!
hasOwn(props,key)){
this._proxy(key)
}elseif(process.env.NODE_ENV!
){
Datafield"
+key+'
"
isalreadydefined'
+
asaprop.Toprovidedefaultvalueforaprop,usethe"
default"
propoption;
ifyouwanttopasspropvaluestoaninstantiation'
call,usethe"
propsData"
option.'
this
//observedata
observe(data,this)
在initData中我们要特别注意proxy方法,它的功能就是遍历data的key,把data上的属性代理到vm实例上。
_proxy方法的源码定义如下:
Vue.prototype._proxy=function(key){
isReserved(key)){
//needtostorereftoselfhere
//becausethesegetter/settersmight
//becalledbychildscopesvia
//prototypeinheritance.
varself=this
Object.defineProperty(self,key,{
configurable:
true,
enumerable:
get:
functionproxyGetter(){
returnself._data[key]
},
set:
functionproxySetter(val){
self._data[key]=val
})
proxy方法主要通过Object.defineProperty的getter和setter方法实现了代理。
在前面的例子中,我们调用vm.times就相当于访问了vm.data.times。
在_initData方法的最后,我们调用了observe(data,this)方法来对data做监听。
observe方法的源码定义如下:
src/observer/index.js-->
exportfunctionobserve(value,vm){
value||typeofvalue!
object'
return
varob
if(
hasOwn(value,'
__ob__'
)&
value.__ob__instanceofObserver
){
ob=value.__ob__
}elseif(
shouldConvert&
(isArray(value)||isPlainObject(value))&
Object.isExtensible(value)&
!
value._isVue
ob=newObserver(value)
if(ob&
vm){
ob.addVm(vm)
returnob
observe方法首先判断value是否已经添加了ob属性,它是一个Observer对象的实例。
如果是就直接用,否则在value满足一些条件(数组或对象、可扩展、非vue组件等)的情况下创建一个Observer对象。
接下来我们看一下Observer这个类,它的源码定义如下:
exportfunctionObserver(value){
this.value=value
this.dep=newDep()
def(value,'
this)
if(isArray(value)){
varaugment=hasProto
?
protoAugment
:
copyAugment
augment(value,arrayMethods,arrayKeys)
this.observeArray(value)
}else{
this.walk(value)
Observer类的构造函数主要做了这么几件事:
首先创建了一个Dep对象实例(关于Dep对象我们稍后作介绍);
然后把自身this添加到value的ob属性上;
最后对value的类型进行判断,如果是数组则观察数组,否则观察单个元素。
其实observeArray方法就是对数组进行遍历,递归调用observe方法,最终都会调用walk方法观察单个元素。
接下来我们看一下walk方法,它的源码定义如下:
Observer.prototype.walk=function(obj){
varkeys=Object.keys(obj)
for(vari=0,l=keys.length;
i<
l;
i++){
this.convert(keys[i],obj[keys[i]])
walk方法是对obj的key进行遍历,依次调用convert方法,对obj的每一个属性进行转换,让它们拥有getter、setter方法。
只有当obj是一个对象时,这个方法才能被调用。
接下来我们看一下convert方法,它的源码定义如下:
Observer.prototype.convert=function(key,val){
defineReactive(this.value,key,val)
convert方法很简单,它调用了defineReactive方法。
这里this.value就是要观察的data对象,key是data对象的某个属性,val则是这个属性的值。
defineReactive的功能是把要观察的data对象的每个属性都赋予getter和setter方法。
这样一旦属性被访问或者更新,我们就可以追踪到这些变化。
接下来我们看一下defineReactive方法,它的源码定义如下:
exportfunctiondefineReactive(obj,key,val){
vardep=newDep()
varproperty=Object.getOwnPropertyDescriptor(obj,key)
if(property&
property.configurable===false){
//caterforpre-definedgetter/setters
vargetter=property&
property.get
varsetter=property&
property.set
varchildOb=observe(val)
Object.defineProperty(obj,key,{
functionreactiveGetter(){
varvalue=getter?
getter.call(obj):
val
if(Dep.target){
dep.depend()
if(childOb){
childOb.dep.depend()
for(vare,i=0,l=value.length;
e=value[i]
e&
e.__ob__&
e.__ob__.dep.depend()
returnvalue
functionreactiveSetter(newVal){
if(newVal===value){
if(setter){
setter.call(obj,newVal)
val=newVal
childOb=observe(newVal)
dep.notify()
defineReactive方法最核心的部分就是通过调用Object.defineProperty给data的每个属性添加getter和setter方法。
当data的某个属性被访问时,则会调用getter方法,判断当Dep.target不为空时调用dep.depend和childObj.dep.depend方法做依赖收集。
如果访问的属性是一个数组,则会遍历这个数组收集数组元素的依赖。
当改变data的属性时,则会调用setter方法,这时调用dep.notify方法进行通知。
这里我们提到了dep,它是Dep对象的实例。
接下来我们看一下Dep这个类,它的源码定义如下:
src/observer/dep.js-->
exportdefaultfunctionDep(){
this.id=uid++
this.subs=[]
//thecurrenttargetwatcherbeingevaluated.
//thisisgloballyuniquebecausetherecouldbeonlyone
//watcherbeingevaluatedatanytime.
Dep.target=null
Dep类是一个简单的观察者模式的实现。
它的构造函数非常简单,初始化了id和subs。
其中subs用来存储所有订阅它的Watcher,Watcher的实现稍后我们会介绍。
Dep.target表示当前正在计算的Watcher,它是全局唯一的,因为在同一时间只能有一个Watcher被计算。
前面提到了在getter和setter方法调用时会分别调用dep.depend方法和dep.notify方法,接下来依次介绍这两个方法。
depend方法的源码定义如下:
Dep.prototype.depend=function(){
Dep.target.addDep(this)
depend方法很简单,它通过Dep.target.addDep(this)方法把当前Dep的实例添加到当前正在计算的Watcher的依赖中。
接下来我们看一下notify方法,它的源码定义如下:
Dep.prototype.notify=function(){
//stablizethesubscriberlistfirst
varsubs=toArray(this.subs)
for(vari=0,l=subs.length;
subs[i].update()
notify方法也很简单,它遍历了所有的订阅Watcher,调用它们的update方法。
至此,vm实例中给data对象添加Observer的过程就结束了
下一节:
Vue.js是如何进行指令解析的。
请在文库中搜索Vue源码解析:
深入响应式原理(中)