Vue源码分析(3)--选项合并过程mergeOptions

前言

本文是vue2.x源码分析的第三篇,主要讲解选项合并过程mergeOptions!

先看调用形式

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

1、执行resolveConstructorOptions(vm.constructor)

function resolveConstructorOptions (Ctor) {
  var options = Ctor.options; //Ctor即Vue$3,Vue$3.options是在第一节中通过extend函数赋值的
  if (Ctor.super) {  //这里由于没有super属性,故直接返回了options
    var superOptions = resolveConstructorOptions(Ctor.super);
    var cachedSuperOptions = Ctor.superOptions;
    if (superOptions !== cachedSuperOptions) {
      // super option changed,
      // need to resolve new options.
      Ctor.superOptions = superOptions;
      // check if there are any late-modified/attached options (#4976)
      var 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;
      }
    }
  }
  return options
}

返回的options长这个样子:

options = {
    components: {
        KeepAlive,
        Transition,
        TransitionGroup
    },
    directives: {
        model,
        show
    },
    filters: {},
    _base: Vue
}

2、执行mergeOptions (parent,child,vm)

parent即上述返回的options,child即new Vue(options)传入的options

function mergeOptions (parent,child,vm) {
  {
    //检查传入的components选项的名字是否符合规定(非内置标签)
    checkComponents(child);
  }
  //将传入的child.props转换为驼峰式结构的对象表达形式.props能使用数组和对象语法,
但在内部都会重新遍历封装到一个新对象中。
  normalizeProps(child);
  //directives使用对象语法,对象中属性的值只能为函数,该操作会将函数绑定到指令的bind和update函数上
  normalizeDirectives(child);
  //若存在extends,则将其内容合并到父对象parent中保存,最后再和自身child合并,extends最好是对象语法
  var extendsFrom = child.extends; //暂不清楚作用,记为Unknown3.1
  if (extendsFrom) {
    parent = typeof extendsFrom === 'function'
      ? mergeOptions(parent, extendsFrom.options, vm)
      : mergeOptions(parent, extendsFrom, vm);
  }
  //若存在mixins,则将其内容合并到父对象parent中保存,最后再和自身child合并,mixins只能是数组语法,数组中元素可以是对象
  if (child.mixins) {
    for (var i = 0, l = child.mixins.length; i < l; i++) {
      var mixin = child.mixins[i];
      if (mixin.prototype instanceof Vue$3) {
        mixin = mixin.options;
      }
      parent = mergeOptions(parent, mixin, vm);
    }
  }
  //初始化一个对象,用于存储parent和child合并后的内容,并作为mergeOptions函数的结果返回
  var options = {};
  var key;
  for (key in parent) {
    mergeField(key);
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key);
    }
  }
  //使用策略对象对parent和child进行合并
  function mergeField (key) {
    var strat = strats[key] || defaultStrat;
    options[key] = strat(parent[key], child[key], vm, key);
  }
  return options
}

2.1、分析checkComponents(child)

function checkComponents (options) {
  for (var key in options.components) {//对象遍历形式,故components选项只能用对象,不用数组
    var lower = key.toLowerCase();
    //组件名字不能是内置标签(slot,component)和保留标签(html标签和svg标签),用了闭包实现
    if (isBuiltInTag(lower) || config.isReservedTag(lower)) {
      warn(
        'Do not use built-in or reserved HTML elements as component ' +
        'id: ' + key
      );
    }
  }
}

2.2、分析normalizeProps(child);

function normalizeProps (options) {
  var props = options.props;
  if (!props) { return }
  var res = {};
  var i, val, name;
  if (Array.isArray(props)) { //props是数组
    i = props.length;
    while (i--) {
      val = props[i];
      if (typeof val === 'string') {
        name = camelize(val);//若val是*-i*的形式,则将其转为*I*,如v-header会被转为vHeader并被缓存
        res[name] = { type: null }; //每个val的值设为含有type属性的对象
      } else {
        warn('props must be strings when using array syntax.');
      }
    }
  } else if (isPlainObject(props)) {//props是对象
    for (var key in props) {
      val = props[key];
      name = camelize(key);
      res[name] = isPlainObject(val)//若用对象语法,则每个key的value对象最好包含type属性
        ? val
        : { type: val };
    }
  }
  options.props = res;
}

// 调用前:options.props=['header-value'];
// 调用后:options.props={
//           'headerValue':{type:null}
//         }

2.3、分析normalizeDirectives(child)

function normalizeDirectives (options) {
  var dirs = options.directives;
  if (dirs) {
    for (var key in dirs) { //数组也可以用for in遍历,故directives选项能用对象和数组语法,当只关心数组的value而不关心key时可以用for in遍历
      var def = dirs[key];
      if (typeof def === 'function') {
        dirs[key] = { bind: def, update: def };
      }
    }
  }
}
// 调用前:options.directives={
//            v-focus:function some(){}
//         };
// 调用后:options.directives={
//           'v-focus':{
//               bind:some,
//               update:some
//           }
//         }

2.4、分析extends和mixins

  二者作用都是进一步丰富parent选项,其本身是调用mergeOptions,这里不做分析,注意mixins只能用数组语法

2.5、分析合并策略对象strats

  • 10个生命周期函数--mixins中同名属性用数组保存,mixins中的优先级高,先调用,
    合并后类似这样:

    beforeCreate:[mixins中的beforeCreate,child中的beforeCreate]
    
  • 3个assets(directives、components、filters)--继承策略,以parent中同名属性的值为原型对象,以child中同名属性的值做实例属性,指令合并后类似这样:

    directives:{'v-focus':obj1,__proto__:{model:obj2,show:obj3}}
    
  • el与propsData、也是用defaultStrat函数,只是会先判断vm实例是否存在

  • data 返回一个函数mergedInstanceDataFn

  • watch 同生命周期函数的合并策略

  • props与methods与computed 覆盖策略,mixins中的同名属性会被child覆盖
    props合并后类似这样:

    props:[child中的属性]
    
  • 其余选项的合并策略采用defaultStrat函数

3、小结

  • 下一步分析内容
    ☐ 主要执行过程-2(实例的初始化过程)
    ☐ 主要执行过程-3(依赖收集、组件挂载等过程)
  • 遗留问题解决情况
    ☐ Unknown3.1
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容