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

news2024/11/14 22:36:56

一,前言

上篇,介绍了 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 是一个对象;

image.png

image.png

原因分析:

  • 全局组件定义 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 方法中,会找到预设的组件合并策略函数:

image.png

组件合并:

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

image.png

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

image.png

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

image.png

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

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


三,结尾

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

  • 组件初始化情况;
  • 组件合并的位置;
  • 组件合并的策略;
  • 组件合并后测试;

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


维护日志

  • 20210812:
    • 添加了少量的三级标题,使内容分布更加清晰;
    • 微调了部分描述与代码注释,有助于更好的理解;
    • 部分内容进行规整,形成无需列表,使思路的表述更加清晰易懂;
    • 添加了部分图示与实际测试结果;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/175845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于ssm jsp在线教育系统源码

演示视频&#xff1a; 基于ssm jsp在线教育系统源码范围 系统主要目标设计 随着互联网发展&#xff0c;在线教学成为一种支持知识共享&#xff0c;无距离知识交流的一种方式&#xff0c;我们的系统主要完成在线视频观看&#xff0c;在线教学&#xff0c;在线知识交流&#xff0…

虚幻引擎中CityEngine导入资产的自动化替换

在这篇博文中&#xff0c;我们将学习将 ArcGIS CityEngine Datasmith 场景导入虚幻引擎后替换资产的流程。 替换工作流由虚幻引擎的数据资产&#xff08;可以认为是简单的表格数据&#xff09;控制。 一旦设置正确&#xff0c;这些数据资产就可以在项目之间共享。 推荐&#x…

This old-style function definition is not preceded by a prototype

文章目录Introwarning 及解决 截屏知其然&#xff0c;却不知其所以然Intro 在 Xcode 14 中随意写了几个C命令行程序&#xff0c;编译运行OK。 但是有以下两种报错&#xff1a; This old-style function definition is not preceded by a prototype This function declaration…

联诚发携手电影《流浪地球2》,让电影特效和场景全面升级!

一万五千年前&#xff0c;一根愈合的人类股骨&#xff0c;标志着人类文明的诞生&#xff1b;一万五千年后&#xff0c;当太阳系将不复存在&#xff0c;人类的团结与勇气将延续文明的火种&#xff01;新年第一部精彩绝伦的国产科幻大片&#xff0c;大家期待已久的《流浪地球2》终…

动态规划的优化

动态规划的优化 一、空间优化 说明 动态规划空间优化为滚动数组优化&#xff0c;即对于一个多维数组&#xff0c;转移时均是由上一阶段转移来的&#xff0c;则可以将这一维省略&#xff0c;以降低空间复杂度&#xff0c;但要注意转移时的顺序&#xff1b; 例题 0 - 1 背包…

基础算法--背包问题(01背包问题、完全背包问题、多重背包问题、分组背包问题)

文章目录前言01背包问题完全背包问题多重背包问题分组背包问题前言 背包问题&#xff1a;给我们 i 件物品&#xff0c;每件物品都有体积 vi 和权重 wi &#xff0c;给我们限制条件&#xff0c;让我们选择在背包的容量内&#xff0c;物品达到权重最大 01背包问题 01背包问题描…

KK集团再冲刺港交所上市:期内被罚款30万元,曾存在“二清”问题

时隔一年&#xff0c;KK集团再次在港交所递交上市申请。2023年1月20日&#xff0c;KK集团&#xff08;KK Group Company Holdings Limited&#xff09;向港交所提交上市申请。据贝多财经了解&#xff0c;KK集团曾于2021年11月4日在港交所递表&#xff0c;后已“失效”。 相较于…

LeetCode_单周赛_329

2544. 交替数字和 代码1 转成字符串&#xff0c;逐个判断 class Solution {public int alternateDigitSum(int n) {char[] s ("" n).toCharArray();int t 1;int ans 0;for (int i 0; i < s.length; i) {ans (s[i] - 0) * t;t -t;}return ans;} }代码2 一…

国内做SaaS软件的知名企业有哪些?

SaaS厂商还挺多挺杂的。具体要列举的话&#xff0c;还是按照分类来吧。 通用业务和垂直行业的SaaS 对使用方来说&#xff0c;一般分为业务通用型和垂直行业型。 通用型是可以服务所有企业的&#xff0c;比如CRM&#xff08;客户管理软件&#xff09;、HR软件、协同办公软件&a…

