乐于分享
好东西不私藏

Vue v2.6.11 生命周期源码解析

Vue v2.6.11 生命周期源码解析

注:建议学习源码从易到难(学习react也可建议先学习react16),核心是感受框架设计的理念,学习vue2后,再去对照学习vue3,从中对比效果更好。

github源码地址

https://github.com/vuejs/vue/tree/v2.6.11

源码学习路线(红色是重点了解模块)

  1. 响应式实现篇

学习Vue中如何实现数据的响应式系统,从而达到数据驱动视图

  1. 虚拟 DOM 篇

学习什么是虚拟 DOM,以及Vue中的DOM-Diff原理

  1. 模板编译篇

学习Vue内部是怎么把template模板编译成虚拟DOM,从而渲染出真实DOM

  1. 实例方法篇

学习Vue中所有实例方法(即所有以$开头的方法)的实现原理

  1. 全局 API 篇

学习Vue中所有全局API的实现原理

  1. 生命周期篇

学习Vue中组件的生命周期实现原理

  1. 指令篇

学习Vue中所有指令的实现原理

  1. 过滤器篇

学习Vue中所有过滤器的实现原理

  1. 内置组件篇

学习Vue中内置组件的实现原理

项目目录:complier 和 core 是核心

├─dist                   # 项目构建后的文件├─scripts                # 与项目构建相关的脚本和配置文件├─flow                   # flow的类型声明文件├─src                    # 项目源代码│    ├─complier          # 与模板编译相关的代码│    ├─core              # 通用的、与运行平台无关的运行时代码│    │  ├─observe        # 实现变化侦测的代码│    │  ├─vdom           # 实现virtual dom的代码│    │  ├─instance       # Vue.js实例的构造函数和原型方法│    │  ├─global-api     # 全局api的代码│    │  └─components     # 内置组件的代码│    ├─server            # 与服务端渲染相关的代码│    ├─platforms         # 特定运行平台的代码,如weex│    ├─sfc               # 单文件组件的解析代码│    └─shared            # 项目公用的工具代码└─test                   # 项目测试代码

从图中我们可以看到,Vue实例的生命周期大致可分为4个阶段:

  • 初始化阶段:为Vue实例上初始化一些属性,事件以及响应式数据;

  • 模板编译阶段:将模板编译成渲染函数;

  • 挂载阶段:将实例挂载到指定的DOM上,即将模板渲染到真实DOM中;

  • 销毁阶段:将实例自身从父组件中删除,并取消依赖追踪及事件监听器;

new Vue()

