vue源码学习总结 options合并策略

vue options合并流程

  1. 格式化数据
    1. 格式化props为小驼峰命名的对象
    2. 格式化inject为{key : { from: val}}形式对象
    3. 格式化directives为{ bind: defFn, update: defFn }形式对象
  2. 依次合并 parent, child.extends(单个继承组件), child.mixins(数组), child.options,取值越靠后越优先
  3. 合并策略:
    1. propsData props methods inject computed component directive filter相同key优先child的值
    2. 如果data和provide是函数,优先使用child的函数执行结果对象,递归遍历该对象合并
      如果data和provide是对象,递归遍历对象合并
      如果data和provide的某个属性是基础类型,优先使用child的合并
    3. hook钩子函数和watcher合并成数组,先执行父,再执行子

合并函数入口(vue源码)

src/core/util/options.js 的 mergeOptions函数

/* @flow */

import config from '../config'
import {
    warn } from './debug'
import {
    nativeWatch } from './env'
import {
    set } from '../observer/index'

import {
   
  ASSET_TYPES,
  LIFECYCLE_HOOKS
} from 'shared/constants'

import {
   
  extend,
  hasOwn,
  camelize,
  toRawType,
  capitalize,
  isBuiltInTag,
  isPlainObject
} from 'shared/util'

/**
 * Option overwriting strategies are functions that handle
 * how to merge a parent option value and a child option
 * value into the final value.
 * 选项重载策略是如何合并父选项的值和子选项的值得到最终的值
 */
const strats = config.optionMergeStrategies
/**
 * Options with restrictions
 */
// propsData合并策略:优先使用child的值合并
if (process.env.NODE_ENV !== 'production') {
   
  strats.el = strats.propsData = function (parent, child, vm, key) {
   
    if (!vm) {
   
      warn(
        `option "${
     key}" can only be used during instance ` +
        'creation with the `new` keyword.'
      )
    }
    return defaultStrat(parent, child)
  }
}

/**
 * Helper that recursively merges two data objects together.
 */
function mergeData (to: Object, from: ?Object): Object {
   
  if (!from) return to
  let key, toVal, fromVal
  const keys = Object.keys(from)
  for (let i = 0; i < keys.length; i++) {
   
    key = keys[i]
    toVal = to[key]
    fromVal = from[key]
    if (!hasOwn(to, key)) {
   
      set(to, key, fromVal)
    } else if (isPlainObject(toVal) && isPlainObject(fromVal)) {
   
      mergeData(toVal, fromVal)
    }
  }
  return to
}

/**
 * Data
 */
export function mergeDataOrFn (
  parentVal: any,
  childVal: any,
  vm?: Component
): ?Function {
   
  if (!vm) {
   
    // in a Vue.extend merge, both should be functions
    if (!childVal) {
   
      return parentVal
    }
    if (!parentVal) {
   
      return childVal
    }
    // when parentVal & childVal are both present,
    // we need to return a function that returns the
    // merged result of both functions... no need to
    // check if parentVal is a function here because
    // it has to be a function to pass previous merges.
    return function mergedDataFn () {
   
      return mergeData(
        typeof childVal === 'function' ? childVal.call(this) : childVal,
        typeof parentVal === 'function' ? parentVal.call(this) : parentVal
      )
    }
  } else if (parentVal || childVal) {
   
    return function mergedInstanceDataFn () {
   
      // instance merge
      //data可能是函数也可能是对象,是函数则执行获取结果对象
      const instanceData = typeof childVal === 'function'
        ? childVal.call(vm)
        : childVal
      const defaultData = typeof parentVal === 'function'
        ? parentVal.call(vm)
        : parentVal
      if (instanceData) {
   
        //以子对象为主,将父对象递归合并进去
        return mergeData(instanceData, defaultData)
      } else {
   
        return defaultData
      }
    }
  }
}

// option.data合并策略
// 如何data是函数,优先使用child的函数结果对象递归合并
// 如何data是对象,递归遍历对象合并
// 如何data是基础类型,优先使用child的合并
strats.data = function (
  parentVal: any,
  childVal: any,
  vm?: Component
): ?Function {
   
  if (!vm) {
   
    if (childVal && typeof childVal !== 'function') {
   
      process.env.NODE_ENV !== 'production' && warn(
        'The "data" option should be a function ' +
        'that returns a per-instance value in component ' +
        'definitions.',
        vm
      )

      return parentVal
    }
    return mergeDataOrFn.call(this, parentVal, childVal)
  }

  return mergeDataOrFn(parentVal, childVal, vm)
}

/**
 * Hooks and props are merged as arrays.
 * 钩子函数和属性合并成数组,先执行父,再执行子
 */
function mergeHook (
  parentVal: ?Array<Function>,
  childVal: ?Function | ?Array<Function>
): ?Array<Function> {
   
  return childVal
    ? parentVal
      ? parentVal.concat(childVal)
      : Array.isArray(childVal)
        ? childVal
        : [childVal]
    : parentVal
}

// option.beforeCreate option.created等钩子函数合并策略
LIFECYCLE_HOOKS.forEach(hook => {
   
  strats[hook] = mergeHook
})

/**
 * Assets
 *
 * When a vm is present (instance creation), we need to do
 * a three-way merge between constructor options, instance
 * options and parent options.
 */
function mergeAssets (
  parentVal: ?Object,
  childVal: ?Object,
  vm?: Component,
  key: string
): Object {
   
  const res = Object.create(parentVal || null)
  if (childVal) {
   
    process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值