请你谈谈:vue的渲染机制(render)- 1 原理讲解

Vue 是如何将一份模板转换为真实的 DOM 节点的,又是如何高效地更新这些节点的呢?我们接下来就将尝试通过深入研究 Vue 的内部渲染机制来解释这些问题。

1 虚拟 DOM

<template><div id="app">this is son component</div>
</template>

你可能已经听说过“虚拟 DOM”的概念了,Vue 的渲染系统正是基于这个概念构建的。

虚拟 DOM (Virtual DOM,简称 VDOM) 是一种编程概念,虚拟 DOM 是一种编程模式而非一种具体的技术实现,它允许开发者以声明式的方式描述他们的 UI 界面,而无需直接操作真实的 DOM 结构。这种方式带来了显著的优点:

虚拟 DOM 通过在内存中操作数据结构来模拟 DOM 的变化,只有在必要时才将变更反映到真实的 DOM 上。这种方式通过减少直接对 DOM 的操作,可以显著提高应用的性能,因为 DOM 操作是昂贵的,特别是在大型应用中。

Vue 的实现方式与 React 有所不同,但同样采用了虚拟 DOM 的概念。Vue 的虚拟 DOM 实现被称为 VNode(Virtual Node),它是 Vue 组件树的一个内存表示。当 Vue 组件的数据变化时,Vue 会使用其高效的算法(如 patch 算法)来比较新旧 VNode 树,并只更新实际 DOM 中需要改变的部分。

Vue 的这种设计方式不仅提高了性能,还使得 Vue 的学习曲线相对平缓,因为它允许开发者使用更加接近原生 JavaScript 的语法来编写应用,而不是像 React 那样需要学习 JSX 或其他模板语言。

const vnode = {type: 'div',props: {id: 'hello'},children: [/* 更多 vnode */]
}

在前端开发中,虚拟DOM(Virtual DOM)树是一种在内存中表示DOM树的数据结构,用于优化真实DOM的更新过程。您提供的vnode对象(即一个纯 JavaScript 的对象 (一个“虚拟节点”))示例是一个典型的虚拟DOM节点(VNode),它代表了DOM树中的一个元素节点。下面是对这个vnode对象以及它所代表的虚拟DOM树的一个详细讲解。

虚拟DOM节点(VNode)

  • type: 表示节点的类型,通常是HTML标签名(如'div''span'等)或组件类型。在您的示例中,type: 'div'表示这是一个<div>元素。

  • props: 包含了节点的属性(如idclassstyle等)。在您的示例中,props: { id: 'hello' }表示这个<div>元素有一个id属性,其值为'hello'

  • children: 表示该节点的子节点列表。这个列表可以包含更多的VNode对象,从而构建出完整的DOM树结构。在您的示例中,children数组被注释掉了,但通常它会包含零个、一个或多个VNode对象,这些对象可能是元素节点、文本节点或组件节点。

不过,重要的是要理解虚拟 DOM 的核心概念和它的工作流程:

  1. 创建(Creation):首先,开发者使用 JavaScript 对象(或 TypeScript 类型)来声明虚拟 DOM 树。这些对象包含了足够的信息来创建对应的真实 DOM 元素,如元素类型(type)、属性(props)、子节点(children)等。

  2. 挂载(Mounting):当应用首次加载时,框架的渲染器会遍历这个虚拟 DOM 树,并根据其中的信息构建出真实的 DOM 树,然后将其挂载到页面的某个元素上(通常是 body 或其他指定的容器元素)。

  3. 更新(Updating/Patching):当应用的状态变化时,框架会重新计算并生成一个新的虚拟 DOM 树。然后,渲染器会比较新旧两棵虚拟 DOM 树之间的差异(这通常是通过高效的算法实现的,如 React 的 Diff 算法或 Vue 的 Patch 算法)。一旦确定了差异,渲染器就会只更新真实 DOM 中必要的部分,以反映这些变化。