源码位置:src/core/instance/index.js

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)}
export function initMixin (Vue: Class<Component>) {  Vue.prototype._init = function (options?: Object) {    const vmComponent = 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(        // 简单理解为返回vm.constructor.options.相当于Vue.options        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)    }  }}export function initGlobalAPI (Vue: GlobalAPI) {  // config  const configDef = {}  configDef.get = () => config  if (process.env.NODE_ENV !== 'production') {    configDef.set = () => {      warn(        'Do not replace the Vue.config object, set individual fields instead.'      )    }  }  Object.defineProperty(Vue'config', configDef)  // exposed util methods.  // NOTE: these are not considered part of the public API - avoid relying on  // them unless you are aware of the risk.  Vue.util = {    warn,    extend,    mergeOptions,    defineReactive  }  Vue.set = set  Vue.delete = del  Vue.nextTick = nextTick  // 2.6 explicit observable API  Vue.observable = <T>(obj: T): T => {    observe(obj)    return obj  }  // 创建了一个空的options  Vue.options = Object.create(null)  ASSET_TYPES.forEach(type => {    Vue.options[type + 's'] = Object.create(null)  })  /*    Vue.options.components = {};     Vue.options.directives = {};      Vue.options.filter = {};  */   // this is used to identify the "base" constructor to extend all plain-object  // components with in Weex's multi-instance scenarios.  Vue.options._base = Vue  extend(Vue.options.components, builtInComponents)  initUse(Vue)  initMixin(Vue)  initExtend(Vue)  initAssetRegisters(Vue)}export const ASSET_TYPES = [  'component',  'directive',  'filter']function mergeHook (  parentVal: ?Array<Function>,  childVal: ?Function | ?Array<Function>): ?Array<Function> {  const res = childVal    ? parentVal      ? parentVal.concat(childVal)      : Array.isArray(childVal)        ? childVal        : [childVal]    : parentVal  return res    ? dedupeHooks(res)    : res}function mergeHook(parentVal, childVal) {  if (childVal) {    if (parentVal) {      return parentVal.concat(childVal)    } else {      if (Array.isArray(childVal)) {        return childVal      } else {        return [childVal]      }    } else {    return parentVal    }  }}export function callHook (vm: Component, hook: string) {  // #7573 disable dep collection when invoking lifecycle hooks  pushTarget()  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)    }  }  if (vm._hasHookEvent) {    vm.$emit('hook:' + hook)  }  popTarget()}

initLifecycle

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}

initEvents

<child @select="selectHandler" @click.native="clickHandler"></child>
el.events = {  select: {    value'selectHandler'  }}el.nativeEvents = {  click: {    value"clickHandler"  }}// event handlersif (el.events) {    data += ${genHandlers(el.events, false)},}if (el.nativeEvents) {    data += ${genHandlers(el.nativeEvents, true)},}{  on: {"select": selectHandlder},  nativeOn: {    "click": function($event) {      return clickHandler($event)    }  }} // extract listeners, since these needs to be treated as // child component listeners instead of DOM listeners const listeners = data.on // replace with listeners with .native modifier // so it gets processed during parent component patch. data.on = data.nativeOn

结论: 父组件给子组件的注册事件中,把自定义事件传给子组件,在子组件实例化的时候进行初始化,而浏览器原生事件是在父组件中处理的。

也就是说,实例初始化阶段调用initEvents实际上初始化的是父组件在模版中使用v-on或@注册的监听子组件内触发的事件。

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)  }}

initInjections

允许一个祖先组件向子孙后代组件注入一个依赖,不论组件层级有多深,都始终生效。

inject选项可以是一个字符串数组或一个对象。

// 父组件var parent = {  provide: {    foo'bar'  }}var child = {  inject: ['foo'],  create() {    console.log(this.foo// "bar"  }}
const s = Symbol()const Provider = {  provide() {    return {      [s]: 'foo'    }  }}const child = {  inject: { s }}

provide 和 inject 选项绑定的数据不是响应式的。

const child = {  inject: ['foo'],  data() {    return {      bar: this.foo    }  }}

源码位置:src/core/instance/inject.js

