vue3运行时核心模块之runtime-dom

【源码分析】@vue/runtime-dom/src/apiCustomElement.ts 解析 源码来源于其他参考这段代码定义了一个可导出的泛型类型,用于描述能创建VueElement实例的构造函数类型,该构造函数可接收可选的任意属性对象作为初始参数,返回的实例同时具备VueElement类型和泛型参数P(默认空对象)所指定的类型特性,主要作用是在TypeScript中约束和描述Vue元素构造函数的参数和返回值类型,确保类型安全。这段代码定义了一个可导出的接口,用于规范自定义元素(通常是Vue自定义元素)的配置选项结构,包含可选的样式数组(styles)、是否启用影子DOM(shadowRoot。 阅读详情

runtime-dom是Vue 3 源码中一个核心的运行时包,主要作用是为Vue应用提供与浏览器DOM相关的具体实现,包括节点元素操作,监听事件绑定,属性更新等与DOM元素相关的一系列方法。
简单来说:runtime-dom 是 Vue 的“DOM 操作适配层”

一、为什么需要 runtime-dom

Vue 是与平台无关的框架,这也意味着同一套核心逻辑,可以运行在常见的有浏览器(DOM)、Nuxt.js(SSR)、Weex(Native APP)等环境中。
在源码中所在的位置:
在这里插入图片描述

二、runtime-dom的核心功能

1.DOM操作之nodeOps

// 导出 nodeOps 对象,它实现了 Renderer(渲染器)所需的平台相关 DOM 操作方法
// 使用 Omit 排除了 'patchProp' 方法(该方法由其他模块单独实现)
export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
  // 插入子节点到父节点中,anchor 为插入位置的参考节点(插入到 anchor 之前)
  insert: (child, parent, anchor) => {
    parent.insertBefore(child, anchor || null)
  },

  // 从 DOM 中移除指定子节点
  remove: child => {
    const parent = child.parentNode
    if (parent) {
      parent.removeChild(child)
    }
  },

  // 创建元素节点
  // 参数说明:
  // - tag: 元素标签名
  // - namespace: 命名空间(如 'svg'、'mathml' 或 undefined)
  // - is: 自定义元素的 is 属性(用于 Web Components)
  // - props: 初始化属性(目前仅用于 select 的 multiple 属性)
  createElement: (tag, namespace, is, props): Element => {
    let el: Element

    // 根据命名空间使用不同的 createElementNS 或 createElement
    if (namespace === 'svg') {
      el = doc.createElementNS(svgNS, tag) // SVG 命名空间
    } else if (namespace === 'mathml') {
      el = doc.createElementNS(mathmlNS, tag) // MathML 命名空间
    } else if (is) {
      el = doc.createElement(tag, { is }) // 自定义元素(Web Components)
    } else {
      el = doc.createElement(tag) // 普通 HTML 元素
    }

    // 特殊处理 <select multiple>:某些浏览器需要显式设置 multiple 属性
    if (tag === 'select' && props && props.multiple != null) {
      (el as HTMLSelectElement).setAttribute('multiple', props.multiple)
    }

    return el
  },

  // 创建文本节点
  createText: text => doc.createTextNode(text),

  // 创建注释节点
  createComment: text => doc.createComment(text),

  // 设置文本节点的内容(nodeValue)
  setText: (node, text) => {
    node.nodeValue = text
  },

  // 设置元素的文本内容(textContent)
  setElementText: (el, text) => {
    el.textContent = text
  },

  // 获取节点的父节点(类型断言为 Element | null)
  parentNode: node => node.parentNode as Element | null,

  // 获取节点的下一个兄弟节点
  nextSibling: node => node.nextSibling,

  // 查询选择器(document.querySelector)
  querySelector: selector => doc.querySelector(selector),

  // 设置作用域 ID(用于 scoped CSS)
  // 实际上是给元素添加一个空属性,例如 <div data-v-xxx="">
  setScopeId(el, id) {
    el.setAttribute(id, '')
  },

  // __UNSAFE__(不安全操作)
  // 原因:使用了 innerHTML。
  // 安全前提:静态内容仅来自编译后的模板(用户只使用可信模板时是安全的)
  // 功能:批量插入静态 HTML 内容(用于优化静态节点的渲染性能)
  insertStaticContent(content, parent, anchor, namespace, start, end) {
    // 记录插入前的最后一个子节点(用于后续返回插入范围)
    const before = anchor ? anchor.previousSibling : parent.lastChild

    // 如果提供了缓存的起始/结束节点(start/end),且结构有效,则复用克隆方式插入(性能优化)
    if (start && (start === end || start.nextSibling)) {
      // 缓存路径:克隆已存在的 DOM 节点
      while (true) {
        parent.insertBefore(start!.cloneNode(true), anchor)
        if (start === end || !(start = start!.nextSibling)) break
      }
    } else {
      // 非缓存路径:通过 innerHTML 解析新内容
      // 根据命名空间包裹内容(确保正确解析 SVG/MathML)
      templateContainer.innerHTML = unsafeToTrustedHTML(
        namespace === 'svg'
          ? `<svg>${content}</svg>`
          : namespace === 'mathml'
            ? `<math>${content}</math>`
            : content,
      ) as string

      const template = templateContainer.content // DocumentFragment

      // 如果是 SVG 或 MathML,移除外层包裹标签(因为 innerHTML 需要根元素)
      if (namespace === 'svg' || namespace === 'mathml') {
        const wrapper = template.firstChild!
        while (wrapper.firstChild) {
          template.appendChild(wrapper.firstChild) // 提取内部节点
        }
        template.removeChild(wrapper) // 移除 <svg> 或 <math> 包裹
      }

      // 将解析好的 DocumentFragment 插入到目标位置
      parent.insertBefore(template, anchor)
    }

    // 返回插入内容的第一个和最后一个节点,供后续引用或缓存
    return [
      // 第一个插入的节点
      before ? before.nextSibling! : parent.firstChild!,
      // 最后一个插入的节点
      anchor ? anchor.previousSibling! : parent.lastChild!,
    ]
  },
}

