写点什么

【Vue2.x 源码学习】第三十七篇 - 组件部分 - 组件的合并

用户头像
Brave
关注
发布于: 1 小时前
【Vue2.x 源码学习】第三十七篇 - 组件部分 - 组件的合并

一,前言


上篇,介绍了 Vue.extend 实现,主要涉及以下几个点:


  • Vue.extend 简介;

  • Vue.extend 实现,包括:组件初始化;子类继承父类;修复 constructor 指向问题;


本篇,组件部分 - 组件的合并策略;



二,组件合并策略

1,前文回顾


在前面的两篇中,分别介绍了组件部分的 Vue.component 和 Vue.extend 实现:

  • Vue.component:定义组件并维护到全局Vue.options.components中;

  • Vue.extend:根据选项参数创建子类,即组件的构造函数;

2,组件合并的位置

执行情况分析


  <script>    Vue.component('my-button',{      name:'my-button',      template:'<button>全局组件</button>'    })  </script>
复制代码


上边的Vue.component执行完成后,Vue.options.components中就已经存储了全局组件的构造函数;


  <script>    Vue.component('my-button',{      name:'my-button',      template:'<button>全局组件</button>'    })    new Vue({      el: "#app",      components:{        'my-button':{          template:'<button>局部组件</button>'        }      }    });  </script>
复制代码


当 new Vue 执行时,会进行组件的初始化流程,调用 _init 方法:


// src/init.js#initMixin
Vue.prototype._init = function (options) { const vm = this; // this 指向当前 vue 实例 // vm.$options = options; // 将 Vue 实例化时用户传入的options暴露到vm实例上 // 此时需使用 options 与 mixin 合并后的全局 options 再进行一次合并 vm.$options = mergeOptions(vm.constructor.options, options); // 目前在 vue 实例化时,传入的 options 只有 el 和 data 两个参数 initState(vm); // 状态的初始化
if (vm.$options.el) { // 将数据挂在到页面上(此时,数据已经被劫持) vm.$mount(vm.$options.el) } }
复制代码


其中,mergeOptions方法对vm.constructor.optionsoptions进行合并:

  • vm.constructor.options包含Vue.options.components中的全局组件;

  • options 为执行 new Vue 时传入的局部组件选项;


即在这个位置,就会进行全局组件局部组件的合并;

函数 or 对象分析

  • vm.constructor.options中的全局组件 my-button 是一个函数;

  • 而在 options 中的局部组件 my-button 是一个对象;




原因分析:

  • 全局组件定义 Vue.component 中,传入的对象,内部会被 Vue.extends 处理成为函数;

  • 内部组件定义 components 中,传入的对象,内部不会被 Vue.extends 处理,仍是对象;


  <script>    // 全局组件    Vue.component('my-button',{ // 内部被 Vue.extends 处理,成为一个构造函数      name:'my-button',      template:'<button>Hello Vue 全局组件</button>'    })    new Vue({      el: "#app",      components:{  // 这里不会被 Vue.extends 处理,就真的是一个对象        'my-button':{// 局部组件          template:'<button>Hello Vue 局部组件</button>'        }      }    });  </script>
复制代码

3,组件合并的策略

策略模式


在之前做 mixin 生命周期的合并时,在 mergeOptions 方法中使用了策略模式:

  • 针对不同生命周期钩子,声明各自的合并策略;

  • 如果在没有找到对应的策略,默认使用新值覆盖老值;


let strats = {};  // 存放所有策略let lifeCycle = ['beforeCreate','created','beforeMount','mounted'];// 创建各生命周期的合并策略lifeCycle.forEach(hook => {  strats[hook] = function (parentVal, childVal) {    // 在strats策略对象中,定义了各生命周期的合并策略  }})
/** * 对象合并:将childVal合并到parentVal中 * @param {*} parentVal 父值-老值 * @param {*} childVal 子值-新值 */export function mergeOptions(parentVal, childVal) { let options = {}; for(let key in parentVal){ mergeFiled(key); } for(let key in childVal){ // 当新值存在,老值不存在时:添加到老值中 if(!parentVal.hasOwnProperty(key)){ mergeFiled(key); } } // 合并当前 key function mergeFiled(key) { // 策略模式:获取当前 key 的合并策略 let strat = strats[key]; if(strat){ options[key] = strat(parentVal[key], childVal[key]); }else{ // 默认合并策略:新值覆盖老值 options[key] = childVal[key] || parentVal[key]; } }
return options;}
复制代码


这样,就可以通过策略模式,实现了在 strats 中配置 component 对应的合并策略;

在 mergeOptions 方法执行并处理 component 合并时,就会根据配置好的策略进行合并;

合并策略


vm.constructor.optionsoptions进行合并,先找局部组件再找全局组件;


// parentVal为函数;childVal为对象;strats.component = function (parentVal, childVal) {  // 继承:子类可以沿着链找到父类的属性 childVal.__proto__ = parentVal   let res = Object.create(parentVal);  if(childVal){    for (let key in childVal) {     res[key] = childVal[key];    }        return res;  }}
复制代码

4,组件合并后测试


测试组件合并


  <script>    // 全局组件    Vue.component('my-button',{      name:'my-button',      template:'<button>Hello Vue 全局组件</button>'    })    new Vue({      el: "#app",      components:{// 局部组件        'my-button':{          template:'<button>Hello Vue 局部组件</button>'        }      }    });  </script>
复制代码


在 mergeOptions 方法中,会找到预设的组件合并策略函数:



组件合并:


此时,参数 parentVal 是一个函数;参数 childVal 是一个对象;



生成的新对象 res 可以在链上拿到 parentVal 上的全局组件:



再将儿子全部合并到新生成的 res 对象上:



这样,在 res 上查找组件时,会先查找局部组件,如果没找到,则继续通过链找到全局组件;

优先查找局部组件,如果没有会沿着链向上继续找,找到局部组件;


三,结尾


本篇,介绍了组件部分-组件的合并,主要涉及以下几个点:


  • 组件初始化情况;

  • 组件合并的位置;

  • 组件合并的策略;

  • 组件合并后测试;


下一篇,组件部分-组件的编译;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【Vue2.x 源码学习】第三十七篇 - 组件部分 - 组件的合并