Vue initMixin
接下来我们看看new Vue()都会发生些什么事情
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)
}
initMixin(Vue)
在我们对new Vue()时,会调用this._init()
方法开始生成创建出一个vue实例,这个方法是initMixin中进行定义
在init.js我们可以找到这个方法,我们来对这个方法来进行拆分分析
export function initMixin(Vue: Class<Component>) {
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)
}
}
}
performance 性能
下面这段代码是在window环境下,使用window.performance来进行前端性能监控
mark方法就是window.performance.mark
measure方法就是window.performance.measure
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)
}
...
/* 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)
}
我们可以这样理解上面的mark就是打点,一个开始节点,一个结束节点,然后通过measure获取两个节点之间的情况,并记录下来
vm._name 如果是根组件拿到的就是 <Root>
如果是子组件,如<demo></demo>
这样的组件,我们拿到的就是<Demo>
因此这里的measure(`vue ${vm._name} init`, startTag, endTag)
就等于measure(`vue <Root> init`, startTag, endTag)
接下来我们可以通过在控制台输入performance.getEntriesByName("vue <Root> init");
来获取数据
如果不理解里面各参数代表的意义,我们可以在刚刚的代码上加入performance.now
来查看
let beginTime,endTime
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
beginTime = performance.now();
console.log('开始时间',beginTime) //打印开始时间
mark(startTag)
}
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
endTime = performance.now();
console.log('结束时间',endTime); //打印结束时间
console.log('相差时间',endTime - beginTime); //打印相差时间
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
console.log(performance.getEntriesByName("vue <Root> init"))
}
由此我们便可以知道startTime就是开始时间,duration就是开始时间和结束时间的时间差
beforeCreate 生命周期
接下来我们看在beforeCreate之前,都进行了哪些初始化的操作
resolveConstructorOptions(vm)
// merge options
if (options && options._isComponent) {
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
options._isComponent是判断元素是否为组件,首先我们可以在用debugger的方式,看下这段代码做了些什么操作
首先看没有执行之前,vm是什么样的
接下来看执行之后
通过断点的方式,我们可以看的出来,这段代码主要是用于创建$options合并我们传入vue的options;
initProxy(vm)
通过刚刚的断点法,我们很快就可以知道initProxy方法,给我们vm的_renderProxy进行了赋值操作
initProxy = function initProxy(vm) {
if (hasProxy) {
// determine which proxy handler to use
const options = vm.$options;
const handlers =
options.render && options.render._withStripped
? getHandler
: hasHandler;
vm._renderProxy = new Proxy(vm, handlers);
} else {
vm._renderProxy = vm;
}
};
我们可以先简单了解下,hasProxy是利用/native code/.test(Proxy)
判断当前浏览器是否支持Proxy,不支持则将vm直接赋值给vm._renderProxy,如果在支持的情况下,先判断是否有render的写法,如果没有,则利用Proxy去改写vm的has方法,也就是let key in target
这一类操作,如果存在,则改写get方法。具体可以去了解Proxy的用法。
initProxy方法使用Proxy主要是在操作vm属性的时候,判断该属性是否存在,给出一些警告提示之类的。
initLifecycle(vm)
这次我们直接看源码,可以从很清楚的看出,initLifecycle主要处理组件的父子关系,定义了parent,children,refs,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
}
我们可以看下的具体处理方式
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
由于是处理父子关系,应该我们可以再定义一个组件
然后进行打印和断点测试都可以,我们就继续选择断点
首先是第一次进入,很明显parent是空的,因为这次进入的是根组件,接下来就是demo组件了
这个时候,我们可以很清楚的看到组件demo的vm.$options.parent是存在值的,就是我们的根组件,而在获取到父组件以后,还会判断options.abstract,这个是用来判断,父组件是否为抽象组件,如<keep-alive><transition>
这一类是不会成为父组件的,因此要对此进行判断。
还有一些属性,我们等到被赋值的时候,再来看具体效果。
initEvents(vm)
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)
}
}
首先我们看第一句代码,这句代码创建一个以null为原型的空对象,并赋值给了_events
vm._events = Object.create(null)
第二行,主要用于判断是否存在hook类的事件,如果存在,在vm.$on的时候,会赋值为true
vm._hasHookEvent = false
下面这部分代码,我们主要看下具体作用。
// init parent attached events
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
我们可以先在组件上定义两个事件
接着进行断点,可以看出根组件进入时为undefined
demo组件进入时,可以看出listeners就是我们通过v-on或者@绑定在demo子组件上的事件。
当子组件上存在绑定的事件时,则调用updateComponentListeners,使用vm.$on的方法,将事件添加到vm.__events上,并判断是否存在hook类的事件,如果存在则将vm._hasHookEvent赋值为true
initRender(vm)
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
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
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)
}
}
从源码中可以在initRender定义了$createElement
这个方法,并调用defineReactive使用Object.defineProperty定义了$attrs,$listeners
两个属性。
以上就是beforeCreate之前vue进行的操作了。我们可以通过断点的方式看下在beforeCreate之前,我们可以拿到哪些值
根组件
demo组件
以上就是beforeCreate之前的一些简单理解了,在后面我们在对created之前的操作进行分析,created分析结束以后,我们再来理一理其中的一些方法具体的实现方式。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!