官网的描述对$mount的描述是:
如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。
在 core/instance/init.js中,我们可以看到是这样执行$mount:
下面,我们主要看一下$mount内部实现的机制。
运行时 + 编译器 vs. 只包含运行时
在Vue.js官网教程里,开始就介绍了关于vue的完整版和runtime两个版本:
- 完整版:同时包含编译器和运行时的版本。
- 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。
- 运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。
其实Vue的完整版渲染过程可以通过这个图来更好的进行了解:
其大致过程为: html字符串 → render函数 → vnode → 真实dom节点 而运行时渲染即所谓的去掉编译器的过程:render函数 → vnode → 真实dom节点。我们来看看源码里是不是这么实现的。 首先是完整版的实现:
|
|
然后我们再看看runtime的实现:
render –> VNode
我们知道,$mount主要是实现了mountComponent方法,我们找到mountComponent方法,可以看一下:
注意:这里用到了我们之前所一直说明的Watcher,也就是在这里定义的。这里先说明一下依赖收集的过程:因为Vue数据里定义的Data可能并不是所有数据都是视图渲染所需要的。也就是说,我们需要知道哪些数据的变动是需要更新视图,哪些是不需要重新渲染视图的, 在我们执行vm._watcher = new Watcher(vm, updateComponent, noop)这句话的时候,会触发我们定义的Watcher里面的get方法。同时设置了Dep.target = watcher。get 方法会去执行传入的updateComponent 方法,也就是说会去做template –> AST –> render Function –> VNode –> patch Dom这样一个流程。这个过程中,会去读取我们绑定的数据。由于之前我们通过observer进行了数据劫持,这样会触发数据的get方法。此时会将watcher添加到 对应的dep中。当有数据更新时,通过dep.notify()去通知到watcher,然后执行watcher中的update方法。此时又会去重新执行get updateComponent,至此完成对视图的重新渲染。
这里我们主要注意渲染部分
|
|
vm._render函数返回一个vnode作为vm._update的第一个参数:
此处,使用call方法, 将this指向 vm.renderProxy,vm.renderProxy是个代理,代理vm,主要用来报错,如果render函数上使用了vm上没有的属性或方法,就会报错。 vm.$createElement 这个是创建vnode的方法,作为第一个参数传入。 我们写render函数的时候会是这样:
也就是说我们这里定义的render,将会被执行返回一个VNode:
|
|
vnode –> 真实的el节点
接下来也就是将我们的虚拟节点VNode转成真实的node节点。实现过程主要是通过vm.$el = vm.patch(prevVnode, vnode);这部分 也就是patch函数。
总结
这里我们粗略的过了一遍数据渲染成dom的过程,后面章节我们会详细介绍其中的整体过程。而且这里我们更多关注的是web方面的,对应weex的挂载就先不考虑了。