linux系统中利用QT实现语音识别项目的操作方法

大家好&#xff0c;今天主要和大家分享一下&#xff0c;如何使用linux系统上的语音识别项目的操作方法与实现。 目录 第一&#xff1a;语音识别基本简介 第二&#xff1a;语音识别产品申请账号 第三&#xff1a;具体代码实现 第一&#xff1a;语音识别基本简介 AI音箱对传统…

Java_Git:3. 远程仓库

目录 1 添加远程库 1.1 在github上创建仓库 1.2 ssh协议 1.2.1 什么是ssh? 1.2.2 基于密匙的安全验证 1.2.3 ssh密钥生成 ​​​​​​​​​​​​​​1.2.4 ssh密钥配置 1.3 同步到远程仓库 1.3.1 使用git bash 1.3.2 使用TortoiseGit同步 2 从远程仓库克隆 2.1 …

CSS颜色:RGB颜色/HEX颜色/HSL颜色(网页颜色完全总结)

目录 CSS 颜色名 CSS 背景色 实例 CSS 文本颜色 ​编辑 实例 CSS 边框颜色 实例 CSS 颜色值 实例 RGB 值 rgb(red, green, blue) 实例 实例 RGBA 值 rgba(red, green, blue, alpha) 实例 HEX 值 #rrggbb 实例 实例 HSL 值 hsla(hue, saturation, lightn…

autojs模仿QQ长按弹窗菜单

牙叔教程 简单易懂 分析弹框菜单 圆角列表, 类似grid箭头位于文字中间上(下)方需求分析 如果要写一个这样的教程, 我们需要做什么 写一个列表, 用来触发长按选项写一个弹窗菜单代码分析 列表怎么写, 先来一个最简单的布局代码 "nodejs ui"; require("rhin…

系统架构:分层架构

引子 系统在从0到1阶段时&#xff0c;为了可让产品快速上线&#xff0c;此时系统分层一般不是软件开发需要重点考虑的范畴&#xff0c;但是随着业务逐渐复杂 &#xff0c;大量代码纠缠耦合&#xff0c;此时会出现逻辑不清楚、模块相互依赖、扩展性差、改一处动全身的问题。 系…

基于双层共识控制的直流微电网优化调度(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f389;作者研究&#xff1a;&#x1f3c5;&#x1f3c5;&#x1f3c5;本科计算机专业&#xff0c;研究生电气学硕…

14. python运算符

Python 语言支持以下类型的运算符 1. 算术运算符 、-、*、/、%、**、// **  返回x的y次幂 //  取整除 - 向下取接近商的整数(//得到的并不一定是整数类型的数&#xff0c;它与分母分子的数据类型有关系) print(7//2) print(7.0//2) print(7//2.0)2. 比较&#xff08;关系&…

进程概念——Linux

“技术是时间积淀出来的&#xff0c;你能速成的东西&#xff0c;别人也可以速成,所以需要耐心学习” 猛戳订阅&#x1f341;&#x1f341; &#x1f449;Linux操作系统详解&#x1f448; &#x1f341;&#x1f341; 这里是目录标题一、冯诺依曼结构为什么要存在内存&#xff1…

关系数据库-1-[mysql8]中的数据类型

详细介绍MySQL中的数据类型 1 MySQL中的数据类型 常见数据类型的属性&#xff0c;如下&#xff1a; 1.1 整数类型 一、类型介绍 整数类型一共有5种&#xff0c;包括TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和 BIGINT。 二、可选属性 1、M:表示显示宽度 Q&#xff1a;…

【2022】年度总结——彼此当年少 莫负好时光

文章目录【2022】年度总结前言&#x1f388;&#x1f388;&#x1f388;2022的得与失&#x1f9e8;&#x1f9e8;&#x1f9e8;对2023的期望&#x1f4e7;&#x1f4e7;&#x1f4e7;写在最后的话✍&#x1f3fb;✍&#x1f3fb;✍&#x1f3fb;【2022】年度总结 前言&#x1f…

client-go实战之七:准备一个工程管理后续实战的代码

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 系列文章链接 client-go实战之一&#xff1a;准备工作client-go实战之二:RESTClientclient-go实战之三&#xff1a;Clientsetclient-go实战之四&#xff1a;…