关于vue.js:vue源码分析从new-Vue开始

初学vue,你得晓得咱们是从new Vue开始的:

new Vue({
  el: '#app',
  data: obj,
  ...
})

那你感觉是不是很有意思,咱们new Vue之后,就能够应用他那么多的性能,可见Vue是暴进去的一个一个性能类函数,咱们进入源码一探到底:

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
//判断是不是服务端渲染
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
/** * 增加全局的API */
initGlobalAPI(Vue)
/** * 服务端渲染须要 */
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})
/** * 服务端渲染须要 */
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})
/** * 服务端渲染须要 */
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})
/** * vue版本号 这里的'__VERSION__'为占位符,公布版本时将被主动替换 */
Vue.version = '__VERSION__'
export default Vue

那么咱们看到咱们的外围Vue来自'./instance/index'那咱们再去一探到底,可想而知外面必然有一个Vue函数类

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

// Vue构造函数必须应用new关键字实例化, 否则会抛出一个正告, 实例化Vue的时候会调用_init办法初始化
// 这里options也是.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)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue

能够看到外面有一个function Vue性能类,而且外面加载了initMixin,stateMixin等,这几个办法别离传入了Vue来初始化一些性能。
另外咱们能够在入口文件出看到initGlobalAPI这个办法,那么咱们关上initGlobalAPI所在的地位:./global-api/index

......
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.
  // 注册全局工具API, 只对Vue失效。
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
  //定义全局属性
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 初始化options
  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  // 用_base属性来挂载Vue结构器
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)
  //定义全局办法
  initUse(Vue) // Vue.use
  initMixin(Vue) // Vue.mixin
  initExtend(Vue) // Vue.extend
  initAssetRegisters(Vue)
}

可见暴露出多个办法给全局,而Vue.util是一些工具办法:

import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'

import {
  warn,
  extend,
  nextTick,
  mergeOptions,
  defineReactive
} from '../util/index'

那么咱们回到Vue性能函数类上:

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

// Vue构造函数必须应用new关键字实例化, 否则会抛出一个正告, 实例化Vue的时候会调用_init办法初始化
// 这里options也是.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)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

咱们能够看到initMixin(Vue)执行了,那么咱们去读一下init的源码:

参考Vue3源码视频解说:进入学习

import config from '../config'
import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { mark, measure } from '../util/perf'
import { initLifecycle, callHook } from './lifecycle'
import { initProvide, initInjections } from './inject'
import { extend, mergeOptions, formatComponentName } from '../util/index'

let uid = 0

// 向Vue.prototype增加_init办法, 用于new Vue时 初始化一些配置项
// 次要的性能是合并配置项, 初始化生命周期, 事件, 组件, 指令等等
export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    // 应用uid来辨别不同的vue实例, 以便前面的缓存性能应用
    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)
    }

    // 如果是vue实例,则不须要被observe
    vm._isVue = true
    // 1、解决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
      )
    }
    //2、renderProxy
    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
    //初始化vm状态 prop/data/computed/watch实现初始化
    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)
    }

    // 配置项里有el属性, 则会挂载到实在DOM上, 实现视图的渲染
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

浏览init.js源码之后,咱们能够看到其实就是这样一个程序:

  • option参数
  • renderProxy代理
  • vm的生命周期相干变量初始化
  • vm的事件监听
  • 初始化vm的状态
  • render & $mount

在Vue的原型上挂了一个_init办法,也就是说,咱们执行new Vue(options)的时候就会执行这个办法,并且咱们传过来的options能够通过vm.$options拜访到,那么mergeOptions()外部原理又是啥呢,而且咱们看到他接管到两个局部resolveConstructorOptions(),options并且交融在一起,那么咱们先钻研resolveConstructorOptions()

