options用什么隔开 vm_vue2.x源码分析(三):new Vue时都做了什么

6cd9f2601cb25255f70f8557c3fd8313.png

书接上文,上面分析了引入Vue的时候都做了哪些事,一般项目里引入Vue之后可能还要添加一些公共组件、全局方法、插件等,主要是使用Vue.component、Vue.extend、Vue.use等方法,这些会在以后给出分析,然后就是new Vue()了,(注:其中Vue.extend也是需要new一下的,会在源码里有表现),主要分为4步

这部分的逻辑是从this._init(options)进入的_init是Vue原型对象上的方法

入口文件是src/core/instance/init.js

3.1 设置_uid以及_isVue

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
// 定义initMixin函数
export function initMixin (Vue: Class<Component>) {
     // 为Vue的原型对象添加_init方法
  Vue.prototype._init = function (options?: Object) {
      // 这里的this是指向的实例对象,而不是原型对象
    const vm: Component = this
    // a uid
    // 实例对象添加_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
    // 记录这是Vue实例化的
    vm._isVue = true

3.2 合并options

options的来源主要由两部分,一是new Vue(options)时传入的options,另一个是Vue的构造函数预先定义准确的说是vm.constructor实例的构造函数内的options(区别在于vm.constructor.options是可以被Vue.extend()改变的)

// 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.
      // 翻译上面的英文:优化内部组件实例化,因为动态选项合并非常慢,而且没有一个内部组件选项需要特殊处理
      // 应该就是说内部组件是用另一种优化的方法来解析options
      initInternalComponent(vm, options)
    } else {
        // 把下面的参数1的值和(参数2的值格式规整后)合在一起,给vm
      vm.$options = mergeOptions(
          // 解析构造函数的options
          // 你说实例对象没有constructor?但是架不住他爹__proto__有啊,这就是原型链的用法
        resolveConstructorOptions(vm.constructor),
        // 传入的options
        options || {},
        vm
      )
    }

function initInternalComponent (vm: Component, options: InternalComponentOptions) {
  const opts = vm.$options = Object.create(vm.constructor.options)
  // doing this because it's faster than dynamic enumeration.
  opts.parent = options.parent
  opts.propsData = options.propsData
  opts._parentVnode = options._parentVnode
  opts._parentListeners = options._parentListeners
  opts._renderChildren = options._renderChildren
  opts._componentTag = options._componentTag
  opts._parentElm = options._parentElm
  opts._refElm = options._refElm
  if (options.render) {
    opts.render = options.render
    opts.staticRenderFns = options.staticRenderFns
  }
}

export function resolveConstructorOptions (Ctor: Class<Component>) {
    // 将Vue构造函数自身的options赋值给options
  let options = Ctor.options
    // 如果构造函数是通过Vue.extend继承来的
    // 下面的方法跟Vue.extend有一些关系,可以先去读Vue.extend方法
  if (Ctor.super) {
      // 继续解析构造函数的父类
    const superOptions = resolveConstructorOptions(Ctor.super)
    // Ctor.superOptions是Vue.extend中子类中记录的父类的options
    const cachedSuperOptions = Ctor.superOptions
    // 如果父类中的options不等于子类中记录的父类的options
    // 目的来判断父类中的options 有没有发生变化,例如使用了Vue.mixin方法
    if (superOptions !== cachedSuperOptions) {
      // super option changed,
      // need to resolve new options.
      // 将子类中记录的options重置
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      // 检查options是否在后期被修改了,并将修改的选项找出来
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      // 如果被修改了
      if (modifiedOptions) {
           // 将modifiedOptions合并到Ctor.extendOptions中
        extend(Ctor.extendOptions, modifiedOptions)
      }
      // 将父类options与扩展的options合并在一起赋值给这个构造函数的options
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}

function resolveModifiedOptions (Ctor: Class<Component>): ?Object {
  let modified
  const latest = Ctor.options
  const extended = Ctor.extendOptions
  const sealed = Ctor.sealedOptions
  for (const key in latest) {
    if (latest[key] !== sealed[key]) {
      if (!modified) modified = {}
      modified[key] = dedupe(latest[key], extended[key], sealed[key])
    }
  }
  return modified
}

function dedupe (latest, extended, sealed) {
  // compare latest and sealed to ensure lifecycle hooks won't be duplicated
  // between merges
  if (Array.isArray(latest)) {
    const res = []
    sealed = Array.isArray(sealed) ? sealed : [sealed]
    extended = Array.isArray(extended) ? extended : [extended]
    for (let i = 0; i < latest.length; i++) {
      // push original options and not sealed options to exclude duplicated options
      if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) {
        res.push(latest[i])
      }
    }
    return res
  } else {
    return latest
  }
}

合并主要是调用了mergeOptions()方法,入口文件是src/core/util/options.js

export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  if (process.env.NODE_ENV !== 'production') {
//检查组件名称的合法性
    checkComponents(child)
  }
//如果传入的是类型是function,则取其options
  if (typeof child === 'function') {
    child = child.options
  }
//格式化props、inject、direcrives,主要是对开发时的多种写法进行统一
  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)
//处理options中的extends,都放到parent里
  const extendsFrom = child.extends
  if (extendsFrom) {
    parent = mergeOptions(parent, extendsFrom, vm)
  }
//处理options中的mixins,都放到parent里
  if (child.mixins) {
    for (let i = 0, l = child.mixins.length; i < l; i++) {
      parent = mergeOptions(parent, child.mixins[i], vm)
    }
  }
  const options = {}
  let key
//按parent上的属性和方法名来合并parent和child里的属性和方法
  for (key in parent) {
    mergeField(key)
  }
//再在child里摘吧摘吧,如果parent上没有这个属性和方法名,按这个属性和方法名来合并parent和child里的属性和方法
  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
}

3.3 初始化各种属性和事件

到了这里vm的$options属性算是整合完毕了,接下来就是其他的属性和方法了,还会调用一些生命周期钩子,本篇只是罗列一个大纲,后面应该会细讲

    /* istanbul ignore else */
//给vm添加_renderProxy()方法
    // 生产环境就老实地让vm._renderProxy等于vm的,非生产环境用ES6的Proxy这个玩意
    // vm._renderProxy,这是后期为render做准备的,作用是在render中将this指向vm._renderProxy
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
// expose real self
//给vm添加_self属性,指向自身
    vm._self = vm
//给vm添加属性:$children、$parent、$refs、$root、_directInactive、
//_inactive、_isBeingDestroyed、_isDestroyed、_isMounted、_watcher
    initLifecycle(vm)
//给vm添加属性:_events、hasHookEvent
    initEvents(vm)
//给vm添加属性:$attrs、$listeners、$scopedSlots、$slots、$vnode
//给vm添加方法:$createElement、_c
//给vm添加get代理:$attrs、$listeners
//给vm添加set代理:$attrs、$listeners
    initRender(vm)
//调用beforeCreate生命周期钩子内的方法
    callHook(vm, 'beforeCreate')
//获取inject定义的各属性值并增加对该属性的监听
    initInjections(vm) // resolve injections before data/props
//对prop,method,data,computed,watch的初始化
    initState(vm)
//设置 vm._provided值,以便子组件逐级查找
    initProvide(vm) // resolve provide after data/props
//调用create生命周期钩子内的方法
    callHook(vm, 'created')

3.4 挂载

这个就牛逼了,主要流程是el、template->render表达式->Vnode->DOM

if (vm.$options.el) {
  vm.$mount(vm.$options.el)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值