2.属性操作之patchProp

/**
 * 判断属性名是否为事件处理器(以 on 开头且后接大写字母)
 * @param key 要检测的属性名
 * @returns 布尔值:是否为事件属性
 */
export const isOn = (key) => {
  // 核心逻辑:
  // 1. 字符串长度至少为 3(on + 至少一个大写字母)
  // 2. 前两个字符是 'on'
  // 3. 第三个字符是大写字母(A-Z)
  return key.length > 2 && key[0] === 'o' && key[1] === 'n' && /[A-Z]/.test(key[2])
}
export const patchClass = (el,value)=>{
    if(value == null){
        value = '';
    }
    el.className = value;// 设置样式名
}
export const patchStyle = (el, prev, next) => {
    const style = el.style;
    if (!next) {
        el.removeAttribute('style');
    } else {

        for (const key in next) {
            style[key] = next[key];
        }
        if (prev) {
            for (const key in prev) {
                if (next[key] == null) {
                    style[key] = '';
                }
            }
        }
    }
}
export const patchEvent = (el, rawName, nextValue) => {
    const invokers = el._vei || (el._vei = {});
    const exisitingInvoker = invokers[rawName];
    if (nextValue && exisitingInvoker) { // 如果绑定过,则替换为新的
        exisitingInvoker.value = nextValue;
    } else {
        const name = rawName.slice(2).toLowerCase();
        if (nextValue) {  // 绑定新值
            const invoker = (invokers[rawName] = createInvoker(nextValue));
            el.addEventListener(name, invoker);
        } else if (exisitingInvoker) {
            el.removeEventListener(name, exisitingInvoker);
            invokers[rawName] = undefined;
        }
    }
}
function createInvoker(initialValue) {
    const invoker = (e) => {
        invoker.value(e);
    }
    invoker.value = initialValue;
    return invoker;
}
export const patchAttr = (el, key, value) => {
    if (value == null) {
        el.removeAttribute(key);
    } else {
        el.setAttribute(key, value);
    }
}
function patchProp(el, key, preValue, nextValue) {
    switch (key) {
        case 'class':
                patchClass(el, nextValue);
            break;
        case 'style':
            patchStyle(el, preValue, nextValue);
            break;
        default:
            if (isOn(key)) { // 事件如onClick
                patchEvent(el, key, nextValue);
            } else {
                patchAttr(el, key, nextValue);
            }
            break;
    }
}

以上方法会被传给 runtime-core,由核心模块调用,runtime-core 使用这套接口操作对应平台环境,而无需知道底层是 DOM 还是其他。对于runtime-core,后续会再单独整理成对应文章。

三、总结

