
入口文件src/core/instance/index
定义了Vue构造函数,初始化Vue相关属性
//this instanceof Vue是不是通过new Vue出来的实例
function Vue(options) {
// 在initMixin中,初始化配置
this._init(options)
}
// Vue 的 prototype 上扩展⼀些⽅法
initMixin(Vue) //挂载初始化方法(_init)
//挂在状态处理方法:对Vue.prototype上的data、props属性进行劫持,并在上面挂载set、del方法
stateMixin(Vue)
//在Vue.prototype上创建 on off once emit方法
eventsMixin(Vue) //挂载事件的方法
//定义了_update方法、forceUpdate方法
lifecycleMixin(Vue) //挂载生命周期方法
//原型上挂载nextTick、定义了_render(生成Vnode)
renderMixin(Vue) //挂载与渲染有关的方法
initMixin
在Vue原型上定义了_init函数,该函数主要功能是
- vm._uid = uid++ 给每一个组件进行标识
- vm._isVue = true 判断是否是Vue实例,如果是则不需要被observed响应话
- merge options 合并配置项
- 定义了vm._renderProxy ,作用是在render中将this指向vm._renderProxy
- 初始化生命周期,事件,Render函数等等
- 组件挂载
let uid = 0;
export function initMixin(Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
//每一个类型实例都会有唯一标识
vm._uid = uid++
// a flag to avoid this being observed
// 是否需要响应化,
// 如果是Vue的实例,则不需要被observe
vm._isVue = true
// merge options
// 第一步: options参数的处理
if (options && options._isComponent) {
initInternalComponent(vm, options)
} else {
// mergeOptions合并属性,为options增加属性
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
// 第二步: renderProxy
if (process.env.NODE_ENV !== 'production') {
// 定义了vm._renderProxy,这是后期为render做准备的,作用是在render中将this指向vm._renderProxy
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
// 初始化数据,传入vm实例
// vm的状态初始化,prop/data/computed/method/watch都在这里完成初始化,因此也是Vue实例create的关键。
initState(vm) //重点,初始化状态数据 (data property等)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
if (vm.$options.el) {
// 组件的挂载
/**
* 先调用扩展的$mount方法,生成render
* 在调用原始的$mount 获得元素,在调用mountComponent方法
*/
vm.$mount(vm.$options.el)
}
}
}
为什么在created之后才能拿到数据?
因为在beforeCreate之前没有initstate,而调用initstate之后,data生成了,
然后组件实例创建成功执行的created
initLifecycle(vm) //初始化生命周期的 一些状态变量
initEvents(vm) //初始化事件的容器
initRender(vm) //初始化创建元素的方法
callHook(vm, 'beforeCreate') //调用生命周期函数
// 初始化注入器
initInjections(vm) // resolve injections before data/props
// 初始化数据,传入vm实例
// vm的状态初始化,prop/data/computed/method/watch都在这里完成初始化,因此也是Vue实例create的关键。
initState(vm) //重点,初始化状态数据 (data property等)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
initState
初始化props、methods、data、computed、watch,对其属性创建watcher响应化
// 初始化数据,传入vm实例
export function initState(vm: Component) {
vm._watchers = []
const opts = vm.$options
// 处理options.props成员
if (opts.props) initProps(vm, opts.props)
// 处理options.methods成员
if (opts.methods) initMethods(vm, opts.methods)
// vm存在属性data
// 处理options.data 响应式化
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
// 处理options.computed 计算属性
if (opts.computed) initComputed(vm, opts.computed)
// 处理options.watch
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
initProps
// 属性响应式化
defineReactive(props, key, value)
if (!(key in vm)) {
// 将_proxy上的成员映射到Vue实例上,不需要app._props.xxx直接app.xxx
proxy(vm, `_props`, key)
}
initData
// 对应的data数据
let data = vm.$options.data
// 判断data是函数还是对象
// getData 改变指向data.call(vm, vm),相当于返回一个新的data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// 如果不是function就报一个警告
//循环data中每一个属性,定义在data中的变量不能和props methods重名,
hasOwn(methods, key)
hasOwn(props, key)
//将data代理到实例上
proxy(vm, `_data`, key)
//将data响应话
observe(data, true /* asRootData */)
initComputed
循环computed对象里的每一项,对其创建watcher响应化,不能和data props methods 重名
否则报出警告
initMethods
- 判断是否是函数
- 是否与props重名
// 将methods属性的中方法绑定上下文后挂载到Vue实例上
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
initWatch
对组件内watcher对象的每个属性建立一个watcher
props、methods、data、computed 重名问题
首先会有警告,但是页面显示数据优先级如下
prop>data
data>computed>methods
computed>methods>props
有个比较特别的,根组件中好像methods>computed,大家可以试一下
stateMixin
对Vue.prototype上的data、props属性进行劫持,对其响应化,并在上面挂载set、del方法
Object.defineProperty(Vue.prototype, "$data", dataDef);
Object.defineProperty(Vue.prototype, "$props", propsDef);
Vue.prototype.$set = set;
Vue.prototype.$delete = del;
eventsMixin
在Vue.prototype上创建 on off once emit方法,进行事件监听,关闭,监听一次,触发事件
lifecycleMixin
- 定义了_update方法
- forceUpdate方法,就是调用vm._watcher.update(),触发依赖更新
- Vue.prototype上定义destroy方法
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
if (!prevVnode) {
// 首次渲染
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
} else {
// 数据更新,diff 返回新的DOM
// updates
vm.$el = vm.__patch__(prevVnode, vnode);
}
};
Vue.prototype.$forceUpdate = function () {
const vm: Component = this;
if (vm._watcher) {
vm._watcher.update();
}
};
renderMixin
- 原型上挂载nextTick
- 定义了_render
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this);
};
// render函数,主要实现:vnode = render.call(vm._renderProxy, vm.$createElement)
Vue.prototype._render = function (): VNode {
const vm: Component = this;
const { render, _parentVnode } = vm.$options;
vm.$vnode = _parentVnode;
// render self
let vnode;
try {
//调用_renderProxy生成Vnode
vnode = render.call(vm._renderProxy, vm.$createElement);
}
if (Array.isArray(vnode) && vnode.length === 1) {
vnode = vnode[0];
}
// return empty vnode in case the render function errored out
//如果说render函数报错了就返回一个空的Vnode
if (!(vnode instanceof VNode)) {
vnode = createEmptyVNode();
}
// set parent
//将vnode的parent指向_parentVnode
vnode.parent = _parentVnode;
return vnode;
};