Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue2.x源码解析系列三:Options配置的处理 #24

Open
lihongxun945 opened this issue Jul 26, 2018 · 3 comments
Open

Vue2.x源码解析系列三:Options配置的处理 #24

lihongxun945 opened this issue Jul 26, 2018 · 3 comments

Comments

@lihongxun945
Copy link
Owner

lihongxun945 commented Jul 26, 2018

在正式说数据响应化之前,先让我们简单说下options的处理,其实对options的处理过程也挺复杂,但是这些细节并不是本文关注的重点,所这里我们只挑它的主要代码讲,至于一些细节比如如何进行 normalize 等有兴趣的话可以自己看看源码。

正如上图所示,我们上一章讲到过,在 _init 函数中,有这么一段代码进行 options 的合并,生成一个新的 this.$options。当然实际情况比这个图要复杂些。

vm.$options = mergeOptions(
  resolveConstructorOptions(vm.constructor),
  options || {},
  vm
)

这一段代码主要调用了两个函数 resolveConstructorOptionsmergeOptions,让我们从调用顺序来分别看看这两个函数的作用。

resolveConstructorOptions 会递归向super查找 options,如果找到了,那么就把父类的options和当前的options进行合并。

export function resolveConstructorOptions (Ctor: Class<Component>) {
  let options = Ctor.options
  if (Ctor.super) {
    // 把父类的options找出来,因为可能父类也有父类,因此这里是递归查找,把`parents`上的所有options都合并到当前。
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions

    //如果找到了,那么就把父类的 `options`和当前的 `options`进行一次合并。
    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 //如果指定了`name`,那么在自己身上注册一下自己。这样就可以直接在模板中用了。
      }
    }
  }
  return options
}

可能大家会有疑问,哪里来的父类呢?我们一般有两种方式来创建一个Vue组件:

//最常见的方式是直接创建,这种情况下不需要处理父类
new Vue(options)

//如果我们有一些常用的默认配置,我们可以自己创建一个自定义的类,此时,我们就需要进行 `options`合并了,不然我们自己创建的这个类上就没有 `MyVue.options`了。

const MyVue = Vue.extend(options)
new MyVue()

那么我们再看第二个函数,也就是 mergeOptions,顾名思义,就是把几个不同的 options合并成同一个,mergeOptions函数的定义在 core/util/options.js中。因为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
  }

  // normalize,归一化处理,这是什么意思呢?
  // 因为vue为了方便使用,同样的定义可以以不同的形式写出来,比如 `props:[‘name’]` 和 `props: { name: { type: String}}` 都是可以的,在解析这些配置之前,就要先把配置的格式统一一下以方便处理
  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)
  const extendsFrom = child.extends
  // 把孩子中的合并
  if (extendsFrom) {
    parent = mergeOptions(parent, extendsFrom, vm)
  }

  // mixins一般也是一个 options,因此也要处理。
  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
  // 这里真的开始进行父子组件的merge操作了。注意,不同的filed是有不同方案的,比如到底是要覆盖还是要合并等。`mergeFiled`函数是通过调用 `strats`上定义好的一些函数规则来实现的,他对不同的 `key` 有不同的规则,比如 `listeners` 就要进行数组的合并,`data` 就要进行 `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
}

好了,我们已经大致知道了 Vue 是如何处理 options的,下面让我们正式进入数据响应化吧。

下一篇:Vue2.x源码解析系列四:数据响应之Observer

@VimMing
Copy link

VimMing commented Oct 23, 2019

“至于一些细节比如如何进行 normailize 等有兴趣的话可以自己看看源码。” 上面有个单词拼错了

@lihongxun945
Copy link
Owner Author

@VimMing 已更正,感谢

@tonyzhou1890
Copy link

"不同的filed是有不同方案的,比如到底是要覆盖还是要合并等。mergeFiled函数是通过调用……"。filed -> field。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants