Skip to content

源码分析-父子类合并策略

1. 基本概念

1.1 什么是合并策略

合并策略是Vue在处理父子组件选项时使用的一套规则系统,用于决定如何合并父组件和子组件的各种选项(data、methods、computed等)。

1.2 合并策略的意义

  • 确保组件继承的正确性
  • 维护数据的响应式
  • 避免命名冲突
  • 优化性能

2. 核心实现

2.1 合并选项

js
// src/core/util/options.js
const strats = config.optionMergeStrategies

// 默认策略
const defaultStrat = function(parentVal, childVal) {
  return childVal === undefined
    ? parentVal
    : childVal
}

// 数据合并策略
strats.data = function(parentVal, childVal, vm) {
  if (!vm) {
    if (childVal && typeof childVal !== 'function') {
      warn('data option should be a function.')
      return parentVal
    }
    return mergeDataOrFn(parentVal, childVal)
  }
  return mergeDataOrFn(parentVal, childVal, vm)
}

2.2 生命周期钩子合并

js
// 生命周期钩子合并策略
function mergeHook(parentVal, childVal) {
  const res = childVal
    ? parentVal
      ? parentVal.concat(childVal)
      : Array.isArray(childVal)
        ? childVal
        : [childVal]
    : parentVal
  return res
    ? dedupeHooks(res)
    : res
}

LIFECYCLE_HOOKS.forEach(hook => {
  strats[hook] = mergeHook
})

3. 具体策略分析

3.1 data合并

js
// data合并示例
const Parent = {
  data() {
    return {
      message: 'parent'
    }
  }
}

const Child = {
  data() {
    return {
      title: 'child',
      message: 'override'
    }
  }
}

// 最终合并结果
{
  message: 'override',
  title: 'child'
}

3.2 methods合并

js
// methods合并策略
strats.methods = function(parentVal, childVal) {
  if (!parentVal) return childVal
  const ret = Object.create(null)
  extend(ret, parentVal)
  if (childVal) extend(ret, childVal)
  return ret
}

4. 高级特性

4.1 自定义合并策略

js
// 自定义选项合并策略
Vue.config.optionMergeStrategies.myOption = function(parentVal, childVal) {
  return childVal ? childVal.concat(parentVal) : parentVal
}

// 使用示例
const Parent = {
  myOption: ['parent']
}

const Child = {
  myOption: ['child']
}

// 合并结果: ['child', 'parent']

4.2 Mixins合并

js
// mixins合并示例
const mixin = {
  created() {
    console.log('mixin created')
  }
}

export default {
  mixins: [mixin],
  created() {
    console.log('component created')
  }
}

5. 实际应用

5.1 组件继承

js
// 基础组件
const BaseComponent = {
  data() {
    return {
      loading: false
    }
  },
  methods: {
    baseMethod() {
      // 基础方法
    }
  }
}

// 继承组件
export default {
  extends: BaseComponent,
  data() {
    return {
      title: 'Extended Component'
    }
  }
}

5.2 全局混入

js
// 全局mixin
Vue.mixin({
  created() {
    const options = this.$options
    if (options.store) {
      this.$store = options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
  }
})

6. 性能优化

6.1 缓存策略

js
// 缓存合并结果
function mergeOptions(parent, child, vm) {
  const key = parent.key + child.key
  const cache = vm.$options._cache
  
  if (cache[key]) {
    return cache[key]
  }
  
  const ret = mergeData(parent, child)
  cache[key] = ret
  return ret
}

6.2 延迟合并

js
// 延迟数据合并
function deferredMerge(parentVal, childVal) {
  return () => {
    const parent = typeof parentVal === 'function'
      ? parentVal.call(this)
      : parentVal
    const child = typeof childVal === 'function'
      ? childVal.call(this)
      : childVal
      
    return mergeData(parent, child)
  }
}

7. 调试技巧

7.1 追踪合并过程

js
// 开发环境下的合并追踪
if (process.env.NODE_ENV !== 'production') {
  strats.data = function(parentVal, childVal, vm) {
    if (!vm) {
      console.log('Merging data:', { parentVal, childVal })
    }
    return mergeDataOrFn(parentVal, childVal, vm)
  }
}

7.2 合并冲突检测

js
function checkConflict(parentVal, childVal, key) {
  if (parentVal && childVal && parentVal[key] && childVal[key]) {
    console.warn(`Duplicate key detected: ${key}`)
  }
}

8. 注意事项

  1. 避免深层次的继承
  2. 合理使用mixins
  3. 注意命名冲突
  4. 关注性能影响
  5. 保持选项的纯函数特性