上一节我们通过分析找到了vue的入口文件src/core/instance/index.js
当执行import vue时,将会执行this._init方法,该方法在初始化时定义在initMixin中
该方法接收一个options,即new vue时传递的对象,形如
new Vue({
el:"#app",
data(){
return{
msg:"hello"
}
}
})
I-使用vm缓存this,即vue
II-对options进行合并
III-通过initState调用,如果data存在,则调用initData()对data进行处理
首先:获取定义的data,即实例化时传递的data函数
其次:对data函数中的this指向进行修正,使其指向vue
最后:确保在data中定义的属性msg未作为methods或props存在
IV-向el指定的元素上挂载vue,并转化data
vm.$mount是在Vue上挂载的方法,这在import Vue的时候已经被准备,打开entry-runtime-with-compiler.js
可以发现,在该文件引入了$mount同时又新建了一个$mount,那么为什么要重复定义呢?通过debugger,我发现我当前的代码并没有走缓存的那一个mount,而是直接走到了下边。于是我打算看看缓存的mount到底做了什么
如果是浏览器环境,则调用query方法
该方法调用原生dom API获取dom对象,即document.querySelector('#app'),也就是拿到了我们在根文件index.html中定义的id为app的div元素
接着执行mountComponent方法
首先判断是否定义了render函数,接着判断templete是否存在,然后就开始报警告
我们知道vue是支持多种书写格式的,既可以使用render函数,也可以使用模板,而这里却是只允许render,并紧接着抛出了runtime-only的警告
我恍然大悟,于是打开小窗口,vue init webpack test
也就是说,这两个$mount分别服务于两个版本的vuejs!!!
我在初始化项目时使用的是runtime+compiler版本,因此我着重看当前文件创建的mount方法
从框红的位置可以看出,vuejs不允许将html或者body作为根组件,并且对render进行了特别处理。那么问题有二:为什么必须定义一个空div作为根组件?;为什么偏偏要处理render?
展开if(!options.render)
I-获取根组件<div id="app"></div>
II-向options上挂载render方法
III-执行缓存的mount方法,再次进入mountComponent方法,此时options上存在render函数,因此不会在报warn
performance是与性能相关的需要在vue配置文件中开启的,因此代码会走向else逻辑,定义一个updateComponent函数,该函数在实例化Watcher时被调用,其中vm._render()负责生成一个虚拟dom,并交由vm._update进行对比更新后生成一个真实dom
那么render到底如何生产vnode呢?(render函数)
update又是如何将vnode更新为dom呢?(update)