vue源码解析(二)--vue实例化

219 阅读1分钟

废话不多说,我们直接开搞源码,初始化创建一个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源码

image.png

我们可以看到实例化vue后,vue内部直接调用了_init方法。进入init方法,首先是对参数进行的处理,然后就是对该实例初始化了各种属性和方法。 image.png 通过方法名字我们都可以得出:

1.initLifecycle初始化生命周期相关的属性

image.png

2.initEvents初始化事件监听(略)

3.initRender初始化渲染相关的函数,这里重点是挂在h函数,也就是_c$createElement。还有$attr和$listenners变成响应式的,这里我们要记住defineReactive这个方法,把数据变成响应式(getter和setter)的都是通过这个方法完成的

image.png

4.callHook(vm, 'beforeCreate'),调用钩子函数,如果用户写了beforeCreate钩子函数,则这里会被触发。

5.initState初始化data。这个方法非常重要,他将会把data数据递归转换成响应式数据。

在这个方法中,会为每一个data数据中的对象初始化一个Observer,挂在在对象的__ob__属性上的,Obserber对象的主要作用就是递归循环为每个属性添加gettersetter,并且每个属性都会有一个dep对象,他是专门存放watcher的,用于数据改变,通知watcher更新视图。

在为属性添加响应式时,如果一个属性也是一个对象,那这个对象也会添加一个__ob__属性,继续向下为其属性添加响应式。如果属性是一个数组时,则会修改数组的原型方法,pop/shift/unshift/push/reverse/sort/splice等方法,为其添加响应式。

6.callHook(vm, 'created'),调用钩子函数,如果用户写了created钩子函数,则这里会被触发。

初始化数据后,调用了$mount方法进行挂在。在挂在dom元素阶段,首先是判断了用户传入了render函数没,如果没传render函数,则判断传入了template模版字符串没,如果传了模版字符串,且vue支持编译器功能,则会把模版字符串转换为render函数,如果rendertemplate都没传,则会去获取el元素作为模版字符串,然后转换为render函数。render函数准备好后,继续初始化,之后就是生成了watcher对象,在首次执行watcher对象时,调用了render函数生成了虚拟dom,然后在实例化VM的_update方法中,调用了patch方法,进行了diff算法的比较,更新了真实dom

vue首次渲染.png

整个实例化的过程就完成了。