虚拟 DOM 的主要优势之一是它允许开发者以声明式的方式编写 UI,同时框架负责优化 DOM 操作。由于 DOM 操作通常比 JavaScript 计算要慢得多,因此减少不必要的 DOM 操作可以显著提高应用的性能。

2 渲染管线

从高层面的视角看,Vue 组件挂载时会发生如下几件事:
在这里插入图片描述

编译

Vue 模板被编译为渲染函数:即用来返回虚拟 DOM 树的函数。这一步骤可以通过构建步骤提前完成,也可以通过使用运行时编译器即时完成。

  • 构建时编译:在 Vue 项目中,通常会使用构建工具(如 Webpack)配合 Vue Loader 来处理 .vue 文件。在这个过程中,Vue Loader 会将模板部分编译成渲染函数(Render Function),这个过程是构建时完成的,意味着在浏览器加载页面之前,模板已经被转换成了 JavaScript 代码。

  • 运行时编译:运行时编译(Runtime Compilation)在Vue.js的上下文中,指的是在浏览器端(客户端)直接将模板字符串编译成渲染函数的过程。这通常用于动态加载的组件或者在不使用构建工具的情况下。但是,出于性能考虑,推荐在生产环境中使用构建时编译。

    运行时编译的详细解释

    1. 模板字符串的存在:在Vue应用中,尤其是当使用Vue的CDN版本或通过<script type="text/x-template">标签直接在HTML中定义模板时,模板是以字符串的形式存在的。这些字符串需要被解析和转换成Vue能够理解的格式来渲染视图。

    2. Vue的运行时编译器:Vue.js提供了一个内置的运行时编译器,它能够在浏览器端解析这些模板字符串,并生成相应的渲染函数。这些渲染函数是高度优化的,它们描述了如何基于组件的响应式状态来生成和更新DOM。

挂载

当 Vue 实例被创建并挂载到 DOM 上时,Vue 会执行渲染函数来生成虚拟 DOM 树。然后,Vue 的运行时渲染器会遍历这棵虚拟 DOM 树,并基于它创建出真实的 DOM 节点,并将这些节点插入到指定的挂载点(通常是某个 HTML 元素)。

  • 响应式依赖追踪:在挂载过程中,Vue 的响应式系统会追踪渲染函数中用到的所有响应式数据。当这些数据发生变化时,Vue 能够知道需要重新渲染组件。

更新:当一个依赖发生变化后,副作用会重新运行,这时候会创建一个更新后的虚拟 DOM 树。运行时渲染器遍历这棵新树,将它与旧树进行比较,然后将必要的更新应用到真实 DOM 上去。

更新

当组件的响应式数据发生变化时,Vue 的响应式系统会触发组件的重新渲染。这个过程会再次执行渲染函数,生成一个更新后的虚拟 DOM 树。

  • 虚拟 DOM 比较:Vue 的运行时渲染器会比较新旧两棵虚拟 DOM 树,通过高效的算法(如 diff 算法)来找出它们之间的差异。

  • DOM 更新:基于比较结果,Vue 会最小化地更新真实的 DOM,以反映数据的最新状态。这通常意味着只有那些真正需要改变的 DOM 部分会被更新,从而提高性能。

总结

Vue 的组件挂载和更新过程是一个高度优化的过程,它利用虚拟 DOM 和响应式系统来确保数据的变化能够高效地反映到视图上。这个过程从模板编译开始,经过挂载、更新等阶段,最终实现了数据的双向绑定和视图的动态更新。

3 模板 vs. 渲染函数

Vue 模板会被预编译成虚拟 DOM 渲染函数。Vue 也提供了 API 使我们可以不使用模板编译,直接手写渲染函数。在处理高度动态的逻辑时,渲染函数相比于模板更加灵活,因为你可以完全地使用 JavaScript 来构造你想要的 vnode。

