以Vue2.6.14做例子解读Vue源码
Vue源码入口
Vue构造函数声明
this._init
this._init 做了
1.自增 uid 为了每个组件做区别
2.对组件的配置项进行处理,如果是子组件的话,做了性能优化,把一些属性挂在options上,减少运行时原型链的查找,如果是根组件的话,就合并全局配置到根组件上
比如根组件是
Vue. component( 'comp',{
template: '<div>I am comp</div>'
})
new Vue({
el:'#app',
data:{}
})
// 根组件 选项合并 将全局配置选项合并到根组件的局部配置上
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
经过根组件的选项合并后,就会合并成下面的形式
new Vue({
el:'#app',
data:{},
components:{
comp:comp
}
})
3.一系列的初始化以及挂载
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
vm._uid = uid++
vm._isVue = true
// 处理组件配置项
// merge options
if (options && options._isComponent) {
// 子组件:性能优化 减少运行时原型链的查找 提高执行效率
initInternalComponent(vm, options)
} else {
// 根组件 选项合并 将全局配置选项合并到根组件的局部配置上
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
vm._self = vm
// 定义组件的关系属性 $parent $root ......
initLifecycle(vm)
// 初始化自定义事件
initEvents(vm)
// 初始化slot 获取this.$slots
// 定义 this._c 就是 createElement 方法 平时使用的h函数
initRender(vm)
// 执行 beforeCreate 生命周期
callHook(vm, 'beforeCreate')
// 注入
initInjections(vm) // resolve injections before data/props
// 响应式
// 初始化 props methods data watch computed
initState(vm)
// 依赖提供
initProvide(vm) // resolve provide after data/props
// 执行 created 生命周期
callHook(vm, 'created')
// 挂在在 mount 上
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
this._init 中的 initLifeCycle
主要是定义一些定义组件的关系属性,比如 root
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
this._init 中的 initEvents
主要是定义一些自定义事件的
export function initEvents (vm: Component) {
vm._events = Object.create(null)
vm._hasHookEvent = false
// init parent attached events
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
比较有趣的点是
<comp @click='handleClick'></comp>
大部分人认为在父组件中使用子组件的handleClick是父组件操控的,其实是子组件自己操控的
父组件
this.$emit('click')
子组件
this.$on('click',function handleClick() {})
this._init 中的 initRender
主要是初始化slot 获取this.$slots,定义 this._c 就是 createElement 方法 平时使用的h函数
export function initRender (vm: Component) {
vm._vnode = null // the root of the child tree
vm._staticTrees = null // v-once cached trees
const options = vm.$options
const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
const renderContext = parentVnode && parentVnode.context
vm.$slots = resolveSlots(options._renderChildren, renderContext)
vm.$scopedSlots = emptyObject
// bind the createElement fn to this instance
// so that we get proper render context inside it.
// args order: tag, data, children, normalizationType, alwaysNormalize
// internal version is used by render functions compiled from templates
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
// $attrs & $listeners are exposed for easier HOC creation.
// they need to be reactive so that HOCs using them are always updated
const parentData = parentVnode && parentVnode.data
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
}, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
}, true)
} else {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
}
}
this._init 中的 callHook(vm, 'beforeCreate')
执行 beforeCreate 生命周期
this._init 中的 initInjections
日常开发基本用不到,都是用于高阶组件的封装
this._init 中的 initState
响应式核心模型,初始化 props methods data watch computed
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
this._init 中的 initProvide
给 initInjections 提供接收
this._init 中的 callHook(vm, 'created')
执行 created 生命周期
this._init 中的 el mount
主要对有el的自动调用 mount 挂载在 template 上
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
如果模板没有el,就必须手动挂载上去
new Vue({
data:{}
}).$mount()