【手写 Vue2.x 源码】第四十一篇 - 组件部分 - 生成组件的真实节点

news2024/9/22 11:31:45

一,前言

上篇,介绍了组件部分-组件的生命周期,主要涉及以下几部分:

本篇,组件部分-生成组件的真实节点;


二,生成组件的真实节点

1,前文回顾

前篇,在 createElement 方法中,扩展了对组件的处理 createComponent:生成组件的虚拟节点;

按照模板渲染流程,接下来会进入 patch 方法,其中的 createElm 方法:将虚拟节点转化成为真实节点;

// todo 后续需详细描述相关流程,对 patch 和 createElm 方法进行必要的介绍和说明;

2,createElm 方法

在 patch 方法中,createElm 方法会将虚拟节点生成为真实节点:
通过vnode.el = document.createElement(tag)直接创建出真实节点

export function createElm(vnode) {
  // vnode.el:绑定真实节点与虚拟节点的映射关系,便于后续的节点更新操作

  // 虚拟节点必备的三个:标签,数据,孩子
  let { tag, data, children, text, vm } = vnode;
  if (typeof tag === 'string') { // 处理元素节点
    // 创建真实节点
    vnode.el = document.createElement(tag) 
    updateProperties(vnode, data)  // 处理元素的 data 属性
    // 处理当前元素节点的儿子:递归创建儿子的真实节点,并添加到对应的父亲中
    children.forEach(child => { // 若不存在儿子,children为空数组
      vnode.el.appendChild(createElm(child))
    });
  } else { // 文本:文本中 tag 是 undefined
    vnode.el = document.createTextNode(text)  // 创建文本的真实节点
  }
  return vnode.el;
} 

现在由于组件的加入,createElm 方法中可能会存在 componentOptions:

打印createElm 查看:
第一次:真实节点:id=app
image.png
第二次:组件:my-button
image.png

添加对组件的处理

/**
 * 创造组件的真实节点
 * @param {*} vnode 
 */
function createComponent(vnode) {
  console.log(vnode) // my-button
}

export function createElm(vnode) {
  let { tag, data, children, text, vm } = vnode;
  if (typeof tag === 'string') {// 元素 or 组件
    // 添加对组件的处理
    if(createComponent(vnode)){ // 将组件的虚拟节点,创建成为组件的真实节点

    }
    // 创建真实节点
    vnode.el = document.createElement(tag) 
    updateProperties(vnode, data)
    children.forEach(child => {
      vnode.el.appendChild(createElm(child))
    });
  } else { // 文本
    vnode.el = document.createTextNode(text)
  }
  return vnode.el;
} 

3,组件的初始化 Hook

之前在组件的 data 属性上,扩展出了生命周期 hook;

在 createComponent 中获取 hook,如果有 hook 说明就是组件;

拿到 hook中的 init 方法,并使用 init 方法处理 vnode:

/**
 * 创造组件的真实节点
 * @param {*} vnode 
 */
function createComponent(vnode) {
  console.log(vnode);
  let i = vnode.data;
  // 先确定有 hook;再拿到 init 方法;
  if((i = i.hook)&&(i = i.init)){ // 最后 i 为 init 方法
    i(vnode); // 使用 init 方法处理 vnode
  }
}
备注:
先将 hook 赋值给 i:看是否有 hook,如果有 hook 就是组件;
再将 hook 中的 init 方法赋值给 i;
最终 i 就是 hook 上的init 方法;

使用 hook 上的init 方法处理 vnode,在 hook 中进行中组件的初始化:

/**
 * 创造组件的虚拟节点 componentVnode
 */
function createComponent(vm, tag, data, children, key, Ctor) {

  if(isObject(Ctor)){
    Ctor = vm.$options._base.extend(Ctor)
  }

  data.hook = {
    init(){
      // 创建组件的实例并挂载
      let child = new Ctor({});
      child.$mount();
    },
    prepatch(){},
    postpatch(){}
  }
  
  let componentVnode = vnode(vm, tag, data, undefined, key, undefined, {Ctor, children, tag});
  return componentVnode;
}