Vue.js 的核心之一是其响应式系统和虚拟 DOM 渲染器。虽然 Vue.js 鼓励开发者使用声明式的模板语法来构建用户界面,因为它更直观且易于理解,但在某些情况下,特别是当需要处理高度动态或复杂的逻辑时,直接使用渲染函数(Render Functions)或 JSX(如果通过 Babel 插件转换)可能会更加灵活和强大。

4 带编译时信息的虚拟 DOM

Vue.js 通过将编译器和运行时紧密耦合,并利用编译时信息来优化虚拟 DOM 的更新过程,从而在保持声明式写法和最终正确性的同时,提高了性能和效率。这种混合解决方案使得 Vue 在处理复杂应用时既强大又高效。当然,每种技术都有其优缺点,选择哪种技术取决于项目的具体需求、团队的熟悉程度以及开发者的个人偏好。

编译时优化

Vue.js 的一个关键优势在于它能够在编译时(即在代码被发送到浏览器之前)对模板进行静态分析。这种分析允许 Vue 的编译器识别出哪些部分是静态的(即不会随数据变化而变化的部分),哪些部分是动态的。对于静态部分,Vue 可以生成更高效的代码,因为它们不需要在每次渲染时都重新计算或重新创建。

带编译时信息的虚拟 DOM

Vue 通过在生成的渲染函数中嵌入编译时信息来进一步优化其运行时性能。这些信息可以帮助 Vue 的虚拟 DOM 算法更智能地处理更新,减少不必要的比较和重新渲染。例如,如果编译器能够确定某个 vnode 的子节点列表是静态的,那么它就可以在渲染函数中生成一个标记来指示这一点。在运行时,虚拟 DOM 算法就可以利用这个标记来跳过对这些子节点的比较,从而节省时间和内存。

保留底层渲染函数的能力

尽管 Vue 提供了这些编译时优化,但它仍然保留了用户直接使用底层渲染函数的能力。这意味着当模板语法不足以满足需求时,开发者可以回退到更灵活的渲染函数或 JSX。这种灵活性是 Vue 设计哲学的一个重要方面,它允许开发者根据项目的具体需求选择最合适的工具。

5 静态提升

在模板中常常有部分内容是不带任何动态绑定的:

<div><div>foo</div> <!-- 需提升 --><div>bar</div> <!-- 需提升 --><div>{{ dynamic }}</div>
</div>
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", null, "foo", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "bar", -1 /* HOISTED */)export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createElementBlock("div", null, [_hoisted_1,_hoisted_2,_createElementVNode("div", null, _toDisplayString(_ctx.dynamic), 1 /* TEXT */)]))
}// Check the console for the AST

foo 和 bar 这两个 div 是完全静态的,没有必要在重新渲染时再次创建和比对它们。Vue 编译器自动地会提升这部分 vnode 创建函数到这个模板的渲染函数之外,并在每次渲染时都使用这份相同的 vnode,渲染器知道新旧 vnode 在这部分是完全相同的,所以会完全跳过对它们的差异比对。

此外,当有足够多连续的静态元素时,它们还会再被压缩为一个“静态 vnode”,其中包含的是这些节点相应的纯 HTML 字符串。(示例)。这些静态节点会直接通过 innerHTML 来挂载。同时还会在初次挂载后缓存相应的 DOM 节点。如果这部分内容在应用中其他地方被重用,那么将会使用原生的 cloneNode() 方法来克隆新的 DOM 节点,这会非常高效。

6 更新类型标记

对于单个有动态绑定的元素来说,我们可以在编译时推断出大量信息:

