以前对看过比较多关于vue源码的文章,可是对于总体框架和流程仍是有些模糊,最后用chrome debug对vue的源码进行查看整理出这篇文章。。。。javascript
本文对vue的总体框架和总体流程进行简要的分析,不对某些具体的细节进行分析,全部须要对vue有初步的认识,包括对Object.defineProperty、虚拟DOM有必定了解,本文不会对Object.defineProperty、虚拟DOM的原理和细节进行分析。
vue大致能够分两个部分:
1.采用Object.defineProperty进行数据的双向绑定;
2.采用虚拟DOM技术进行视图渲染;vue
vue构造函数调用了this._init(options)方法,这个方法在initMixin中,如上图所示,进入initMixin
initMixin主要完成数据的初始化和视图的初始化:
1.数据初始化主要是数据的observe,在上图的initState中进行;
2.视图的初始化在vm.$mount(vm.$options.el),其中vm为Vue的实例,watcher的设置也是在vm.$mount(vm.$options.el)中完成的;
咱们能够看到这里定义了beforeCreated和created这两个钩子函数。java
接着上面咱们看看数据初始化都作了什么,进入initState
这里咱们主要对数据进行操做的是initData,传入的是vm,咱们来具体看看initData:
咱们先忽略前面的一些逻辑判断,主要看两个地方:
1.数据代理,主要是将_data的数据代理到vm上,这样的话能够直接对vm上的数据进行修改;
2.数据observe,传入data;
咱们先看看vue怎么对数据进行observe的,进入observe
在observe里返回的是ob,也就是Observer类的实例,咱们看看Observer类是怎么定义的,进入Observer类
如上图在对data进行observe时对数组进行了特殊的处理,这块咱们先不看,先看通常状况下的处理,即调用this.walk(value)
walk主要对data的属性进行遍历,进入defineReactive
能够看到Object.defineProperty是在这里对属性设置get和set的,其中get主要进行依赖收集,其实就是在收集视图渲染的watcher,后面会提到,set主要是数据更新时进行视图的更新
至此,数据的初始化就完成了,从上面的分析来看,数据的初始化主要的工做就是对数据进行observe。git
接着上面,在vue入口那里,咱们知道视图的挂载主要是调用了vm.$mount(vm.$options.el)
如图,因此咱们进入vm.$mount,看看里面都干了啥,在源码里面有两处地方涉及到$mount
这是第一处,就是return mountComponent
这是第二处,上面两个图是一块儿的,屏幕大小有限,因此截了两个图。。。
我们看看第二处,里面作了一个处理,就是将template编译成render函数,在vue的教程里有render函数的使用,这里咱们能够看出咱们在组件里定义render函数会比定义template快,由于在定义template的组件挂载时多了一步将template编译成render函数;
第二处的return 仍是调用了第一处,因此咱们看看第一处调用的mountComponent方法,进入mountComponent
上面两个图是一块儿的,屏幕大小有限,因此截了两个图。。。
这里咱们能够看到定义了两个钩子beforeMount和mount,中间调用了watcher,咱们看一下这里watcher的定义,这里标注的不太好,挡住了。。。咱们看看watcher的这行代码:
github
vm._watcher=new Watcher(vm,updateComponent,noop)
咱们能够看到Watcher类主要传入了vm,updateComponent,noop三个参数,其中updateComponent的主要做用是将虚拟DOM转化为真实的DOM并进行挂载,具体的细节下面在讨论,咱们下面看看Watcher类是怎么定义的,进入Watcher
这里咱们注意两个地方,一个是this.getter的定义,这里就是上面传进来的updateComponent,还有就是执行this.get(),咱们进入这个get方法
这里咱们看到首先收集的依赖是当前watcher实例,而后调用getter方法也就是updateComponent方法,以前咱们对updateComponent方法的做用进行了简单的说明,这里咱们具体看看updateComponent都干了啥,进入updateComponent:
这里调用了vm._update方法,其中传入的参数有vm._render(),_render函数主要的做用是产生虚拟DOM,进入_update
这里主要是将虚拟DOM转化为真实DOM并进行挂载,分两种状况,分别是有旧的虚拟DOM和无旧的虚拟DOM,对应初始化时调用仍是数据更新时调用,这里定义了一个钩子beforeUpdate
到这里,视图的初始化和挂载也结束了,下面看看数据变化时视图是如何更新的chrome
接着上面咱们看看数据变化时视图是怎么变化的,在数据初始化的时候,咱们知道数据变化时将触发set方法,以下图:
上图能够看出,set最后调用了dep.notify,进入notify
如上图,notify主要将收集的依赖,也就是收集的全部watcher,调用全部watcher的update方法,咱们看看watcher的updata方法干了啥
这里就是调用了queueWatcher,进入queueWatcher
这里采用队列异步更新,就是讲=将watcher push进队列queue中,而后执行nextTick方法,进入nextTick
上面两个图是一块儿的,屏幕大小有限,因此截了两个图。。。
这个部分有点难看,cb为传入的flushSchedulerQueue函数,执行timerFunc,将nextTickHander加入异步队列,执行nextTickHander,执行cb,既执行flushSchedulerQueue,进入flushSchedulerQueue
上面两个图是一块儿的,屏幕大小有限,因此截了两个图。。。
主要看watcher.run(),进入watcher.run
执行了this.get(),即进入前面数据渲染和挂载的地方
到这里,vue整个的执行流程基本就结束了。segmentfault
盗用一下vue官网关于vue生命周期的图,对照以前的内容梳理一下:
对照上面的分析基本上能够找到各个钩子函数的位置,下面那个销毁的我就没用作分析了。。。数组
你们有兴趣的话能够关注一下个人博客框架