export function resolveConstructorOptions (Ctor: Class<Component>) {
  let options = Ctor.options
  if (Ctor.super) {
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions
    if (superOptions !== cachedSuperOptions) {
      // super option changed,
      // need to resolve new options.
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}

那么问题又来了,let options = Ctor.options中的Ctor又是什么货色呢?咱们能够看到上面extend(Ctor.extendOptions, modifiedOptions)那咱们去找extend:

import { ASSET_TYPES } from 'shared/constants'
import { defineComputed, proxy } from '../instance/state'
import { extend, mergeOptions, validateComponentName } from '../util/index'

export function initExtend (Vue: GlobalAPI) {
  /**   * Each instance constructor, including Vue, has a unique   * cid. This enables us to create wrapped "child   * constructors" for prototypal inheritance and cache them.   */
  // 为每个构造函数调配一个惟一的cid, 以便用于缓存
  Vue.cid = 0
  let cid = 1

  /**   * Class inheritance   */
  // 生成Vue结构器的子类
  Vue.extend = function (extendOptions: Object): Function {
    extendOptions = extendOptions || {}
    // 如果cid已存在, 则利用缓存, 间接返回缓存的结构器
    const Super = this
    const SuperId = Super.cid
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    const name = extendOptions.name || Super.options.name
    if (process.env.NODE_ENV !== 'production' && name) {
      validateComponentName(name)
    }
    // 创立一个蕴含_init办法的子类, 并赋予惟一的cid
    const Sub = function VueComponent (options) {
      this._init(options)
    }
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    // 合并配置项
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    Sub['super'] = Super

    // For props and computed properties, we define the proxy getters on
    // the Vue instances at extension time, on the extended prototype. This
    // avoids Object.defineProperty calls for each instance created.
    // 将props和computed都代理到vm实例上
    if (Sub.options.props) {
      initProps(Sub)
    }
    if (Sub.options.computed) {
      initComputed(Sub)
    }

    // allow further extension/mixin/plugin usage
    // 增加extend,mixin,use这些API
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    // create asset registers, so extended classes
    // can have their private assets too.
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // 将组件实例本身也挂载为一个属性, 用于递归组件
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }

    // keep a reference to the super options at extension time.
    // later at instantiation we can check if Super's options have
    // been updated.
    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)

    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
  }
}

那我么找到了Vue.extend,能够看进去这不就是实现了一个继承嘛。Sub继承自super,而后return进来。
所以resolveConstructorOptions就做两件事

  • Ctor.super 来判断该类是否是Vue的子类
  • if (superOptions !== cachedSuperOptions) 来判断父类中的options 有没有发生变化
    那么咱们晓得了resolveConstructorOptionsnew Vue(options),咱们的mergeOptions函数就是把他们合并的:
// 合并组件的配置项
export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  if (process.env.NODE_ENV !== 'production') {
    checkComponents(child)
  }

  if (typeof child === 'function') {
    child = child.options
  }

  // 序列化props, inject, directive
  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)
  const extendsFrom = child.extends
  if (extendsFrom) {
    parent = mergeOptions(parent, extendsFrom, vm)
  }
  // child的mixins退出parent中
  if (child.mixins) {
    for (let i = 0, l = child.mixins.length; i < l; i++) {
      parent = mergeOptions(parent, child.mixins[i], vm)
    }
  }
  // 应用strat中的合并办法去顺次合并配置对象
  const options = {}
  let key
  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
  function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

那么咱们写的一些组件和个性全副放在vm.$options外面了,那么下一步也就是renderProxy咱们有趣味的童鞋,能够根据援用门路去看看实现原理。

那么咱们initMixin函数执行完前两步之后须要执行的几个函数别离为

    // 顺次初始化配置项, 并调用生命周期钩子
    vm._self = vm
    initLifecycle(vm)
    // 事件监听初始化
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    //初始化vm状态 prop/data/computed/watch实现初始化
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

那咱们先来理解一下这几个函数别离干了啥:

1、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
}

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

3、initRender

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

4、callback

export function callHook (vm: Component, hook: string) {
  const handlers = vm.$options[hook]
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      try {
        handlers[i].call(vm)
      } catch (e) {
        handleError(e, vm, `${hook} hook`)
      }
    }
  }
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook)
  }
}

那咱们在initRender中能够发现初始化了一些性能,例如$lintener,$createElement,而在callback外面却间接调用了beforeCreate钩子函数。
前面还有