<!-- 仅含 class 绑定 -->
<div :class="{ active }"></div><!-- 仅含 id 和 value 绑定 -->
<input :id="id" :value="value"><!-- 仅含文本子节点 -->
<div>{{ dynamic }}</div>
import { normalizeClass as _normalizeClass, createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createElementBlock(_Fragment, null, [_createElementVNode("div", {class: _normalizeClass({ active: _ctx.active })}, null, 2 /* CLASS */),_createElementVNode("input", {id: _ctx.id,value: _ctx.value}, null, 8 /* PROPS */, ["id", "value"]),_createElementVNode("div", null, _toDisplayString(_ctx.dynamic), 1 /* TEXT */)], 64 /* STABLE_FRAGMENT */))
}// Check the console for the AST

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

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

相关文章

《javaEE篇》--阻塞队列详解

阻塞队列 阻塞队列概述 阻塞队列也是一种队列&#xff0c;和普通队列一样遵循先进先出的原则&#xff0c;但是阻塞队列相较于普通队列多了两项功能阻塞添加和阻塞移除&#xff0c;使得阻塞队列成为一种线程安全的数据结构 阻塞添加&#xff1a;当队列满的时候继续入队就会阻…

UE4 UnrealPak加密功能(配置AES encrypt key)

本文的重点在于如何使用UnrealPak的加密功能&#xff0c;以及相关的UE4源代码学习。本文参考了&#xff1a;https://www.cnblogs.com/shiroe/p/14803859.html 。 设置密钥 在编辑、项目设置中找到下面栏目&#xff0c;并点击“生成新的加密密钥”&#xff0c;就可以为Unreal P…

unity2D游戏开发10生命条脚本

HitPoints 在ScriptableObjects文件夹中创建新的脚本,叫HitPoint using System.Collections; using System.Collections.Generic; using UnityEngine;//创建条目,方便轻松创建HitPoints的实例 [CreateAssetMenu(menuName ="HitPoints")] public class HitPoints :…

锅总介绍CNCF主要目标、全景图及发展历史

一、CNCF简介 云原生计算基金会&#xff08;Cloud Native Computing Foundation&#xff0c;简称 CNCF&#xff09;是一个成立于 2015 年的非营利性组织&#xff0c;隶属于 Linux 基金会。CNCF 的主要目标是通过开源软件推动云原生计算技术的发展和普及&#xff0c;帮助企业更…

四、使用renren-generator生成基本代码

1、打开generator.properties配置文件&#xff0c;修改配置 主要修改包名、模块名、前缀信息 2、修改application.yml配置文件中的数据库信息 3、启动项目 直接访问代码生成器 http://localhost/#generator选择表&#xff0c;点击生成代码即可

怎么使用github上传XXX内所有文件

要将 目录中的所有文件上传到 GitHub&#xff0c;你可以按照以下步骤进行&#xff1a; 创建一个新的 GitHub 仓库 登录到你的 GitHub 账户。 点击右上角的加号&#xff08;&#xff09;&#xff0c;选择 “New repository”。 输入仓库名称&#xff08;例如&#xff1a;202407…

滑动窗口练习6-找到字符串中所有字母异位词

题目链接&#xff1a;**. - 力扣&#xff08;LeetCode&#xff09;** 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#…

《程序猿入职必会(6) · 返回结果统一封装》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

Profinet从站转TCP/IP协议转化网关(功能与配置)

如何将Profinet和TCP/IP网络连接通讯起来呢?近来几天有几个朋友问到这个问题&#xff0c;那么作者在这里统一说明一下。其实有一个不错的设备产品可以很轻易地解决这个问题&#xff0c;名为JM-DNT-PN。接下来作者就从该设备的功能及配置详细说明一下。 一&#xff0c;设备主要…

el-table合计行更新问题

说明&#xff1a;在使用el-table自带的底部合计功能时&#xff0c;初始界面不会显示合计内容 解决方案&#xff1a;使用 doLayout()方法 updated() {this.$nextTick(() > {this.$refs[inventorySumTable].doLayout();});},完整代码&#xff1a; // show-summary&#xff1a…

Bugku的web题目get,post

