废话不多说,我们直接开搞源码,初始化创建一个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。
整个实例化的过程就完成了。