今日目标:lifecycle.js
路径:src\core\instance\lifecycle.js
今天就分析这9个函数
setActiveInstance
// 设置active实例 export function setActiveInstance(vm: Component) {
// 记录之前的activeInstance const prevActiveInstance = activeInstance // 将传入的赋给activeInstance activeInstance = vm return () => {
// 返回之前的 activeInstance = prevActiveInstance } }
讯享网
initLifecycle
初始化生命周期相关的属性 以及为一些属性赋值
讯享网// 这里导出了initLifecycle 初始化生命周期相关的属性 以及为一些属性赋值 export function initLifecycle(vm: Component) {
// 获取选项 const options = vm.$options // locate first non-abstract parent // 定位第一个"非抽象"的父组件 // https://cn.vuejs.org/v2/api/#keep-alive 在这里可以看为什么要非抽象 // <keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。 let parent = options.parent // 定位第一个非抽象父组件 if (parent && !options.abstract) {
// 判断parent父亲节点是否存在,并且判断是否存在抽象节点 // 如果父实例parent是抽象组件,则继续找parent上的parent,直到找到非抽象组件为止 while (parent.$options.abstract && parent.$parent) {
// 如果有父亲抽象组件,则把父或爷爷节点给当前节点的父亲节点 parent = parent.$parent } // 子节点添加vm // 把当前vm实例push到定位的第一个非抽象parent的$children属性上 parent.$children.push(vm) } // 初始化一些属性 // 这里的parent可以告诉我们,子组件创建时,父组件已经存在了 // 添加$parent vm.$parent = parent // 判断parent是否是root 如果是 则把parent.$root赋给$root vm.$root = parent ? parent.$root : vm // 当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。 vm.$children = [] // 获取节点的key 一个对象,持有已注册过 ref 的所有子组件。 vm.$refs = {
} // 内部属性,不希望被访问的 vm._watcher = null // 组件实例相应的 watcher 实例对象 vm._inactive = null // 表示keep-alive中组件状态,如被激活,该值为false,反之为true。 vm._directInactive = false // 也是表示keep-alive中组件状态的属性。 vm._isMounted = false // 当前实例是否完成挂载(对应生命周期图示中的mounted)。 vm._isDestroyed = false // 当前实例是否已经被销毁(对应生命周期图示中的destroyed)。 vm._isBeingDestroyed = false // 是否已经销毁的组件 如果为true 则不触发 beforeDestroy 钩子函数 和destroyed 钩子函数 当前实例是否正在被销毁,还没有销毁完成(介于生命周期图示中deforeDestroy和destroyed之间)。 }
lifecycleMixin
初始化 _update, $forceUpdate, KaTeX parse error: Undefined control sequence: \src at position 14: destroy三个方法,在\̲s̲r̲c̲\core\instance\…destory中会触发两个生命周期钩子函数:beforeDestroy,destroyed。
这里面的_update要注意:在第一次更新和后面更新使用的是不同的__patch__,重载,传不同参数,因为第一次是没有创建vnode,所以需要更新vdom,之后更新就只是更新数据了