runtime-dom是Vue 3 在浏览器环境中的 DOM 操作实现层,提供 createElement, patchProp, insert 等 DOM 操作。目的是解耦核心逻辑与平台细节,支持多端渲染,通过与runtime-core结合使用,把虚拟 DOM 变成真实页面,这也是 runtime-dom主要职责之一。今天简单总结了 runtime-dom的部分核心实现,如果有问题的小伙伴欢迎在评论区沟通交流!

【王道训练营 C/C++方向基础 60 题(31-40)】 王道训练营 C/C++方向基础 60 题(31-40) 阅读详情

相关推荐

Vivado 2018.3 SDK烧写bin文件避坑指南:如何解决QSPI模式下的常见错误

本文深入解析了在Vivado 2018.3 SDK中为Zynq FPGA烧写QSPI Flash时遇到的常见问题,特别是启动模式为QSPI时出现的警告与失败。文章从Zynq启动机制入手,提供了修改FSBL代码以强制使用JTAG模式、调整硬件配置等核心解决方案,并分享了实战案例与最佳实践,帮助开发者高效避坑,确保bin文件烧写成功。

arduino9maker的博客 389

Vue3源码学习之路-实现runtime-dom

Vue3源码学习之路-实现runtime-dom

凉秋秋秋 1928

Visual Tracker Benchamark傻瓜式教程(二)

问题  1 1  在调用main.m跑TRE和SRE时,因为会生成相应的.mat数据文件,如果跑完以后发现自己算法生成的.mat文件有问题,那么要把该文件删除,待找到错误原因后,重新生成相应的.mat文件。因为程序中写的是如果已经存在了.mat文件,例如已经存在car4_STC.mat文件,即使该文件中的数据有错误,但是重新跑时会默认该文件中的数据写完,不会重新写入数据 问题  2  b

mingbujingren的专栏 9928

Vue2.0教程(二) vue指令学习

vue的基本使用 1)导入vue.js的script脚本文件 可以前往 https://staticfile.org/ 下载vue.js或者引入在线cdn 也可以前往 https://www.bootcdn.cn/ 下载vue.js或者引入在线cdn 2)在页面中声明一个将要被vue所控制的DOM区域 3)创建vm实例对象(vue实例对象) <body> <!--2.在页面中声明一个将要被vue所控制的DOM区域--> <div id="test">hel

qq_43682422的博客 2544

论文阅读------Exposure Fusion: A Simple and Practical Alternative to High Dynamic Range Photography

Mertens T, Kautz J, Van Reeth F. Exposure fusion: A simple and practical alternative to high dynamic range photography[C]//Computer graphics forum. Oxford, UK: Blackwell Publishing Ltd, 2009, 28(1): 161-171. 摘要 本文提出了一种将包围曝光的序列融合到高质量图像中的技术,而无需先转换为高动态范围(HD.

qq_25283239的博客 3397

Windows本地化部署Dify完整指南

本文详细介绍了在Windows系统上本地化部署Dify的完整步骤,涵盖了Docker Desktop的安装与配置、Dify源代码的获取、环境变量的设置、服务的启动与管理、以及常见问题的解决方案。文章首先指导用户如何下载和安装Docker Desktop,并配置WSL 2功能。接着,详细说明了如何获取Dify源代码,并通过Docker Compose启动服务。文章还提供了高级配置指南,包括如何配置大语言模型和更新Dify版本。最后,针对Docker镜像拉取失败等常见问题,提供了详细的诊断和解决方案。通过本文,

jacky_zhuyuanlu的博客 1万+

【学习笔记】Vue3源码解析:第四部分- runtime-dom(1)

第四部分-:(对应课程的第24-26节)

youyoufenglai的博客 1520

【抖音】a_bogus参数逆向分析

点击——展开xxx条回复﹀就会出现。可以看到这个栈是有请求相关的数据的。所以就是在bdms.js里面生成的。

qq_44990881的博客 3000

Vue3中runtime-dom的实现-详细步骤

runtime-dom本质上没做什么,它只是封装了一些平台对应的代码,它封装好以后,内部去调用创建渲染器去进行一个渲染,那具体怎么渲染是内部去做的,内部runtime-core去根据你传进来的一些属性操作、事件操作和dom操作等等方法,把传进来元素生成的vnode虚拟节点,然后放进对应的容器中就可以了。所以runtime-dom跟平台有关,而runtime-core跟平台无关。...

老司机的后备箱 1454

