先从new Vue()的小例子来看,vue初始化的运行;vue源码下载地址请转到vue源码下载及运行笔记
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>源码解读</title>
<script src="../dist/vue.js"></script>
</head>
<body>
<div id="app">{{msg}}</div>
<script>
debugger
new Vue({
el: '#app',
data: {
msg: '源码解读'
}
})
</script>
</body>
</html>
在浏览器中运行代码,然后debugger查看
通过上图可以看到,在调用vue时,首先会判断vue是否已经实例化,如果没有实例化在控制台会提示必须由关键字new调用。接下就是调用vue的初始化方法this._init(options)进行初始化。
追踪来源this._init()
小例子中的vue文件引入是在dist文件夹中,dist文件夹是混淆后的文件。从vue的源码构建过程可以知道所有vue的开发源码都在src/core文件加下。打开core文件夹的index.js文件。
import Vue from './instance/index'
export default Vue
由第一行可以找到instance文件下的index.js文件。在这个文件中就看到动画中的vue实例化的判断和初始化的调用。initMixin(Vue)将Vue作为参数实现了_init()方法的挂载
import { initMixin } from './init'
function Vue (options) {
// 如果不是生产环境且不是用new实例化vue,在控制台输出警告
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)}
// 挂载初始化方法initMixin(Vue)
// 导出vue实例,添加全局方法export default Vue
init文件中initMixin方法实现代码如下:
let uid = 0
export function initMixin (Vue) {
// 在vue原型上添加初始化方法_init()方法
// 接收原始对象类型的options参数,为非必传
Vue.prototype._init = function (options?: Object) {
// 将实例对象赋值给vm变量
// 这里会再次进行component类型检查确保vm接收到的是Vue类的实例
const vm = this
// a uid
vm._uid = uid++
vm._isVue = true
// 合并option对象
if (options && options._isComponent) {
// 内部组件的options初始化
initInternalComponent(vm, options)
} else {
// 执行option的合并函数,并赋值给vm的公共属性
// 在这里的合并函数主要是解决与继承自父类的配置对象的合并
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm )
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// 暴露实例对象
vm._self = vm
// 初始化实例的生命周期相关的属性
initLifecycle(vm)
// 初始化实例事件的相关属性和监听功能
initEvents(vm)
// 初始化渲染函数相关的属性
initRender(vm)
// 调用生命周期钩子函数beforeCreate
callHook(vm, 'beforeCreate')
// 初始化父组件注入属性
initInjections(vm) // resolve injections before data/props
// 初始化状态相关的属性
initState(vm)
// 初始化子组件属性提供器
initProvide(vm) // resolve provide after data/props
// 调用生命周期钩子函数created
callHook(vm, 'created')
// 执行DOM挂载函数
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
至此可以看到初始化方法_init()方法的整个前后引用关系一目了然。
init()方法都做了些什么
从以上init方法的源码中可以知道,它做了以下几件事情:
- 将this绑定到当前vm对象
- 给每个vue实例对象添加唯一uid标识
- 将vm的isVue属性设置为true,方便以后过滤用
- 合并options对象
- 暴露实例对象
- 初始化生命周期、事件相关功能、渲染函数、调用生命周期钩子函数等
- 替换options中传入的DOM