src/core/instance/lifecycle.ts TYPESCRIPT 422 lines View on github.com → Search inside
1import config from '../config'2import Watcher, { WatcherOptions } from '../observer/watcher'3import { mark, measure } from '../util/perf'4import VNode, { createEmptyVNode } from '../vdom/vnode'5import { updateComponentListeners } from './events'6import { resolveSlots } from './render-helpers/resolve-slots'7import { toggleObserving } from '../observer/index'8import { pushTarget, popTarget } from '../observer/dep'9import type { Component } from 'types/component'10import type { MountedComponentVNode } from 'types/vnode'1112import {13  warn,14  noop,15  remove,16  emptyObject,17  validateProp,18  invokeWithErrorHandling19} from '../util/index'20import { currentInstance, setCurrentInstance } from 'v3/currentInstance'21import { getCurrentScope } from 'v3/reactivity/effectScope'22import { syncSetupProxy } from 'v3/apiSetup'2324export let activeInstance: any = null25export let isUpdatingChildComponent: boolean = false2627export function setActiveInstance(vm: Component) {28  const prevActiveInstance = activeInstance29  activeInstance = vm30  return () => {31    activeInstance = prevActiveInstance32  }33}3435export function initLifecycle(vm: Component) {36  const options = vm.$options3738  // locate first non-abstract parent39  let parent = options.parent40  if (parent && !options.abstract) {41    while (parent.$options.abstract && parent.$parent) {42      parent = parent.$parent43    }44    parent.$children.push(vm)45  }4647  vm.$parent = parent48  vm.$root = parent ? parent.$root : vm4950  vm.$children = []51  vm.$refs = {}5253  vm._provided = parent ? parent._provided : Object.create(null)54  vm._watcher = null55  vm._inactive = null56  vm._directInactive = false57  vm._isMounted = false58  vm._isDestroyed = false59  vm._isBeingDestroyed = false60}6162export function lifecycleMixin(Vue: typeof Component) {63  Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {64    const vm: Component = this65    const prevEl = vm.$el66    const prevVnode = vm._vnode67    const restoreActiveInstance = setActiveInstance(vm)68    vm._vnode = vnode69    // Vue.prototype.__patch__ is injected in entry points70    // based on the rendering backend used.71    if (!prevVnode) {72      // initial render73      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)74    } else {75      // updates76      vm.$el = vm.__patch__(prevVnode, vnode)77    }78    restoreActiveInstance()79    // update __vue__ reference80    if (prevEl) {81      prevEl.__vue__ = null82    }83    if (vm.$el) {84      vm.$el.__vue__ = vm85    }86    // if parent is an HOC, update its $el as well87    let wrapper: Component | undefined = vm88    while (89      wrapper &&90      wrapper.$vnode &&91      wrapper.$parent &&92      wrapper.$vnode === wrapper.$parent._vnode93    ) {94      wrapper.$parent.$el = wrapper.$el95      wrapper = wrapper.$parent96    }97    // updated hook is called by the scheduler to ensure that children are98    // updated in a parent's updated hook.99  }100101  Vue.prototype.$forceUpdate = function () {102    const vm: Component = this103    if (vm._watcher) {104      vm._watcher.update()105    }106  }107108  Vue.prototype.$destroy = function () {109    const vm: Component = this110    if (vm._isBeingDestroyed) {111      return112    }113    callHook(vm, 'beforeDestroy')114    vm._isBeingDestroyed = true115    // remove self from parent116    const parent = vm.$parent117    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {118      remove(parent.$children, vm)119    }120    // teardown scope. this includes both the render watcher and other121    // watchers created122    vm._scope.stop()123    // remove reference from data ob124    // frozen object may not have observer.125    if (vm._data.__ob__) {126      vm._data.__ob__.vmCount--127    }128    // call the last hook...129    vm._isDestroyed = true130    // invoke destroy hooks on current rendered tree131    vm.__patch__(vm._vnode, null)132    // fire destroyed hook133    callHook(vm, 'destroyed')134    // turn off all instance listeners.135    vm.$off()136    // remove __vue__ reference137    if (vm.$el) {138      vm.$el.__vue__ = null139    }140    // release circular reference (#6759)141    if (vm.$vnode) {142      vm.$vnode.parent = null143    }144  }145}146147export function mountComponent(148  vm: Component,149  el: Element | null | undefined,150  hydrating?: boolean151): Component {152  vm.$el = el153  if (!vm.$options.render) {154    // @ts-expect-error invalid type155    vm.$options.render = createEmptyVNode156    if (__DEV__) {157      /* istanbul ignore if */158      if (159        (vm.$options.template && vm.$options.template.charAt(0) !== '#') ||160        vm.$options.el ||161        el162      ) {163        warn(164          'You are using the runtime-only build of Vue where the template ' +165            'compiler is not available. Either pre-compile the templates into ' +166            'render functions, or use the compiler-included build.',167          vm168        )169      } else {170        warn(171          'Failed to mount component: template or render function not defined.',172          vm173        )174      }175    }176  }177  callHook(vm, 'beforeMount')178179  let updateComponent180  /* istanbul ignore if */181  if (__DEV__ && config.performance && mark) {182    updateComponent = () => {183      const name = vm._name184      const id = vm._uid185      const startTag = `vue-perf-start:${id}`186      const endTag = `vue-perf-end:${id}`187188      mark(startTag)189      const vnode = vm._render()190      mark(endTag)191      measure(`vue ${name} render`, startTag, endTag)192193      mark(startTag)194      vm._update(vnode, hydrating)195      mark(endTag)196      measure(`vue ${name} patch`, startTag, endTag)197    }198  } else {199    updateComponent = () => {200      vm._update(vm._render(), hydrating)201    }202  }203204  const watcherOptions: WatcherOptions = {205    before() {206      if (vm._isMounted && !vm._isDestroyed) {207        callHook(vm, 'beforeUpdate')208      }209    }210  }211212  if (__DEV__) {213    watcherOptions.onTrack = e => callHook(vm, 'renderTracked', [e])214    watcherOptions.onTrigger = e => callHook(vm, 'renderTriggered', [e])215  }216217  // we set this to vm._watcher inside the watcher's constructor218  // since the watcher's initial patch may call $forceUpdate (e.g. inside child219  // component's mounted hook), which relies on vm._watcher being already defined220  new Watcher(221    vm,222    updateComponent,223    noop,224    watcherOptions,225    true /* isRenderWatcher */226  )227  hydrating = false228229  // flush buffer for flush: "pre" watchers queued in setup()230  const preWatchers = vm._preWatchers231  if (preWatchers) {232    for (let i = 0; i < preWatchers.length; i++) {233      preWatchers[i].run()234    }235  }236237  // manually mounted instance, call mounted on self238  // mounted is called for render-created child components in its inserted hook239  if (vm.$vnode == null) {240    vm._isMounted = true241    callHook(vm, 'mounted')242  }243  return vm244}245246export function updateChildComponent(247  vm: Component,248  propsData: Record<string, any> | null | undefined,249  listeners: Record<string, Function | Array<Function>> | undefined,250  parentVnode: MountedComponentVNode,251  renderChildren?: Array<VNode> | null252) {253  if (__DEV__) {254    isUpdatingChildComponent = true255  }256257  // determine whether component has slot children258  // we need to do this before overwriting $options._renderChildren.259260  // check if there are dynamic scopedSlots (hand-written or compiled but with261  // dynamic slot names). Static scoped slots compiled from template has the262  // "$stable" marker.263  const newScopedSlots = parentVnode.data.scopedSlots264  const oldScopedSlots = vm.$scopedSlots265  const hasDynamicScopedSlot = !!(266    (newScopedSlots && !newScopedSlots.$stable) ||267    (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||268    (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key) ||269    (!newScopedSlots && vm.$scopedSlots.$key)270  )271272  // Any static slot children from the parent may have changed during parent's273  // update. Dynamic scoped slots may also have changed. In such cases, a forced274  // update is necessary to ensure correctness.275  let needsForceUpdate = !!(276    renderChildren || // has new static slots277    vm.$options._renderChildren || // has old static slots278    hasDynamicScopedSlot279  )280281  const prevVNode = vm.$vnode282  vm.$options._parentVnode = parentVnode283  vm.$vnode = parentVnode // update vm's placeholder node without re-render284285  if (vm._vnode) {286    // update child tree's parent287    vm._vnode.parent = parentVnode288  }289  vm.$options._renderChildren = renderChildren290291  // update $attrs and $listeners hash292  // these are also reactive so they may trigger child update if the child293  // used them during render294  const attrs = parentVnode.data.attrs || emptyObject295  if (vm._attrsProxy) {296    // force update if attrs are accessed and has changed since it may be297    // passed to a child component.298    if (299      syncSetupProxy(300        vm._attrsProxy,301        attrs,302        (prevVNode.data && prevVNode.data.attrs) || emptyObject,303        vm,304        '$attrs'305      )306    ) {307      needsForceUpdate = true308    }309  }310  vm.$attrs = attrs311312  // update listeners313  listeners = listeners || emptyObject314  const prevListeners = vm.$options._parentListeners315  if (vm._listenersProxy) {316    syncSetupProxy(317      vm._listenersProxy,318      listeners,319      prevListeners || emptyObject,320      vm,321      '$listeners'322    )323  }324  vm.$listeners = vm.$options._parentListeners = listeners325  updateComponentListeners(vm, listeners, prevListeners)326327  // update props328  if (propsData && vm.$options.props) {329    toggleObserving(false)330    const props = vm._props331    const propKeys = vm.$options._propKeys || []332    for (let i = 0; i < propKeys.length; i++) {333      const key = propKeys[i]334      const propOptions: any = vm.$options.props // wtf flow?335      props[key] = validateProp(key, propOptions, propsData, vm)336    }337    toggleObserving(true)338    // keep a copy of raw propsData339    vm.$options.propsData = propsData340  }341342  // resolve slots + force update if has children343  if (needsForceUpdate) {344    vm.$slots = resolveSlots(renderChildren, parentVnode.context)345    vm.$forceUpdate()346  }347348  if (__DEV__) {349    isUpdatingChildComponent = false350  }351}352353function isInInactiveTree(vm) {354  while (vm && (vm = vm.$parent)) {355    if (vm._inactive) return true356  }357  return false358}359360export function activateChildComponent(vm: Component, direct?: boolean) {361  if (direct) {362    vm._directInactive = false363    if (isInInactiveTree(vm)) {364      return365    }366  } else if (vm._directInactive) {367    return368  }369  if (vm._inactive || vm._inactive === null) {370    vm._inactive = false371    for (let i = 0; i < vm.$children.length; i++) {372      activateChildComponent(vm.$children[i])373    }374    callHook(vm, 'activated')375  }376}377378export function deactivateChildComponent(vm: Component, direct?: boolean) {379  if (direct) {380    vm._directInactive = true381    if (isInInactiveTree(vm)) {382      return383    }384  }385  if (!vm._inactive) {386    vm._inactive = true387    for (let i = 0; i < vm.$children.length; i++) {388      deactivateChildComponent(vm.$children[i])389    }390    callHook(vm, 'deactivated')391  }392}393394export function callHook(395  vm: Component,396  hook: string,397  args?: any[],398  setContext = true399) {400  // #7573 disable dep collection when invoking lifecycle hooks401  pushTarget()402  const prevInst = currentInstance403  const prevScope = getCurrentScope()404  setContext && setCurrentInstance(vm)405  const handlers = vm.$options[hook]406  const info = `${hook} hook`407  if (handlers) {408    for (let i = 0, j = handlers.length; i < j; i++) {409      invokeWithErrorHandling(handlers[i], vm, args || null, vm, info)410    }411  }412  if (vm._hasHookEvent) {413    vm.$emit('hook:' + hook)414  }415  if (setContext) {416    setCurrentInstance(prevInst)417    prevScope && prevScope.on()418  }419420  popTarget()421}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.