5、initInjections,resolveInject,initProvide

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).filter(key => {
        /* istanbul ignore next */
        return Object.getOwnPropertyDescriptor(inject, key).enumerable
      })
      : Object.keys(inject)

    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      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
  }
}
export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

那么对于这三个函数是干嘛的?咱们先来理解一下:

provide/inject:

这对选项须要一起应用,以容许一个先人组件向其所有子孙后代注入一个依赖,不管组件档次有多深,并在起上下游关系成立的工夫里始终失效。如果你相熟 React,这与 React 的上下文个性很类似。

那就是相当于初始化依赖的关系,而initProvide根本没有什么内容,就是将$options里的provide赋值到以后实例上
写到这里那么激动人心的时刻到了–initState

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)
  // 如果选项中不存在data,调用observe来观测一个空对象
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  // 这里须要额定判断一下opts.watch !== nativeWatch
  // 是因为在Firefox下, Object实例上会有一个自带的watch属性, 须要判断这个watch必须是vue提供的watch
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

能够看出initState外面初始化了propsdatacomputedwatchmethods 那么咱们次要看一下initData,initProps的代码:

/** * data初始化, 和props基本一致 */
function initData (vm: Component) {
  let data = vm.$options.data
  /**   * data字段有两种可选类型   * 当data是function时, 间接执行这个function 并将返回值作为data   * data也能够间接就是一个object, 但仅应该在root组件这样做, 因为间接应用data对象会导致多个雷同的组件持有同一个data对象的援用   * 而应用一个返回新对象的function就能够防止这个问题   * 详见 https://cn.vuejs.org/v2/style-guide/#%E7%BB%84%E4%BB%B6%E6%95%B0%E6%8D%AE-%E5%BF%85%E8%A6%81   * 其实在initData之前的mergeOptions操作中曾经将data格式化为一个function   * 然而在initData和mergeOptions两头还有一个生命周期钩子beforeCreate被调用   * 这里应用typeof再次判断data的类型是为了避免在beforeCreate中扭转了vm.$options.data   */
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  // 避免开发者在写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
  // 将data的每个字段都代理到vm实例上, 并判断是否与methods, props抵触, 反复key会报错
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  // 查看是否有反复的key, 这里能够看出props优先级最高, 其次是data, 最初是methods
  // 真的呈现重名时,会依照优先级笼罩
  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
      )
    // isReserved用于判断一个key是否以$或者_结尾,vue不会在vm下面代理这些key
    } else if (!isReserved(key)) {
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  // 使data变为响应式
  observe(data, true /* asRootData */)
}

function initProps (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.
  const keys = vm.$options._propKeys = []
  const isRoot = !vm.$parent
  // root instance props should be converted
  // 应用toggleObserving能够设置shouldObserve为参数的值, 这里设为false是阻止观察者响应更新, 直到props初始化完结
  if (!isRoot) {
    toggleObserving(false)
  }
  // 遍历所有props选项, 并校验props
  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)
      // 如果应用了保留属性, 这里会报错, 保留属性包含key,ref,slot,slot-scope,is
      if (isReservedAttribute(hyphenatedKey) ||
          config.isReservedAttr(hyphenatedKey)) {
        warn(
          `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
          vm
        )
      }
      // isUpdatingChildComponent会在组件开始更新时置为true, 实现更新时置为false
      // 如果子组件中更改一个props时会导致父组件也从新渲染, 这里就会抛出正告
      defineReactive(props, key, value, () => {
        if (vm.$parent && !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 {
      // 使props变为响应式属性
      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.
    // 将props都代理到vm实例上, 以便代码中能够通过this.propName去拜访到对应的props
    if (!(key in vm)) {
      proxy(vm, `_props`, key)
    }
  }
  // 初始化结束, 开启响应式更新
  toggleObserving(true)
}

上述代码实现了把propsdata变为响应式,前面的computedwatch临时不讲,后续专门有一篇文章来写他们。

那么initState之后咱们还会执行一个callback函数,传入的是created参数,调用钩子函数created这个时候也就是页面曾经创立了,并且下一个生命周期为beforMount,在讲挂载之前,肯定要讲响应式零碎原理

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理