找回密码
 立即注册
查看: 125|回复: 6

Vue2 mergeOptions选项合并策略

[复制链接]

9

主题

37

回帖

151

积分

注册会员

积分
151
发表于 2022-9-19 22:22:40 | 显示全部楼层 |阅读模式
mergeOptions选项合并策略的主要作用:
  • 对 options 进行规范
  • options 的合并, 默认策略和自定义策略
合并策略目的:围绕着组件和子类来进行限制的
  1. const vm= new Vue({
  2.     el:"#app",
  3.     data:{
  4.         test:"这是一个测试"
  5.     }
  6.     })
复制代码
当在控制台打印vm.$options可以看到多了几个属性

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:23:41 | 显示全部楼层
Vue.js在初始化的时候, 有些默认的配置, initGlobalAPI 函数为 Vue.options, 进行了一些初始化的默认配置

  1.     function initGlobalAPI(Vue) {
  2.     ...
  3.     Vue.options = Object.create(null);
  4.     /*对资源assets的默认初始化*/
  5.     ASSET_TYPES.forEach(function (type) {
  6.         Vue.options[type + 's'] = Object.create(null);
  7.     });

  8.     // this is used to identify the "base" constructor to extend all plain-object
  9.     // components with in Weex's multi-instance scenarios.
  10.     Vue.options._base = Vue;

  11.     extend(Vue.options.components, builtInComponents);
  12.    
复制代码
...
}
在 vm._init 函数中,调用了 mergeOptions 函数, 进行选项的合并

  1. function initMixin(Vue) {
  2.     Vue.prototype._init = function (options) {
  3.         var vm = this;
  4.         ...
  5.         // merge options
  6.         if (options && options._isComponent) {
  7.             // optimize internal component instantiation
  8.             // since dynamic options merging is pretty slow, and none of the
  9.             // internal component options needs special treatment.
  10.             initInternalComponent(vm, options);
  11.         } else {
  12.             /*
  13.             * 在这个调用了mergeOptions函数
  14.             * 获取resolveConstructorOptions(vm.constructor)返回值
  15.             */
  16.             vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor), options || {}, vm)
  17.         }
  18.         ...
  19.     };
  20. }
复制代码
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:24:07 | 显示全部楼层
接下来看 mergeOptions 函数的实现:

  1. /* 用于把parent,child进行合并 */
  2. function mergeOptions(parent, child, vm) {
  3.    /*检测options里,组件的名字命名规范*/
  4.     {
  5.         checkComponents(child);
  6.     }
  7.     /*child也可以是是一个函数*/
  8.     if (typeof child === 'function') {
  9.         child = child.options;
  10.     }
  11.     /*规范化Props*/
  12.     normalizeProps(child, vm);
  13.     /*规范化Inject*/
  14.     normalizeInject(child, vm);
  15.     /*规范化Directives*/
  16.     normalizeDirectives(child);

  17.     // Apply extends and mixins on the child options,
  18.     // but only if it is a raw options object that isn't
  19.     // the result of another mergeOptions call.
  20.     // Only merged options has the _base property.
  21.     if (!child._base) {
  22.         if (child.extends) {
  23.             /*递归调用把extends合并到parent上*/
  24.             parent = mergeOptions(parent, child.extends, vm);
  25.         }
  26.         if (child.mixins) {
  27.             /*递归调用把mixins合并到parent上*/
  28.             for (var i = 0, l = child.mixins.length; i < l; i++) {
  29.                 parent = mergeOptions(parent, child.mixins[i], vm);
  30.             }
  31.         }
  32.     }
  33.     /*最终合并完成要返回的$options, vm.$options对象*/
  34.     var options = {};
  35.     var key;
  36.     for (key in parent) {  /*先判断parent上是否存在key*/
  37.         mergeField(key);
  38.     }
  39.     for (key in child) {
  40.         if (!hasOwn(parent, key)) { /*判断parent以后,在判断child是上否有key*/
  41.             mergeField(key);
  42.         }
  43.     }
  44.     /* 合并字段 */
  45.     function mergeField(key) {
  46.         /*根据不同的 */
  47.         var strat = strats[key] || defaultStrat;
  48.         options[key] = strat(parent[key], child[key], vm, key);
  49.     }
  50.     /*返回最终合并完成的options, 会赋值给Vue.$options*/
  51.     return options
  52. }
