关于前端:vue源码分析从new-Vue开始

42次阅读

共计 17591 个字符,预计需要花费 44 分钟才能阅读完成。

初学 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 的源码:

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,在讲挂载之前,肯定要讲 响应式零碎原理

正文完
 0