TODO:

  1. mergeOptions 第三个参数的具体意义

    函数作用

  2. 格式化不同格式的配置

  3. 根据具体的策略合并不同来源的选项
  1. export function mergeOptions (
  2. parent: Object,
  3. child: Object,
  4. vm?: Component
  5. ): Object {
  6. // 校验组件命名是否规范
  7. if (process.env.NODE_ENV !== 'production') {
  8. checkComponents(child)
  9. }
  10. // 如果 child 是另外一个构造函数则取其静态属性来进行合并
  11. if (typeof child === 'function') {
  12. child = child.options
  13. }
  14. // 格式化 Props 格式
  15. // 最终形态为 {[p: string]: {type: Number | String | Boolean | Array | Object, default?: any }}
  16. normalizeProps(child, vm)
  17. // 格式化 inject
  18. // 最终形态为 inject: {[p: string]: {from: string, [p: string]: string}}
  19. normalizeInject(child, vm)
  20. // 格式化 directives
  21. // 最终形态为 directive: {bind?: Function, update?: Function}
  22. normalizeDirectives(child)
  23. // _base 的有无代表是否已经 merge 过
  24. if (!child._base) {
  25. // 如果是继承的实例/构造函数,则递归的向上合并所有 options
  26. if (child.extends) {
  27. parent = mergeOptions(parent, child.extends, vm)
  28. }
  29. // 若果有 mixins, 则遍历合并 mixins 中的 options
  30. if (child.mixins) {
  31. for (let i = 0, l = child.mixins.length; i < l; i++) {
  32. parent = mergeOptions(parent, child.mixins[i], vm)
  33. }
  34. }
  35. }
  36. // 到此为止,除了 child 的 options 没合并外,父类和 mixins 的 options 均已合并
  37. // 将 parent.options 和 child.options 中的交集通过 config.optionMergeStrategies 中的配置过的对应策略来合并,
  38. // 比如 web 端的 el、propsData 如果两边都有就会报错,因为只有可能根节点, 通过 new Vue 的才有可能有需要有 el、propsData 配置
  39. // 如果没有对应策略则优先取 child 的值
  40. // 接着再将 child 中 parent 没有的合并到 options 中
  41. const options = {}
  42. let key
  43. for (key in parent) {
  44. mergeField(key)
  45. }
  46. for (key in child) {
  47. if (!hasOwn(parent, key)) {
  48. mergeField(key)
  49. }
  50. }
  51. function mergeField (key) {
  52. const strat = strats[key] || defaultStrat
  53. // 此处可能会没有 vm,没有 vm 意味着当前合并的场景并非 Vue 实例化,反之则是。
  54. // 如果没有 vm,则一些特殊的合并策略则会得出不同的结果
  55. // 比如
  56. options[key] = strat(parent[key], child[key], vm, key)
  57. }
  58. return options
  59. }

el、propsData 的合并策略

  1. if (process.env.NODE_ENV !== 'production') {
  2. strats.el = strats.propsData = function (parent, child, vm, key) {
  3. if (!vm) {
  4. warn(
  5. `option "${key}" can only be used during instance ` +
  6. 'creation with the `new` keyword.'
  7. )
  8. }
  9. return defaultStrat(parent, child)
  10. }
  11. }

由于 el、propsData 只有在 new Vue 的时候才会出现,所以如果没传了 vm 则意味着并非在 new Vue 的时候出现,则报错

data 的合并策略

  1. strats.data = function (
  2. parentVal: any,
  3. childVal: any,
  4. vm?: Component
  5. ): ?Function {
  6. if (!vm) {
  7. if (childVal && typeof childVal !== 'function') {
  8. process.env.NODE_ENV !== 'production' && warn(
  9. 'The "data" option should be a function ' +
  10. 'that returns a per-instance value in component ' +
  11. 'definitions.',
  12. vm
  13. )
  14. return parentVal
  15. }
  16. return mergeDataOrFn(parentVal, childVal)
  17. }
  18. return mergeDataOrFn(parentVal, childVal, vm)
  19. }

如果不是在实例化过程中,传的 child.data 不是函数则报错并且只返回 parent.data,反之则合并。
合并的规则是,如果不是在实例化过程中合并,则返回一个函数,该函数调用后则会调用返回 child.data 为优先的