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

    Vue2.x源码解析系列三:Options配置的处理 - 图1

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

    1. vm.$options = mergeOptions(
    2. resolveConstructorOptions(vm.constructor),
    3. options || {},
    4. vm
    5. )

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

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

    1. export function resolveConstructorOptions (Ctor: Class<Component>) {
    2. let options = Ctor.options
    3. if (Ctor.super) {
    4. // 把父类的options找出来,因为可能父类也有父类,因此这里是递归查找,把`parents`上的所有options都合并到当前。
    5. const superOptions = resolveConstructorOptions(Ctor.super)
    6. const cachedSuperOptions = Ctor.superOptions
    7. //如果找到了,那么就把父类的 `options`和当前的 `options`进行一次合并。
    8. if (superOptions !== cachedSuperOptions) {
    9. // super option changed,
    10. // need to resolve new options.
    11. Ctor.superOptions = superOptions
    12. // check if there are any late-modified/attached options (#4976)
    13. const modifiedOptions = resolveModifiedOptions(Ctor)
    14. // update base extend options
    15. if (modifiedOptions) {
    16. extend(Ctor.extendOptions, modifiedOptions)
    17. }
    18. options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
    19. if (options.name) {
    20. options.components[options.name] = Ctor //如果指定了`name`,那么在自己身上注册一下自己。这样就可以直接在模板中用了。
    21. }
    22. }
    23. }
    24. return options
    25. }

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

    1. //最常见的方式是直接创建,这种情况下不需要处理父类
    2. new Vue(options)
    3. //如果我们有一些常用的默认配置,我们可以自己创建一个自定义的类,此时,我们就需要进行 `options`合并了,不然我们自己创建的这个类上就没有 `MyVue.options`了。
    4. const MyVue = Vue.extend(options)
    5. new MyVue()

    那么我们再看第二个函数,也就是 mergeOptions,顾名思义,就是把几个不同的 options合并成同一个,mergeOptions函数的定义在 core/util/options.js中。因为options中有很多字段,不同字段的处理方式会不同,比如有些字段就会子类覆盖父类,有些字段可能就需要把值合并起来。我们先看看 mergeOptions 函数:

    1. export function mergeOptions (
    2. parent: Object,
    3. child: Object,
    4. vm?: Component
    5. ): Object {
    6. if (process.env.NODE_ENV !== 'production') {
    7. checkComponents(child)
    8. }
    9. // 因为我们的定义即可能是一个对象,也可能是一个函数。
    10. if (typeof child === 'function') {
    11. child = child.options
    12. }
    13. // normalize,归一化处理,这是什么意思呢?
    14. // 因为vue为了方便使用,同样的定义可以以不同的形式写出来,比如 `props:[‘name’]` 和 `props: { name: { type: String}}` 都是可以的,在解析这些配置之前,就要先把配置的格式统一一下以方便处理
    15. normalizeProps(child, vm)
    16. normalizeInject(child, vm)
    17. normalizeDirectives(child)
    18. const extendsFrom = child.extends
    19. // 把孩子中的合并
    20. if (extendsFrom) {
    21. parent = mergeOptions(parent, extendsFrom, vm)
    22. }
    23. // mixins一般也是一个 options,因此也要处理。
    24. if (child.mixins) {
    25. for (let i = 0, l = child.mixins.length; i < l; i++) {
    26. parent = mergeOptions(parent, child.mixins[i], vm)
    27. }
    28. }
    29. const options = {}
    30. let key
    31. // 这里真的开始进行父子组件的merge操作了。注意,不同的filed是有不同方案的,比如到底是要覆盖还是要合并等。`mergeFiled`函数是通过调用 `strats`上定义好的一些函数规则来实现的,他对不同的 `key` 有不同的规则,比如 `listeners` 就要进行数组的合并,`data` 就要进行 `key` 的合并,而一些其他的就直接子类覆盖父类。
    32. for (key in parent) {
    33. mergeField(key)
    34. }
    35. for (key in child) {
    36. if (!hasOwn(parent, key)) {
    37. mergeField(key)
    38. }
    39. }
    40. function mergeField (key) {
    41. const strat = strats[key] || defaultStrat
    42. options[key] = strat(parent[key], child[key], vm, key)
    43. }
    44. return options
    45. }

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

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