【Altium designer】如何添加过孔阵列

按下图1指示,执行【Tool】菜单栏下【Via Stitching/Shielding】选项卡中的【Add Stitching to Net...】选项 图1 “添加过孔阵列”选项 点击【Add Stitching to Net...】选项后,会跳出一个窗口,让我们设置过孔阵列的参数信息,如下图2所示。 图2 过孔阵列参数设置 过孔阵列区域:勾选该选项框,即可绘制多边形区域; 过孔内径与焊盘直径:比如内径 【Hole Size】设为12mil,焊盘直径【Diameter】设为24mil; .

夜半少年的博客 2万+

使用mid360运行lio-livox对比fast-lio2

msg_type为激光雷达发出的数据格式,有livox自定义数据类型格式CustomMsg和ros标准的PointCloud2格式,因此首先需要确定激光雷达端配置的是哪种格式。可以从激光雷达的ros驱动中看到驱动端有三种格式可以选,驱动端的数字2对应lio-livox中的1,驱动端的0对应lio-livox的格式0。如果使用的是Ubuntu18.04编译完成后运行直接成功,如果使用20.04运行会报错,需要修改以下源代码。针对livox-mid360 运行lio-livox的一些问题。

z1872385的博客 1230

vue3.0源码分析--vue-next-master目录结构

文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例可供参考 一、pandas是什么? 示例:pandas 是基于NumPy 的一种

weixin_50077637的博客 7915

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Vue 3.0 源码导读

这个模块是基于上面模块而写的浏览器上的 runtime,主要功能是适配了浏览器环境下节点和节点属性的增删改查。很明显,这个模块就是 Composition API 的核心了,其中的 ref 和 reactive 应该重点掌握。这个模块则基于上个模块,针对浏览器做了适配,如对 textarea 和 style 标签做了特殊处理。大部分 Vue 开发者应该不会用到这个模块,因为它是专门用于自定义 renderer 的。代码仓库中有个 packages 目录,里面是 Vue 3 的主要功能的实现,包括。

On the way 436

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Vue3 源码目录结构与构建流程 | monorepo 结构、packages 目录划分、构建脚本解析

本文深入解析了Vue3的源码目录结构与构建流程。Vue3采用Monorepo架构,将核心功能拆分为多个独立包,包括响应式系统(reactivity)、平台无关运行时(runtime-core)、DOM运行时(runtime-dom)、编译器(compiler-core/dom)等。这些模块通过shared包共享工具函数,最终由vue包整合为完整框架。构建流程使用Rollup打包,包含开发和生产模式配置。这种架构设计提升了代码复用性和可维护性,为深入理解Vue3源码奠定了基础。

寒露丶小白 421

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

【Vue3源码学习】— CH3 探索核心运行时: runtime-core

Vue 3 的 runtime-core 包含了 Vue 的核心运行时逻辑,它负责组件的创建、更新、渲染等核心功能。runtime-core 设计的目的是为了让 Vue 更加轻量,并且能够被不同的平台(如 Web、小程序等)复用。以下是一些 runtime-core 的关键概念和组成部分:虚拟 DOM 是对真实 DOM 的抽象表示,它允许 Vue 在内存中以 JavaScript 对象的形式描述 UI 结构,然后高效地计算出最小的更新操作来实现视图的更新。这减少了直接操作 DOM 的次数,提高了性能。Vue

YSG的博客 892

Vue3 核心模块源码解析

Vue3 模块源码解析 diff算法优化

yolo 3718

Vue3源码梳理:关于运行时和编译时

上述 v-if 和 m-com 自定义组件不是正常的html, 浏览器是不认识的,所以不会解析。.vue文件是单文件组件, 在template中写入的html是真实的html标记节点么?vue中的编译时,准确来说是编译器的意思,核心代码在 compiler-core模块下。运行时做不到的事情,通过html标签结构的方式来渲染解析成真实的dom节点。初次渲染,会生成一个ul标签,同时生成三个li标签,并把他们挂载到div中。这个过程主要是:让假标记,被渲染成真实的html标签,最终呈现给用户。

Wang的专栏 990

Vue3核心运行时之runtime-core

本文分析了Vue3运行时核心@vue/runtime-core的实现机制。主要内容包括:1) runtime-core作为平台无关的VNode调度器和组件生命周期管理器的作用;2) 从createApp到render的初始化流程,包括创建渲染器、应用实例和首次渲染;3) patch函数实现虚拟DOM的挂载和更新;4) 元素节点和组件的处理过程;5) 组件自动更新的简化实现,通过setupRenderEffect建立响应式更新机制。runtime-core通过调用runtime-dom提供的接口操作DOM,管