export function initProvide (vm: Component) {  const provide = vm.$options.provide  if (provide) {    vm._provided = typeof provide === 'function'      ? provide.call(vm)      : provide  }}export function initInjections (vm: Component) {  const result = resolveInject(vm.$options.inject, vm)  if (result) {    toggleObserving(false)    Object.keys(result).forEach(key => {      /* istanbul ignore else */      if (process.env.NODE_ENV !== 'production') {        defineReactive(vm, key, result[key], () => {          warn(            `Avoid mutating an injected value directly since the changes will be ` +            `overwritten whenever the provided component re-renders. ` +            `injection being mutated: "${key}"`,            vm          )        })      } else {        defineReactive(vm, key, result[key])      }    })    toggleObserving(true)  }}
export function resolveInject (inject: any, vm: Component): ?Object {  if (inject) {    // inject is :any because flow is not smart enough to figure out cached    const result = Object.create(null)    const keys = hasSymbol      ? Reflect.ownKeys(inject)      : Object.keys(inject)    for (let i = 0; i < keys.length; i++) {      const key = keys[i]      // #6574 in case the inject object is observed...      if (key === '__ob__'continue      const provideKey = inject[key].from      let source = vm      while (source) {        if (source._provided && hasOwn(source._provided, provideKey)) {          result[key] = source._provided[provideKey]          break        }        source = source.$parent      }      if (!source) {        if ('default' in inject[key]) {          const provideDefault = inject[key].default          result[key] = typeof provideDefault === 'function'            ? provideDefault.call(vm)            : provideDefault        } else if (process.env.NODE_ENV !== 'production') {          warn(`Injection "${key}" not found`, vm)        }      }    }    return result  }}

对于字符串数组,在_init中调用合并属性的时候去处理,通过normalizeInject。

源码位置:src/core/util/options.js

function normalizeInject (options: Object, vm: ?Component) {  const inject = options.inject  if (!inject) return  const normalized = options.inject = {}  if (Array.isArray(inject)) {    for (let i = 0; i < inject.length; i++) {      normalized[inject[i]] = { from: inject[i] }    }  } else if (isPlainObject(inject)) {    for (const key in inject) {      const val = inject[key]      normalized[key] = isPlainObject(val)        ? extend({ from: key }, val)        : { from: val }    }  } else if (process.env.NODE_ENV !== 'production') {    warn(Invalid value for option "inject" : expected an Array or an Object, + but got ${toRawType(inject)}.,      vm    )  }}

initState

源码位置:src/core/instance/state.js

export function initState (vm: Component) {  vm._watchers = []  const opts = vm.$options  if (opts.propsinitProps(vm, opts.props)  if (opts.methodsinitMethods(vm, opts.methods)  if (opts.data) {    initData(vm)  } else {    observe(vm._data = {}, true /* asRootData */)  }  if (opts.computedinitComputed(vm, opts.computed)  if (opts.watch && opts.watch !== nativeWatch) {    initWatch(vm, opts.watch)  }}
props: ['name']props: {  name: String}props: {  name: {    type: String  }}

normalizeProps 

源码位置:src/core/util/options.js

function normalizeProps (options: Object, vm: ?Component) {  const props = options.props  if (!props) return  const res = {}  let i, val, name  if (Array.isArray(props)) {    i = props.length    while (i--) {      val = props[i]      if (typeof val === 'string') {        name = camelize(val)        res[name] = { typenull }      } else if (process.env.NODE_ENV !== 'production') {        warn('props must be strings when using array syntax.')      }    }  } else if (isPlainObject(props)) {    for (const key in props) {      val = props[key]      name = camelize(key)      res[name] = isPlainObject(val)        ? val        : { type: val }    }  } else if (process.env.NODE_ENV !== 'production') {    warn(Invalid value for option "props": expected an Array or an Object,  + but got ${toRawType (props)}.,      vm    )  }  options.props = res}

将props数据规范化处理后,交给 initProps处理。 

源码位置:src/core/instance/state.js

functioninitProps (vm: Component, propsOptions: Object{  const propsData = vm.$options.propsData || {}  const props = vm._props = {}  // cache prop keys so that future props updates can iterate using Array  // instead of dynamic object key enumeration.  // 用来缓存props对象中的key,将来更新props时只需要遍历m.$options._propKeys数组即可得到所有props的key  const keys = vm.$options._propKeys = []  const isRoot = !vm.$parent  // root instance props should be converted  if(!isRoot) {    toggleObserving(false)  }  for(const key in propsOptions) {    keys.push(key)    const value validateProp(key, propsOptions, propsData, vm)    /* istanbul ignore else */    if(process.env.NODE_ENV !== 'production') {      const hyphenatedKey hyphenate(key)      if(isReservedAttribute(hyphenatedKey) ||          config.isReservedAttr(hyphenatedKey)) {        warn("${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.,          vm        )      }      defineReactive(props, key, value, () => {        if(!isRoot && !isUpdatingChildComponent) {          warn(Avoid mutating a prop directly since the value will be + overwritten whenever the parent component re-renders. + Instead, use a data or computed property based on the prop's +value. Prop being mutated: "${key}",            vm          )        }      })    } else {      defineReactive(props, key, value)    }    // static props are already proxied on the component's prototype    // during Vue.extend(). We only need to proxy props defined at    // instantiation here.    if(!(key in vm)) {      proxy(vm, _props, key)    }  }  toggleObserving(true)}export function validateProp (  keystring,  propOptions: Object,  propsData: Object,  vm?: Component): any {  const prop = propOptions[key]  const absent = !hasOwn(propsData, key)  let value = propsData[key]  // boolean casting  const booleanIndex getTypeIndex(Boolean, prop.type)  if(booleanIndex > -1) {    // 父组件没有传入该prop属性并且该属性也没有默认值    if(absent && !hasOwn(prop, 'default')) {      value = false    } else if (value === '' || value === hyphenate(key)) {      // only cast empty string / same name to boolean if      // boolean has higher priority      const stringIndex getTypeIndex(String, prop.type)      if(stringIndex < 0 || booleanIndex < stringIndex) {        value = true      }    }  }  // check default value  if(value === undefined) {    value = getPropDefaultValue(vm, prop, key)    // since the default value is a fresh copy,    // make sure to observe it.    const prevShouldObserve = shouldObserve    toggleObserving(true)    observe(value)    toggleObserving(prevShouldObserve)  }  if(    process.env.NODE_ENV !== 'production' &&    // skip validation for weex recycle-list child component props    !(__WEEX__ && isObject(value) && ('@binding' in value))  ) {    assertProp(prop, key, value, vm, absent)  }  return value}functiongetPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): any{  // no default, return undefined  if(!hasOwn(prop, 'default')) {    return undefined  }  const def = prop.default  // warn against non-factory defaults for Object & Array  if(process.env.NODE_ENV !== 'production' && isObject(def)) {    warn(      'Invalid default value for prop "' + key + '": ' +      'Props with type Object/Array must use a factory function ' +      'to return the default value.',      vm    )  }  // the raw prop value was also undefined from previous render,  // return previous default value to avoid unnecessary watcher trigger  if(vm && vm.$options.propsData && vm.$options.propsData[key] === undefined &&    vm._props[key] !== undefined  ) {    return vm._props[key]  }  // call factory function for non-Function types  // a value is Function if its prototype is function even across different execution context  return typeof def === 'function' && getType(prop.type) !== 'Function'    ? def.call(vm)    : def}
function assertProp (  propPropOptions,  namestring,  valueany,  vm: ?Component,  // 判断当前key是否在propsData中存在,也就是父组件是否传入了该属性  absentboolean) {  if (prop.required && absent) {    warn(      'Missing required prop: "' + name + '"',      vm    )    return  }  if (value == null && !prop.required) {    return  }  let type = prop.type  // props: {name: true}  let valid = !type || type === true  const expectedTypes = []  if (type) {    if (!Array.isArray(type)) {      type = [type]    }    for (let i = 0; i < type.length && !valid; i++) {      /*      {        valid: true // 表示校验是否成功        expectedType: 'Boolean' | 'String'.. // 表示被校验的类型      }      */      const assertedType = assertType(value, type[i])      expectedTypes.push(assertedType.expectedType || '')      valid = assertedType.valid    }  }  if (!valid) {    warn(      getInvalidTypeMessage(name, value, expectedTypes),      vm    )    return  }  const validator = prop.validator  if (validator) {    if (!validator(value)) {      warn(        'Invalid prop: custom validator check failed for prop "' + name + '".',        vm      )    }  }}

initMethods

function initMethods (vm: Component, methods: Object) {  const props = vm.$options.props;  for (const key in methods) {    if (process.env.NODE_ENV !== 'production') {      if (typeof methods[key] !== 'function') {        warn(          `Method "${key}" has type "${typeof methods[key]}" in the component definition. ` +          `Did you reference the function correctly?`,          vm        )      }      if (props && hasOwn(props, key)) {        warn(          `Method "${key}" has already been defined as a prop.`,          vm        )             }             // isReserved 判断函数是以_ 或 $ 开头的             if ((key in vm) && isReserved(key)) {                 warn(                     `Method "${key}" conflicts with an existing Vue instance method. ` +          `Avoid defining component methods that start with _ or $.`        )      }    }    vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)  }}

initData

function initData (vm: Component) {  let data = vm.$options.data  data = vm._data = typeof data === 'function'    ? getData(data, vm)    : data || {}  if (!isPlainObject(data)) {    data = {}    process.env.NODE_ENV !== 'production' && warn(      'data functions should return an object:\n' +      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',      vm    )  }  // proxy data on instance  const keys = Object.keys(data)  const props = vm.$options.props  const methods = vm.$options.methods  let i = keys.length  while (i--) {    const key = keys[i]    if (process.env.NODE_ENV !== 'production') {      if (methods && hasOwn(methods, key)) {        warn(`Method "${key}" has already been defined as a data property., vm)       }     }     if (props && hasOwn(props, key)) {       process.env.NODE_ENV !== 'production' && warn(The data property "${key}" is already declared as a prop. + Use prop default value instead., vm)} else if (!isReserved(key)) {       // 通过proxy代理到vm上,我们就可以通过this.xxx来访问data的数据了       proxy(vm, _data`, key)    }  }  // observe data  observe(datatrue /* asRootData */)}

initComputed

const vm = new Vue({  data: {a: 1},  computed: {    adouble: function() {      return this.data * 2    },    aPlus: {      get: function() {        return this.a + 1      },      set: function(v) {        this.a = v - 1      }    }  }})
function initComputed (vm: Component, computed: Object) {  // $flow-disable-line  const watchers = vm._computedWatchers = Object.create(null)  // computed properties are just getters during SSR  const isSSR = isServerRendering()  for (const key in computed) {    const userDef = computed[key]    const getter = typeof userDef === 'function' ? userDef : userDef.get    if (process.env.NODE_ENV !== 'production' && getter == null) {      warn(Getter is missing for computed property "${key}".,        vm      )    }    if (!isSSR) {      // create internal watcher for the computed property.      watchers[key] = new Watcher(        vm,        getter || noop,        noop,        computedWatcherOptions      )    }    // component-defined computed properties are already defined on the    // component prototype. We only need to define computed properties defined    // at instantiation here.    if (!(key in vm)) {      defineComputed(vm, key, userDef)    } else if (process.env.NODE_ENV !== 'production') {      if (key in vm.$data) {        warn(`The computed property "${key}" is already defined in data., vm)} else if (vm.$options.props && key in vm.$options.props) {warn(The computed property "${key}" is already defined as a prop.`, vm)      }    }  }}
// 给target定义一个属性key,并且属性key的getter和setter根据userDef的值来设置export function defineComputed (  target: any,  key: string,  userDef: Object | Function) {  const shouldCache = !isServerRendering()  if (typeof userDef === 'function') {    sharedPropertyDefinition.get = shouldCache      // 需要创建一个具有缓存功能的getter      ? createComputedGetter(key)      // 服务端环境下计算属性不需要缓存的      : createGetterInvoker(userDef)    sharedPropertyDefinition.set = noop  } else {    sharedPropertyDefinition.get = userDef.get      ? shouldCache && userDef.cache !== false        ? createComputedGetter(key)        : createGetterInvoker(userDef.get)      : noop    sharedPropertyDefinition.set = userDef.set || noop  }  if (process.env.NODE_ENV !== 'production' &&      sharedPropertyDefinition.set === noop) {    sharedPropertyDefinition.set = function () {      warn(Computed property "${key}"was assigned to but it has no setter.,        this      )    }  }  Object.defineProperty(target, key, sharedPropertyDefinition)}function createComputedGetter (key) {  return function computedGetter () {    const watcher = this._computedWatchers && this._computedWatchers[key]    if (watcher) {      if (watcher.dirty) {        watcher.evaluate()      }      if (Dep.target) {        watcher.depend()      }      return watcher.value    }  }}depend () {    let i = this.deps.length    while (i--) {      this.deps[i].depend()    }}evaluate () {    this.value = this.get()    this.dirty = false}update () {    /* istanbul ignore else */    if (this.lazy) {      this.dirty = true    } else if (this.sync) {      this.run()    } else {      queueWatcher(this)    }}

new Watcher的时候,传入

{    computedtrue    }

initWatch

const vm new Vue({  data: {    a1,    b2,    c3  },  watch: {    a: function(val, oldVal) {    },    b'someMethod',    c: {      handler: function(val, oldVal) {...},      deep: true    },    d: {      handler: 'xxx',      immediate: true    },    e: [      'handler',      functionhander2 (val, oldVal{...}    ]    //    "e.f"function (val, oldVal{...}  }})
function initWatch (vm: Component, watch: Object) {  for (const key in watch) {    const handler = watch[key]    if (Array.isArray(handler)) {      for (let i = 0; i < handler.length; i++) {        createWatcher(vm, key, handler[i])      }    } else {      createWatcher(vm, key, handler)    }  }}
function createWatcher (  vmComponent,  expOrFnstring | Function,  handlerany,  options?: Object) {  if (isPlainObject(handler)) {    options = handler    handler = handler.handler  }  // 在initMehotds的时候已经将选项中的每一个方法都绑定到当前实例上了  if (typeof handler === 'string') {    handler = vm[handler]  }  return vm.$watch(expOrFn, handler, options)}

挂载阶段

Vue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {  el = el && inBrowser ? query(el) : undefined  return mountComponent(this, el, hydrating)}
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)}// 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// updateComponent中读取到的所有数据,都会被watcher所监控,这些数据中只要有一个发生变化,那么watcher就会得到通知,从而去更新视图new Watcher(vm, updateComponent, noop, {    before () {      if (vm._isMounted && !vm._isDestroyed) {        callHook(vm, 'beforeUpdate')      }    }}, true /* isRenderWatcher */)

销毁阶段

Vue.prototype.$destroy function () {    const vm: Component = this    // 标志当前实例是否处于正在被销毁的阶段    if(vm._isBeingDestroyed) {      return    }    callHook(vm, 'beforeDestroy')    vm._isBeingDestroyed = true    // remove self from parent    const parent = vm.$parent    // 把自己从父级实例的子实例列表中删除    if(parent && !parent._isBeingDestroyed && !vm.$options.abstract) {      remove(parent.$children, vm)    }     // 一部分是实例自身依赖其他数据,需要将实例自身从其他数据的依赖列表中删除    // 另一部分是实例内的数据对其他数据的依赖,也需要从其他数据的依赖列表中删除实例内数据    // teardown watchers    if(vm._watcher) {      // 从所有依赖向的Dep列表中将自己删除      vm._watcher.teardown()    }    let i = vm._watchers.length    while(i--) {      vm._watchers[i].teardown()    }    // remove reference from data ob    // frozen object may not have observer.    if(vm._data.__ob__) {      vm._data.__ob__.vmCount--    }    // call the last hook...    vm._isDestroyed = true    // invoke destroy hooks on current rendered tree    vm.__patch__(vm._vnode, null)    // fire destroyed hook    callHook(vm, 'destroyed')    // turn off all instance listeners.     // 移除实例的所有事件监听器    vm.$off()    // remove __vue__ reference    if(vm.$el) {      vm.$el.__vue__ = null    }    // release circular reference (#6759)    if(vm.$vnode) {      vm.$vnode.parent = null    }}
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » Vue v2.6.11 生命周期源码解析

评论 抢沙发

8 + 7 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