1.web基础$_GET http://114.67.175.224:17587/ OK明显的代码审计题目。 让我们看看代码&#xff0c;先get获取what参数变量&#xff0c;如果what变量‘flag’&#xff0c;输出flag。 该题为GET传参&#xff0c;可直接在url后面加参数 在url后加上?whatflag 即可获得flag 2…

科普文:科普文:springcloud之-Hystrix服务容错

Hystrix概念 Hystrix 服务容错保护 的概念和说明 这就是大名鼎鼎的&#xff1a;豪猪 豪猪的英文就是&#xff1a;Hystrix&#xff0c;国外一些大牛的程序员在给自己的架构起名字的时候&#xff0c;往往就这么特别。哪天咱们中国人自己也能写出些架构&#xff0c;咱们就按照中…

2024后端开发面试题总结

一、前言 上一篇离职贴发布之后仿佛登上了热门&#xff0c;就连曾经阿里的师兄都看到了我的分享&#xff0c;这波流量真是受宠若惊&#xff01; 回到正题&#xff0c;文章火之后&#xff0c;一些同学急切想要让我分享一下面试内容&#xff0c;回忆了几个晚上顺便总结一下&#…

【VS2019安装+QT配置】

【VS2019安装QT配置】 1. 前言2. 下载visual studio20193. visual studio2019安装4. 环境配置4.1 系统环境变量配置4.2 qt插件开发 5. Visual Studio导入QT项目6. 总结 1. 前言 前期安装了qt&#xff0c;发现creator编辑器并不好用&#xff0c;一点都不时髦。在李大师的指导下&…

C++画蜡烛图

GPT-4o (OpenAI) 在 C 中绘制蜡烛图通常不像在高级语言&#xff08;如 Python&#xff09;中那么简单&#xff0c;因为 C 并没有内置的图形绘制库。然而&#xff0c;您可以使用一些第三方库来完成这项任务&#xff0c;比如使用 Qt 或者 SFML 等图形库。这里我们以 Qt 库为例&a…

PM2 快速上手指南

PM2是 Node.js 的优秀运行时管理工具&#xff0c;专为简化和优化 Node.js 应用程序的生产部署与运行而设计。 PM2 官网链接: https://pm2.keymetrics.io/ 1.PM2 的优势 持续运行&#xff1a;即使应用出错或崩溃&#xff0c;也能自动重启。负载均衡&#xff1a;智能地自动分…

Linux shell编程学习笔记67: tracepath命令 追踪数据包的路由信息

0 前言 网络信息是电脑网络信息安全检查中的一块重要内容&#xff0c;Linux和基于Linux的操作系统&#xff0c;提供了很多的网络命令&#xff0c;今天我们研究tracepath命令。 Tracepath 在大多数 Linux 发行版中都是可用的。如果在你的系统中没有预装&#xff0c;请根据你的…

WordPress插件介绍页源码单页Html

源码介绍 WordPress插件介绍页源码单页Html源码&#xff0c;这是一款产品介绍使用页面&#xff0c;也可以用来做其他软件或者应用介绍下载页&#xff0c;界面简约美观&#xff0c;源码由HTMLCSSJS组成&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器…

合作伙伴中心Partner Center中添加了Copilot预览版

目录 一、引言 二、Copilot 功能概述 2.1 Copilot 简介 2.2 Copilot 的核心功能 2.3 Copilot 的访问和使用 三、Copilot 的使用方法 3.1 Copilot 功能区域 3.2 Copilot 使用示例 3.2.1 编写有效提示 3.2.2 使用反馈循环 四、负责任的人工智能 4.1 Copilot 结果的可…

UE4如何直接调试Game

某些功能在编辑器里不好调试&#xff0c;例如Pak&#xff0c;就需要直接调试 Game&#xff0c;做法是选择 Game&#xff0c;不要选择Client&#xff0c;加断点&#xff0c;然后点击 Debug 就好了。 断点调试成功&#xff1a; 同时看到界面&#xff1a;