废话不多说,我们直接开搞源码,初始化创建一个vue实例。如果所示,我们就初始化了一个最简单的页面
<div id="app">
<span>{{message}}</span>
<h2 v-text='message'></h2>
<div>
<span>{{dog.name}}</span>
<span>{{dog.age}}</span>
<ul>
<li v-for='p in people'>{{p}}</li>
</ul>
</div>
</div>
<script>
// 在此处设置断点,接下来我们就要调试vue实例化都干了什么
const vm = new Vue({
el: '#app',
data: {
message: 'hello world',
people: ['tom', 'jack', 'xiwei'],
dog: {
name: 'tom',
age: 3
}
}
})
</script>
加断点在new Vue的地方,进入vue源码
我们可以看到实例化vue后,vue内部直接调用了_init
方法。进入init方法,首先是对参数进行的处理,然后就是对该实例初始化了各种属性和方法。
通过方法名字我们都可以得出:
1.initLifecycle
初始化生命周期相关的属性
2.initEvents
初始化事件监听(略)
3.initRender
初始化渲染相关的函数,这里重点是挂在h
函数,也就是_c
和$createElement
。还有$attr和$listenners变成响应式的,这里我们要记住defineReactive
这个方法,把数据变成响应式(getter和setter)的都是通过这个方法完成的
4.callHook(vm, 'beforeCreate')
,调用钩子函数,如果用户写了beforeCreate
钩子函数,则这里会被触发。
5.initState
初始化data。这个方法非常重要,他将会把data数据递归转换成响应式数据。
在这个方法中,会为每一个data数据中的对象初始化一个Observer
,挂在在对象的__ob__
属性上的,Obserber
对象的主要作用就是递归循环为每个属性添加getter
和setter
,并且每个属性都会有一个dep对象,他是专门存放watcher
的,用于数据改变,通知watcher
更新视图。
在为属性添加响应式时,如果一个属性也是一个对象,那这个对象也会添加一个__ob__
属性,继续向下为其属性添加响应式。如果属性是一个数组时,则会修改数组的原型方法,pop/shift/unshift/push/reverse/sort/splice
等方法,为其添加响应式。
6.callHook(vm, 'created')
,调用钩子函数,如果用户写了created
钩子函数,则这里会被触发。
初始化数据后,调用了$mount方法进行挂在。在挂在dom元素阶段,首先是判断了用户传入了render
函数没,如果没传render
函数,则判断传入了template
模版字符串没,如果传了模版字符串,且vue支持编译器功能,则会把模版字符串转换为render
函数,如果render
和template
都没传,则会去获取el元素作为模版字符串,然后转换为render
函数。render
函数准备好后,继续初始化,之后就是生成了watcher
对象,在首次执行watcher
对象时,调用了render
函数生成了虚拟dom,然后在实例化VM的_update方法中,调用了patch
方法,进行了diff算法
的比较,更新了真实dom
。
整个实例化的过程就完成了。