Vue项目的入口文件中经常有下面代码:
new Vue({ render: h => h(App), }).$mount('#app')
要理解这个过程,首先要有以下知识储备:
创建一个vue实例
const vm = new Vue(options) vm.__proto__ === Vue.prototype
1、vm的构造函数是Vue(ES6:vm所属的类是Vue)
2、options是new Vue的参数,一般称之为选项或构造选项(选项式API)
options选项里面有什么?
- 数据: data / props 属性 / propsData / computed 被计算出来的 / methods 方法 / watch 观察
- DOM: el 容器或挂载点 / template是html内容 / render / renderError
- 生命周期钩子: beforeCreate / created / beforeMount / mounted / beforeUpdate / updated / activated / deactivated / beforeDestroy / destroyed / errorCaptured
- 资源: directives 指令 / filters 过滤 /component 组件
- 组合: parent / mixins 混入 / extends 扩展 / provide 提供 / inject 注入
el--挂载点
// 基本语法
const vm = new Vue({
// 当是string时,为css选择器;
el: 'css选择器(标签选择器\id选择器....等) | HTMLElement'
})
创建出vue的实例后再render并挂载:
new Vue({
render: h => h(app)
}).mount('#app')
// 等价于(原因看后面):
new Vue({
render: h => h(app),
el:'#app
})
// 在vue构造函数外部,可以使用vm.$el去访问改DOM节点
另外,再体验一把new Vue的传参吧:
const app = new Vue({
// el: '#app',
el: document.querySelector('#app'),
data: {
message: 'hello world!'
},
//
template:
`
<div>
<div>{{ message }}</div>
<button @click="changeMsg">点我改变信息</button>
</div>
`,
methods: {
changeMsg () {
this.message = 'i was changed!'
}
}
})
回到Vue的构造函数,让我们来look look源码吧~(版本为2.6.14,点击查看)
定位到src/core/instance/index.js,源码:
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
可以看到调用了_init方法,可以从src/core/instance/init.js
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
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')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
从源码的最后一个if判断可以看出,通过el挂载不过是自动调用了$mount挂载方法,所以上述的那个疑问解决了;
其次,通过_init方法可以看到一系列的初始化内容:
initLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm, 'beforeCreate')initInjections(vm)initState(vm)initProvide(vm)callHook(vm, 'created')
从上面的命名可以看出,这一系列函数是初始化生命周期、事件、渲染、inJect、State等内容,所以可以总结一下new Vue中主要做了什么事情:
- 调用
_init方法。 - 通过
mergeOptions函数合并options并且赋值给Vue上的$options属性。 - 初始化生命周期、事件、渲染、Inject、State等内容。