// 初始化 _update $forceUpdate $destroy \src\core\instance\index.js中调用 export function lifecycleMixin(Vue: Class < Component > ) {
// _update : 更新数据 主要功能在于第一次和后面更新是用的不同__patch__,根据preveVnode判断是否有vnode Vue.prototype._update = function (vnode: VNode, hydrating ? : boolean) {
// 保存Vue实例 const vm: Component = this // 获取Vue的el const prevEl = vm.$el // 获取Vue的vnode 标志上一个vnode const prevVnode = vm._vnode const restoreActiveInstance = setActiveInstance(vm) vm._vnode = vnode //标志上一个vnode // Vue.prototype.__patch__ is injected in entry points // based on the rendering backend used. if (!prevVnode) {
// 如果prevVnode不存在,表示上一次没有创建vnode,这个组件或者new Vue 是第一次进来 // initial render // 更新虚拟dom vm.$el = vm.__patch__( vm.$el, //真正的dom vnode, //vnode hydrating, //ssr相关 false /* removeOnly */ ) } else {
// 如果prevVnode存在,表示已经创建过vnode,所以只要更新数据就行了 // updates // 更新 vm.$el = vm.__patch__(prevVnode, vnode) } // vue的实例化对象 restoreActiveInstance() // update __vue__ reference // 更新vue参考 if (prevEl) {
prevEl.__vue__ = null } if (vm.$el) {
//更新vue vm.$el.__vue__ = vm } // if parent is an HOC, update its $el as well //如果parent是一个HOC,那么也要更新它的$el if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el } // updated hook is called by the scheduler to ensure that children are // updated in a parent's updated hook. // 调度器调用update hook以确保子节点是在父类的更新钩子中更新。 } // $forceUpdate :强制更新数据 观察者数据 Vue.prototype.$forceUpdate = function () {
// 保存vue实例 const vm: Component = this // 如果有_watcher 观察者,就更新 if (vm._watcher) {
// 执行update 更新观察者数据 vm._watcher.update() } } // $destroy :销毁组件 Vue.prototype.$destroy = function () {
// 保存vue实例 const vm: Component = this // 如果已经销毁过,直接返回 if (vm._isBeingDestroyed) {
return } // 触发生命周期beforeDestroy钩子函数 callHook(vm, 'beforeDestroy') // 将这个标识设为true,表示已经开始销毁 vm._isBeingDestroyed = true // remove self from parent // 从父节点移除self const parent = vm.$parent // 如果父节点还存在,并没有被销毁 if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
// 删除父节点 remove(parent.$children, vm) } // teardown watchers // 如果_watcher还存在 拆卸观察者 if (vm._watcher) {
vm._watcher.teardown() } // 获取观察者长度 let i = vm._watchers.length // 全部删除 while (i--) {
vm._watchers[i].teardown() } // remove reference from data ob // 从ob中删除引用 // frozen object may not have observer. // 被冻结的对象可能没有观察者 if (vm._data.__ob__) {
vm._data.__ob__.vmCount-- } // call the last hook... // 将这个设为true,表示已经完成销毁 调用最后一个钩子函数 vm._isDestroyed = true // invoke destroy hooks on current rendered tree // 调用当前渲染树上的销毁钩子 vm.__patch__(vm._vnode, null) // fire destroyed hook // 触发生命周期destroyed钩子函数 callHook(vm, 'destroyed') // turn off all instance listeners. // 销毁事件监听器 vm.$off() // remove __vue__ reference // 删除vue参数 if (vm.$el) {
vm.$el.__vue__ = null } // release circular reference (#6759) // 释放循环引用 销毁父节点 if (vm.$vnode) {
vm.$vnode.parent = null } } }
mountComponent
组件初始化,会执行三个生命周期函数:beforeMount,beforeUpdate,mounted。这里面的beforeUpdate是因为观察者的初始patch可能调用$foreceUpdate,所以可能会触发beforeUpdate
讯享网// mountComponent :安装组件 export function mountComponent( vm: Component, //vnode el: ? Element, //dom hydrating ? : boolean //ssr相关 ): Component {
// 获取el,也就是dom vm.$el = el // 如果选项中没有render函数 这里说的render就是实例化vm的render,虚拟dom调用的渲染函数 if (!vm.$options.render) {
// render等于 createEmptyVNode函数 // createEmptyVNode是一个创建一个节点 空的vNode的函数 vm.$options.render = createEmptyVNode if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore if */ // 如果template第一个不是#,就警告 if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') || vm.$options.el || el) {
warn( 'You are using the runtime-only build of Vue where the template ' + 'compiler is not available. Either pre-compile the templates into ' + 'render functions, or use the compiler-included build.', vm ) } else {
warn( 'Failed to mount component: template or render function not defined.', vm ) } } } // 执行生命周期 beforeMount 钩子函数 callHook(vm, 'beforeMount') // 更新组件 let updateComponent /* istanbul ignore if */ // 忽略 如果开发环境 if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
const name = vm._name const id = vm._uid const startTag = `vue-perf-start:${
id}` const endTag = `vue-perf-end:${
id}` mark(startTag) const vnode = vm._render() mark(endTag) measure(`vue ${
name} render`, startTag, endTag) mark(startTag) vm._update(vnode, hydrating) mark(endTag) measure(`vue ${
name} patch`, startTag, endTag) } } else {
// updateComponet函数 直接更新view视图 updateComponent = () => {
vm._update( /* render 是 虚拟dom,需要执行的编译函数 类似于这样的函数 (function anonymous( ) { with(this){return _c('div',{attrs:{"id":"app"}},[_c('input',{directives:[{name:"info",rawName:"v-info"},{name:"data",rawName:"v-data"}],attrs:{"type":"text"}}),_v(" "),_m(0)])} }) */ vm._render(), // ssr相关 hydrating ) } } // we set this to vm._watcher inside the watcher's constructor // since the watcher's initial patch may call $forceUpdate (e.g. inside child // component's mounted hook), which relies on vm._watcher being already defined // 我们在观察者的构造函数中设置vm._watcher // 因为观察者的初始patch可能调用$foreceUpdate(例如 inside child 组件的挂载钩子) // 它依赖于已经定义的vm._watcher new Watcher( vm, //vnode updateComponent, //上面的更新视图函数 noop, //回调函数 {
before() {
// 如果已经挂载并且没有被销毁 if (vm._isMounted && !vm._isDestroyed) {
// 触发生命周期 beforeUpdate 钩子函数 callHook(vm, 'beforeUpdate') } } }, true /* isRenderWatcher */ ) hydrating = false // manually mounted instance, call mounted on self // mounted is called for render-created child components in its inserted hook // 手动挂载实例,调用挂载在self上 // 在插入的钩子中为呈现器创建的子组件调用 // 如果没有vnode,代表挂载完毕 if (vm.$vnode == null) {
// 将这个设为true vm._isMounted = true // 触发生命周期 mounted 钩子函数 callHook(vm, 'mounted') } // return vm return vm }
updateChildComponent
更新子组件
// 更新子组件 export function updateChildComponent( vm: Component, //vnode propsData: ? Object, //props listeners : ? Object, //事件监听 parentVnode : MountedComponentVNode, //父亲vnode renderChildren: ? Array < VNode > //子节点 ) {
if (process.env.NODE_ENV !== 'production') {
// 将这个设为true 是否更新过了子组件 isUpdatingChildComponent = true } // determine whether component has slot children // we need to do this before overwriting $options._renderChildren. // 确定组件是否有子级插槽 // 我们需要在覆盖$options._renderChildren之前执行此操作。 // check if there are dynamic scopedSlots (hand-written or compiled but with // dynamic slot names). Static scoped slots compiled from template has the // "$stable" marker. // 检查是否有动态scopedslot(手工编写或编译,但使用 // 动态插槽名称)。从模板编译的静态作用域插槽具有 // “$stable”标记。 // 新的作用域插槽 const newScopedSlots = parentVnode.data.scopedSlots // 旧的作用域插槽 const oldScopedSlots = vm.$scopedSlots // 是否有动态作用域插槽 const hasDynamicScopedSlot = !!( (newScopedSlots && !newScopedSlots.$stable) || // has new dynamic scoped slots 是否有新的动态作用域插槽 (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) || // has old dynamic scoped slots 是否有旧的动态作用域插槽 (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key) // has different key scoped slots 是否有不同key的新的动态作用域插槽 ) // Any static slot children from the parent may have changed during parent's // update. Dynamic scoped slots may also have changed. In such cases, a forced // update is necessary to ensure correctness. // 父级的任何静态槽子级都可能在父级的更新中改变。动态作用域插槽也可能已更改。在这种情况下必须进行更新以确保正确性。 const needsForceUpdate = !!( renderChildren || // has new static slots 是否有新的静态插槽 vm.$options._renderChildren || // has old static slots 是否有进的静态插槽 hasDynamicScopedSlot //是否有动态作用域插槽 ) // 父亲vnode vm.$options._parentVnode = parentVnode // 无需重新渲染即可更新vm的占位符节点 vm.$vnode = parentVnode // update vm's placeholder node without re-render // 如果_vnode存在 if (vm._vnode) {
// update child tree's parent // 更新子树的父树 vm._vnode.parent = parentVnode } // 子节点 vm.$options._renderChildren = renderChildren // update $attrs and $listeners hash // these are also reactive so they may trigger child update if the child // used them during render //更新$attrs和$listener散列 //它们也是相应性的,因此如果子进程更新,它们可能触发子进程更新 //渲染时使用它们 // 获取虚拟dom的属性attrs vm.$attrs = parentVnode.data.attrs || emptyObject // 获取事件 vm.$listeners = listeners || emptyObject // update props 更新props属性 if (propsData && vm.$options.props) {
// 这个函数只是返回一个 shouldObserve = boolean toggleObserving(false) // 是否可以添加到观察者模式 // 获取属性 const props = vm._props // 获取属性的propKeys const propKeys = vm.$options._propKeys || [] // 遍历props for (let i = 0; i < propKeys.length; i++) {
// 每一个props const key = propKeys[i] // 获取原始props 用来进行validateProp函数 const propOptions: any = vm.$options.props // wtf flow? / * 验证 prosp 是否是规范数据 并且为props 添加 value.__ob__ 属性,把prosp添加到观察者中 * 校验 props 参数 就是组建 定义的props 类型数据,校验类型 * 判断prop.type的类型是不是Boolean或者String,如果不是他们两类型,调用getPropDefaultValue获取默认值并且把value添加到观察者模式中 / props[key] = validateProp(key, propOptions, propsData, vm) } // 可添加到观察者模式中 toggleObserving(true) // keep a copy of raw propsData // 保留原始propsData的副本 vm.$options.propsData = propsData } // update listeners 更新事件 // 判断listeners并赋值 listeners = listeners || emptyObject // 旧的事件 const oldListeners = vm.$options._parentListeners // 新的事件 vm.$options._parentListeners = listeners // 更新组件事件 :todo updateComponentListeners(vm, listeners, oldListeners) // resolve slots + force update if has children // 解决插槽+强制更新如果有 子节点 // 如果需要ForceUpdate if (needsForceUpdate) {
//判断children 有没有分发式插槽 并且过滤掉空的插槽,并且收集插槽 vm.$slots = resolveSlots(renderChildren, parentVnode.context) //更新数据 观察者数据 vm.$forceUpdate() } if (process.env.NODE_ENV !== 'production') {
// 将这设为false 代表不是要update的子组件 isUpdatingChildComponent = false } }
接下来这三个函数都是判断keep-alive相关的 包括之前的非抽象父组件,也是排除keep-alive
isInInactiveTree
讯享网// 循环父组件dom,如果有不活跃的返回true function isInInactiveTree(vm) {
// 循环父节点 while (vm && (vm = vm.$parent)) {
// 如果父节点有_inactive 则返回true if (vm._inactive) return true } return false }
activateChildComponent
// 判断是否有不活跃的组件 禁用他 如果有活跃组件则触发钩子函数activated export function activateChildComponent( vm: Component, // 虚拟dom vnode direct ? : boolean //布尔值 ) {
if (direct) {
// _directInactive 设为false vm._directInactive = false // 如果有不活跃的树,或者被禁用组件 if (isInInactiveTree(vm)) {
return } } else if (vm._directInactive) {
// 单个不活跃的 return } // 如果 _inactive=true 不活跃组件 或者 vm._inactive === null if (vm._inactive || vm._inactive === null) {
vm._inactive = false // 循环禁止子组件 for (let i = 0; i < vm.$children.length; i++) {
// 递归循环 禁用子组件 activateChildComponent(vm.$children[i]) } // 触发activated 生命周期钩子函数 callHook(vm, 'activated') } }
deactivateChildComponent
讯享网// 循环子组件 和父组件 判断是否有禁止的组件 如果有活跃组件则执行生命后期函数deactivated export function deactivateChildComponent( vm: Component, // 虚拟dom vnode direct ? : boolean // 布尔值 ) {
if (direct) {
vm._directInactive = true if (isInInactiveTree(vm)) {
return } } // 如果该组件是活跃的 if (!vm._inactive) {
vm._inactive = true // 设置活动中的树 for (let i = 0; i < vm.$children.length; i++) {
deactivateChildComponent(vm.$children[i]) } // 触发deactivated 生命周期钩子函数 callHook(vm, 'deactivated') } }
callHook
定义钩子函数
// 在initLifeCycle中初始化callHook 触发钩子函数 export function callHook( vm: Component, // 虚拟dom vnode hook: string // 钩子函数的key 也就是生命周期 ) {
// #7573 disable dep collection when invoking lifecycle hooks //调用生命周期钩子时禁用dep集合 //Dep.target = _target; 压栈 pushTarget() // 获得传入的钩子 beforeCreated这些 // 在vm 中添加声明周期函数 const handlers = vm.$options[hook] // 模板字符串拼接 const info = `${
hook} hook` // 如果获取到钩子周期 if (handlers) {
// 遍历执行 for (let i = 0, j = handlers.length; i < j; i++) {
// 判断是否异步 invokeWithErrorHandling(handlers[i], vm, null, vm, info) } } // 如果存在vm._hasHookEvent if (vm._hasHookEvent) {
// 提交事件 vm.$emit('hook:' + hook) } // 出栈 popTarget() }
总结
今日份思维导图


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/53362.html