复制代码
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:24:27 | 显示全部楼层
通过分析 mergeOptions 函数, 主要做了一下几件事情:

检查组件的命名是否规范
规范化 Props,Inject,Directives
Vue 选项的合并 mergeOptions的第三个参数 vm, 用于区分根实例还是子组件. 在上面的代码中, 传递了vm参数, mergeOptions在另一个也被调用了,在 Vue.extend() 这个函数中, 没有传递 vm 参数
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:25:54 | 显示全部楼层
检查组件的命名是否规范checkComponents(child)
f
  1. unction checkComponents(options) {
  2.     for (var key in options.components) {
  3.         validateComponentName(key);
  4.     }
  5. }
复制代码

将 child 的传递进来, 进行遍历, 获取到每个 key. 将每个 key 作为参数传递给 validateComponentName(key);

  1. function validateComponentName(name) {
  2.     if (!new RegExp(("^[a-zA-Z][\\-\\.0-9_" + unicodeLetters + "]*$")).test(name)) {
  3.         warn(
  4.             'Invalid component name: "' + name + '". Component names ' +
  5.             'should conform to valid custom element name in html5 specification.'
  6.         );
  7.     }
  8.     if (isBuiltInTag(name) || config.isReservedTag(name)) {
  9.         warn(
  10.             'Do not use built-in or reserved HTML elements as component ' +
  11.             'id: ' + name
  12.         );
  13.     }
  14. }
复制代码

isBuiltInTag 函数, 不能是 slot,component Vue 内置组件的名字

  1. var isBuiltInTag = makeMap('slot,component', true);
复制代码

isReservedTag 函数, 组件的名字不能为html标签的名字和svg标签的名字

  1. var isReservedTag = function (tag) {
  2.     return isHTMLTag(tag) || isSVG(tag)
  3. };
复制代码

从 validateComponentName 分析得出组件的命名规范应该满足一下的要求:

  /^[a-zA-Z][-.0-9_/.test(name) 为 true
  isBuiltInTag(name) 或者 config.isReservedTag(name) 为 false
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:26:31 | 显示全部楼层
Vue选项的合并
  1. /*最终合并完成要返回的$options, vm.$options对象*/
  2.     var options = {};
  3.     var key;
  4.     /*对parent进行遍历*/
  5.     for (key in parent) {
  6.         mergeField(key);
  7.     }
  8.     /*对child进行遍历*/
  9.     for (key in child) {
  10.         /*hasOwn多了一层的判断, 如果child的key, 已经在上面parent进行了mergeField*/
  11.         /*就不在进行遍历, 避免重复调用*/
  12.         if (!hasOwn(parent, key)) {
  13.             mergeField(key);
  14.         }
  15.     }
  16.      /*对parent, child的key字段进行和合并,采取了不同的策略*/
  17.      function mergeField(key) {
  18.          var strat = strats[key] || defaultStrat;
  19.          /*把 strat(parent[key], child[key], vm, key)函数的返回值给对应的options[key]*/
  20.          options[key] = strat(parent[key], child[key], vm, key);
  21.      }
  22.      return options
  23. }
复制代码

上面的代码, 最终返回一个 options, 先对 parent 进行遍历, 在对 child 进行遍历, 在遍历 child 时, 多了一层的限制, 对于相同的key, 如果 parent 已经进 mergeField, child,就不在进行遍历.
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:27:08 | 显示全部楼层
mergeField 函数:

先去检测 strats[key], 对该 key 是否有自定义的合并策略, 如果有就直接使用,像 el, watch, data等都进行自定义的合并策略
如果没有自定义的策略, 就是用是默认的策略合并
对于 strats[key] 函数, 什么是 strats?

  1. /**
  2. * Option overwriting strategies are functions that handle
  3. * how to merge a parent option value and a child option
  4. * value into the final value.
  5. */
  6. var strats = config.optionMergeStrategies;
复制代码

config.optionMergeStrategies 是一个合并选项的策略对象,这个对象下包含很多函数, 这些函数就可以认为是合并特定选项的策略 el, data, watch等进行了合并的限制

在看看 defaultStrat 函数的实现, childVal === undefined 直接使用 parentVal

  1. var defaultStrat = function (parentVal, childVal) {
  2.     return childVal === undefined? parentVal : childVal
  3. };
复制代码
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|粤嵌技术交流空间

GMT+8, 2025-7-7 13:13 , Processed in 0.683568 second(s), 18 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表