在 new Ctor 时,会执行 _init 进行组件的初始化:
// 调用子类的初始化 _init 方法

  Vue.prototype._init = function (options) {
    const vm = this;
    vm.$options = mergeOptions(vm.constructor.options, options);
    initState(vm);
    // 由于 el 不存在,所以不会执行 vm.$mount
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

通过child.$mount();进行挂在,但没有传参 el = null,所以不会挂载:

  Vue.prototype.$mount = function (el) {
    const vm = this;
    const opts = vm.$options;
    el = document.querySelector(el); // 获取真实的元素
    vm.$el = el;// $el:页面上的真实元素

    if (!opts.render) {
      let template = opts.template;
      if (!template) {
        template = el.outerHTML;
      }

      let render = compileToFunction(template);
      opts.render = render;
    }

    mountComponent(vm);
  }

如果组件的 render 函数不存在,使用组件的 template 编译为 render函数,并保存起来,之后 mountComponent 进行组件的挂载:

export function mountComponent(vm) {

  let updateComponent = ()=>{
    vm._update(vm._render());  
  }

  callHook(vm, 'beforeCreate');
  // 生成渲染 watcher :每个组件都具有独立的渲染 watcher
  new Watcher(vm, updateComponent, ()=>{
    callHook(vm, 'created');
  },true)
   callHook(vm, 'mounted');
}

updateComponent 会调用 _render 方法根据子组件创造虚拟节点:

  Vue.prototype._render = function () {
    const vm = this;
    let { render } = vm.$options;
    let vnode = render.call(vm);
    return vnode
  }

通过 render.call 产生虚拟节点,这个 vnode 就是模板的 button


三,结尾

本篇,介绍了组件部分-生成组件的真实节点;

下篇,组件部分 - 组件挂载流程简述;

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

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

相关文章

【剧前爆米花--爪哇岛寻宝】Java中有关异常类的详细讲解

作者:困了电视剧 专栏:《JavaSE语法与底层详解》 文章分布:这是一篇关于Java中异常类的文章,在本篇文章中详细讲解了异常的使用逻辑和底层的执行过程,如有疏漏,欢迎大佬指正! 目录 异常的体系结…

<C++>哈希

文章目录1. unordered系列容器1.1 unordered_map1.1.1 unordered_map的文档介绍1.1.2 unordered_map的接口说明1.2 unordered_set2. 哈希概念3. 哈希冲突4. 哈希函数5. 哈希冲突解决5.1 闭散列5.1.1 线性探测5.1.2 二次探测5.2 开散列5.3 开散列与闭散列比较6. 模拟实现1. unor…

配置野火霸道V2环境

配置野火霸道V2环境野火霸道开发板学习笔记信息说明下载安装Keil5配置Keil以使用DAP下载器DAP下载器的使用使用串口下载程序安装USB转串口驱动CH340检查是否安装成功配置MCUISP软件野火霸道开发板学习笔记 信息说明 日期 : 2023-01-23开发板: 野火霸道V2芯片型号: STM32F103Z…

[Paper Reading] Towards Conversational Recommendation over Multi-Type Dialogs

[Paper Reading] Towards Conversational Recommendation over Multi-Type Dialogs 文章目录[Paper Reading] Towards Conversational Recommendation over Multi-Type Dialogs论文简介快速回顾论文(借助scispace)梳理一下文章内容(参考百度N…

自动化将Gitee的仓库导入Github

自动化将Gitee的仓库导入Github准备工作获取方式gitee的授权码github授权码工具源码用法下载gitee所有仓库到本地下载并更新到github(自动创建仓库)写在最后本方法能实现自动创建仓库 脚本及用法放在文章最后了,需要的自取 转跳到结尾 准备工…

高性能 Java 框架。Solon v1.12.3 发布(春节前兮的最后更)

一个更现代感的 Java "生态型"应用开发框架:更快、更小、更自由。不是 Spring,没有 Servlet,也无关 JavaEE;新兴独立的开放生态 (已有150来个生态插件) 。主框架仅 0.1 MB。 相对于 Spring Boot…

计算正整数的阶乘math.factorial()

【小白从小学Python、C、Java】【计算机等级考试500强双证书】【Python-数据分析】计算正整数的阶乘math.factorial()[太阳]选择题请问math.factorial(3)的输出结果是?import mathprint("【执行】math.factorial(3):",math.factorial(3))print("【执行】math.f…

带你玩转Jetson之Deepstream简明教程(二)Deepstream是什么?干什么?有什么优势?

1.Deepstream是什么? Deepstream是Nvidia公司推出的一套基于开源视频流框架Gstreamer的一套库。其本身由多个.lib.so和.h构成,其支持语言包括了Python和Cpp两种主流语言。你可以在任何Python或者Cpp编译器、开发环境中引用库的API构建属于你自己的推理流…

【c++之于c的优化】

目录:前言关键字一、命名空间1.什么是命名空间2.如何使用命名空间3.如何自己创建命名空间4.为什么要使用命名空间5.命名空间起别名6.匿名命名空间二、缺省参数定义缺省参数类型注意事项三、函数重载定义函数重载的三种方式操作系统的区分方式四、引用定义引用特性使…

【4-网络八股扩展】北京大学TensorFlow2.0

课程地址:【北京大学】Tensorflow2.0_哔哩哔哩_bilibiliPython3.7和TensorFlow2.1六讲:神经网络计算:神经网络的计算过程,搭建第一个神经网络模型神经网络优化:神经网络的优化方法,掌握学习率、激活函数、损…

【LeetCode每日一题】【2023/1/24】1828. 统计一个圆中点的数目

文章目录1828. 统计一个圆中点的数目方法1:枚举1828. 统计一个圆中点的数目 LeetCode: 1828. 统计一个圆中点的数目 中等\color{#FFB800}{中等}中等 给你一个数组 points ,其中 points[i] [x_i, y_i] ,表示第 i 个点在二维平面上的坐标。多…

【算法面试】队列算法笔试面试全解(金三银四面试专栏启动)

📫作者简介:小明java问道之路,专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

02_gpio子系统

总结 驱动程序还想控制gpio 可以不用读写寄存器 直觉用gpio子系统开发的接口就能用了 轻松做输入输出 获取当前值 详细介绍 用设备树里的节点 gpio1 介绍 imx6ull.dtsi gpio1 记录了控制器相关的寄存器基地址 gpio1: gpio209c000 {compatible "fsl,imx6ul-gpio"…

三、利用迁移学习进行模型微调(Datawhale组队学习)

文章目录安装配置环境准备图像分类数据集迁移学习微调训练图像分类模型导入环境图像预处理载入图像分类数据集建立类别和索引号之间映射关系定义数据加载器查看一个batch的图像和标注可视化一个batch的图像和标注模型的构建与测试可视化常见的迁移学习训练方式训练配置模型训练…

过完2022,依然记得仰望星空

🕺作者:一名普普通通的双非大二学生迷茫的启明星🎃专栏:《数据库》《C语言从0到1专栏》《数据结构》《C语言杂谈》目录 ​编辑 一.2022之初 二.2022年中 三.2022年末 四.展望2023 一.2022之初 想起一年前这个时候&#xff0c…

07_plantform平台总线

总结 /sys/bus/plantform 平台总线其实就是继承 06_自己创建xbus总线 有了更多的玩法 和自己创建的xbus总线一样 平台总线也有dev和drv 需要这两个进行匹配之后 进行porbe调用 plantform_device 结构体中直觉继承了 struc device lantform_driver 继承了driver 详细介绍 plan…

树(基础部分)

章节目录:一、二叉树1.1 为什么要使用树?1.2 树的常用术语1.3 二叉树概念1.4 二叉树应用二、顺序存储二叉树2.1 概述2.2 基本应用三、线索化二叉树3.1 问题引出3.2 概述3.3 基本应用四、结束语一、二叉树 1.1 为什么要使用树? 数组存储方式&…

MP-2平面烟雾气体传感器介绍

MP-2平面烟雾气体传感器简介MP-2烟雾检测气体传感器采用多层厚膜制造工艺,在微型Al2O3陶瓷基片的两面分别制作加热器和金属氧化物半导体气敏层,封装在金属壳体内。当环境空气中有被检测气体存在时传感器电导率发生变化,该气体的浓度越高&…

【数据库概论】3.1 SQL简述、数据定义和索引

第三章 关系数据库标准语言SQL 目录第三章 关系数据库标准语言SQL3.1 SQL概述3.1.1 产生与发展3.1.2 SQL的特点3.1.3 SQL的基本概念3.2 数据库实例3.3 数据定义3.3.1 模式的定义和删除3.2.2基本表的定义、删除和修改1.常见数据类型2.定义基本表3.修改基本表4.删除基本表5.模式和…

英语学习打卡day3

2023.1.22 1.mariner n.水手 2.formation n.队形;组成;形成 n.形状;形式样式;表格 the formation of landscapes Keep the formation 保持队形 The chairs were arranged in the form of circle. fill in the form 填写表格 formal adj.正式的inform 通知deform 变形uniform 统…