Zhencode的博客 507

Vue3源码runtime-core运行时核心模块之scheduler调度器

Vue3调度器scheduler负责管理异步任务队列和执行顺序,核心实现位于runtime-core/scheduler.ts。它通过queueJob管理组件更新任务,queuePostFlushCb处理DOM更新后的回调,使用微任务确保异步执行。flushJobs和flushPostFlushCbs分别处理主任务队列和后置回调队列,通过currentFlushPromise避免重复刷新。watchEffect默认使用主队列,而watchPostEffect则通过queuePostFlushCb延迟到DOM

Jinuss的博客 582

Vue3 核心模块源码解析(中)

【Vue3 核心模块源码解析(上)】讲到了 Vue2 与 Vue3的一些区别,Vue3 新特性的使用,以及略微带了一点源码。那么这篇文章就要从`Vue3 模块源码解析` 与 `Vue3 执行逻辑解析`这两个方面去给大家剖析 Vue3 的深层次,一起学习起来吧!

程邓楠的博客 2135

手写 vue 源码 === runtime-dom 实现

本文从实现原理和具体代码出发,手把手演示了如何在浏览器环境下搭建一个简化版的明确渲染器的职责,把虚拟节点渲染成真实 DOM。创建包结构,写好,引入必要依赖。实现nodeOps,把所有常用的 DOM 操作封装成一组方法。实现patchProp,并拆分出patchClasspatchStylepatchEventpatchAttr等细节逻辑,确保能灵活更新元素的属性、样式、事件。组装渲染选项,把nodeOps和patchProp合并后传给,最终获得方法。通过以上步骤,你就能理解:在 Vue 3 体系下,

每天都要努力学习哦~~ 947

22_Vue3源码学习之真实DOM和虚拟DOM的渲染及Vue的核心模块

Vue3源码学习之真实DOM与虚拟DOM渲染及Vue的核心模块 真实的DOM渲染 我们传统的前端开发中,我们是编写自己的HTML,最终被渲染到浏览器上的,那么它是什么样的过程呢? 虚拟DOM的优势 目前框架都会引入虚拟DOM来对真实的DOM进行抽象,这样做有很多的好处: 首先是可以对真实的元素节点进行抽象,抽象成VNode(虚拟节点),这样方便后续对其进行各种操作: 因为对于直接操作DOM来说是有很多的限制的,比如diff、clone等等,但是使用JavaScript编程语言来操作这些,就变得非常的简

酒客的博客 671

Vue3源码runtime-core运行时核心模块之directive自定义指令

Vue3自定义指令开发指南 本文介绍了Vue3中自定义指令的开发方法和实际应用。主要内容包括: 自定义指令的基本概念和生命周期钩子函数 防抖指令的具体实现方案,支持灵活配置时间间隔 指令的全局注册方法 使用注意事项和性能考量 从源码层面解析指令的注册和绑定过程 文章通过防抖指令的完整示例,展示了如何从定义、注册到使用自定义指令的全流程,并深入分析了Vue3指令系统的底层实现原理,帮助开发者更好地理解和应用自定义指令功能。

Jinuss的博客 732

Vue3.4源码runtime-dom,runtime-core相关(h函数,render函数,diff算法,插槽,props,emits)

runtime-dom用于浏览器渲染,基于runtime-core实现跨平台,而此包基于reactivity实现响应式处理。创建响应的packages包并安装给runtime-dom创建节点相关的配置文件nodeOpts创建属性相关的操作文件patchProp在createRenderer中使用,测试配置项是否可以正常使用然后创建render方法然后将createRenderer函数提取到runtime-core包中。

m0_62475892的博客 1143

【Vue3源码Runtime-core篇】 第一章 初识Runtime

当设计一个框架的时候,我们有三种选择:纯运行时的、运行时 + 编译时的或纯编译时的。我们先聊聊纯运行时的框架。

m0_68324632的博客 953

Vue运行时编译时

Vue运行时编译时

qq_55791710的博客 475
上一篇: 深入Vue3响应式核心:computed 的实现原理与应用
下一篇: Vue3核心运行时之runtime-core
Zhencode
博客等级 码龄7年 106粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值