初始化阶段
Vue 的初始化过程(new Vue(options))都做了什么?
-
处理组件配置项
- 初始化根组件时进行了选项合并操作,将全局配置合并到根组件的局部配置上
- 初始化每个子组件时做了一些性能优化,将组件配置对象上的一些深层次属性放到 vm.$options 选项中,以提高代码的执行效率
-
初始化组件实例的关系属性,比如
$parent、$children、$root、$refs
等 -
处理自定义事件
-
调用 beforeCreate 钩子函数
-
初始化组件的 inject 配置项,得到 ret[key] = val 形式的配置对象,然后对该配置对象进行响应式处理,并代理每个 key 到 vm 实例上
-
数据响应式,处理 props、methods、data、computed、watch 等选项
-
解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上
-
调用 created 钩子函数
-
如果发现配置项上有 el 选项,则自动调用 $mount 方法,也就是说有了 el 选项,就不需要再手动调用 $mount 方法,反之,没提供 el 选项则必须调用 $mount
-
接下来则进入挂载阶段
export function initMixin (Vue) {
Vue.prototype._init = function (options) {
const vm = this
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
vm.$mount
使用 vm.$mount( [elementOrSelector] )
,返回vm实例本身,
var MyComponent = Vue.extend({
template:'<div>Hello!</div>',
})
<!-- 创建并挂载到#app(会替换#app) -->
new MyComponent().$mount('#app');
new MyComponent().$mount({el:'#app'});
var component = new MyComponent().$mount();
document.getElementById('app').appendChild(component.$el);
完整的构建版本中,vm.mount 首先会检查template或el选项所提供的模板是否已经转换成渲染函数(render函数)。
如果没有,则立即进入编译过程,将模板编译成渲染函数,完成之后再进入挂载与渲染的流程中。
完整版包含只包含运行时版本。
const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el){
<!-- 做些什么 -->
return mount.call(this,el);
}
通过函数劫持,可以在原始功能上新增一些其他功能,在完整版中有编译功能
编译器
-
1 是否有render选项
在实例化Vue.js时给出了render选项,那么template其实是无效的,
因为不会进入模板编译的流程,而是直接使用render选项中提供的渲染函数。 -
2 是否设置了template选项
如果用户没有通过template选项设置模板,那么会从el选项中获取HTML字符串当作模板。
如果用户提供了template选项,那么需要对它进一步解析,因为这个选项支持很多种使用方式。
template选项可以直接设置成字符串模板,也可以设置为以#开头的选择符,还可以设置成DOM元素。 -
3 从不同的格式中将模板解析出来
-
4 渲染函数
获取模板之后,下一步是将模板编译成渲染函数,通过执行compileToFunctions函数可以将模板编译成渲染函数并设置到this.options上
只包含运行时版本的vm.$mount的实现原理
1、$mount方法将ID转换为DOM元素后,使用mountComponent函数将Vue.js实例挂载到DOM元素上。
2、将实例挂载到DOM元素上指的是将模板渲染到指定的DOM元素中,而且是持续性的,以后当数据(状态)发生变化时,依然可以渲染到指定的DOM元素中。
3、实现这个功能需要开启watcher。
4、mountComponent方法会判断实例上是否存在渲染函数。如果不存在,则设置一个默认的渲染函数createEmptyVNode,该渲染函数执行后,会返回一个注释类型的VNode节点。
5、事实上,如果在mountComponent方法中发现实例上没有渲染函数,则会将el参数指定页面中的元素节点替换成一个注释节点,并且在开发环境下在浏览器的控制台中给出警告。
- mountComponent具体实现
export function mountComponent(vm,el){
if(!vm.$options.render){
vm.$options.render = createEmptyVNode;
if(process.env.NODE_ENV !== 'production'){
<!-- 在开发环境发出警告 -->
}
<!-- 触发生命周期钩子 -->
callHook(vm,'beforeMount');
<!-- 挂载 -->
vm._watcher = new Watcher(vm,()=>{
vm._update(vm._render())
},noop);
<!-- 触发生命周期钩子 -->
callHook(vm,'mounted');
return vm;
}
}
-
vm._update作用:调用虚拟DOM中的patch方法来执行节点的比对与渲染操作。
-
vm._render作用:执行渲染函数,得到一份新的VNode节点树。
-
vm._update(vm._render())作用:先调用渲染函数得到一份最新的VNode节点树,然后通过vm._update方法对最新的VNode和上一次渲染用到的旧VNode进行对比并更新DOM节点。简单来说,就是执行了渲染操作。