【Vue.js设计与实现解读-1】
Vue设计与实现阅读-1
- 1、命令式和声明式
- 2、性能
- 3、虚拟DOM性能
- 4、运行时和编译时
- 5、总结
前言
最近工作清闲了些,想着很久没有看书,Vue.js设计与实现这本书看了好几次都没有读完,趁着这个机会边读边记录一下吧。如果有理解的不正确的地方,欢迎指出。
1、命令式和声明式
命令式: 关注过程
例如实现这样一个代码
-获取 id 为 app 的 div 标签-设置他的文本内容为hello world-为他绑定点击事件-点击时弹出提示:ok
const div = document.querySelector('#app');
div.innerText = 'hello world';
div.addEventListener('click', () => {alert('ok');
});
声明式: 关注结果
同样用声明式的写法实验上面的功能
<div @click="() => alert('ok')">hello world</div>
上面这个html模板采用的是Vue实现的方式,可以看出,我们提供的是一个结果,vue内部去帮我封装了这个过程,就像我们告诉vue:我需要一个div,他的文本内容是hello world,有一个绑定事件。可以猜测,Vue.js的内部实现是命令式的,暴露给用户的是声明式的。
2、性能
声明式代码的性能不优于命令式代码的性能。
例如,还是上面的例子,我们需要修改 div 中文本的内容为 hello Vue。
命令式的写法,直接调用相关命令
div.textContent = 'hello Vue' // 直接修改
声明式写法
// 修改前
<div @click="() => alert('ok')">hello world</div>
// 修改后
<div @click="() => alert('ok')">hello Vue</div>
从上面可以看出,命令式代码明确知道哪里发生了变更,只需要做必要的修改就行,但声明式还不一定能做到这一点。对于一个框架来说,为了实现最优的更新性能,他需要找到前后的差异且只更新变化的地方。
如果我们把直接修改的性能消耗定义为A,找出差异的消耗定义为B,则
命令式代码的更新性能消耗 = A声明式代码的更新新能消耗 = A + B
在性能层面命令式是更好的选择,为什么Vue.js要选择声明式设计方案。原因在于声明式的可维护性更强。在上面的例子中可以看到,如果采用声明式开发,我们需要维护实现目标的整个过程(DOM创建、更新、删除等),而命令式我们只需要关注结果就行。
3、虚拟DOM性能
声明式代码的更新新能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗。如果能够最小化找出差异的性能消耗,就可以让声明式代码的性能无限接近于命令式代码的性能。所谓的虚拟DOM,就是为了最小化找出差异这一步的性能消耗而出现的。
innerHTML创建页面的过程: 构造 HTML 字符串,将该字符串赋值给 DOM 元素的 innerHTML 属性。
const html = `<div><span></span></div>
`
div.innerHTML = html
innerHTML创建页面的性能:HTML 字符串拼接的计算量 + innerHTML 的 DOM 计算量
虚拟DOM创建页面过程:第一步,创建js对象(真实 dom),第二步递归遍历虚拟 dom 树并创建真实 dom
虚拟dom创建页面的性能:创建JS对象的计算量 + 创建真实 DOM 的计算量
从创建页面来看,两者差距不大,都是需要新建所有 DOM 元素。但是继续看下更新页面时的性能
innerHTML更新页面的过程:相当于重新构建HTML字符串,再设置 DOM 元素的 innerHTML 属性。相当于更改了一个文字,也需要重新设置 innerHTML 属性,等价于销毁所有旧的 DOM 元素,再全量创建新的 DOM 元素。
虚拟DOM更新页面过程:创建 js 对象(虚拟DOM树),比较新旧虚拟 DOM, 找到变化的元素更新他。
对比:在更新页面的时候,虚拟 DOM 在 js 层面比创建页面时多了个找 diff 的性能消耗,但是比较是 js 层面的运算,不会产生数量级的差异,再观察 DOM 层面的运算,虚拟 DOM 只需要更新边户的COM, 而 innerHTML 需要全量更新。
4、运行时和编译时
设计框架,我们有三种选择,纯运行时、运行时+编译时、纯编译时。
假设设计一个框架,提供一个 Render 函数,用户可以为该函数提供一个属性结构的数据对象,Render 函数会根据对象递归将数据渲染成 DOM 元素。
规定树形结构如下:
const obj = {tag: 'div',children: [{tag: 'span',children: 'hello world'}]
}
每个对象有两个属性,tag 标签名称, children 可以是数组,表示子节点,可以是文本表示文本子节点。
Render 函数实现
function Render(obj, root) {// 创建标签const el = document.createElement(obj.tag);// 如果是文本子节点if (typeof obj.children === 'string') {// 创建文本元素const text = document.createTextNode(obj.children);// 添加进标签元素el.appendChild(text);} else if (obj.children) {// 子节点,递归调用render 构造子元素obj.children.forEach((child) => Render(child, el))}// 将元素添加到根节点root.appendChild(el);
}
元素使用
Render(obj, document.boby);
可以发现,用户在使用Render函数渲染内容的时候,可以直接提供一个树形数据对象给 Render 对象,不涉及其他额外步骤。
有一天用户抱怨,手写树形数据对象太麻烦,不直观,想支持用类型HMTL标签的方式描述树形结构对象。发现刚写的Render函数并不能满足。刚实现的Render函数,是一个纯运行时的框架。
为满足用户需求,使用编译的手段 把HTML 标签变异成树形结构的数据对象。
<div><span>hello world</span>
</div>------ 经过编译 ------const obj = {tag: 'div',children: [{tag: 'span',children: 'hello world'}]}
假设 你编写了一个 Compiler 的程序,他的作用是将 HTML 字符串编译成树形结构的对象,用户需要分别调用 Compiler 和 Render 函数就能实现。
const html = `<div><span>hello world</span></div>
`
const obj = Compiler(html);
Render(obj, document.boby);
此时 框架变成了运行时 + 编译时。既支持运行时,用户可以直接提供数据对象无需编译;又支持编译时,用户可以提供 HTML字符串,我们将它编译成数据对象交给运行时处理。
那编译器可以把 HTML 变成成数据对象,应该也可以编译成命令式代码。
<div><span>hello world</span>
</div>------ 经过编译 ------const div = document.createElement('div');
const span = document.createElement('span');
span.innerText = 'hello world';
div.appendChild(span);
document.boby.appendChild(div);
这样我们只需要一个 Compiler 函数就行。这就成了一个纯编译时的框架。我们不支持任何运行时内容,用户的代码通过编译器编译后才能运行。
一个框架可以是上面说的这三种的任意一种
纯运行时,无编译过程,因此没办法分析用户提供的内容。如果加入编译,我们可以分析用户提供内容,看到哪些发生改变,哪些没有,
那我们可以在编译时提取这些信息,传递给 Render 函数,Render函数根据这些信息进一步优化。但是如果是纯编译时,她可以分析用户提供的内容,
由于不需要任何运行时,而是直接编译,那性能会更好,但是因为用户提供的内容必须通过编译才能使用,所以有损灵活性
Vue3保持运行时+编译时的架构,在保持灵活性的基础上能够尽可能地去优化。
5、总结
- 命令式和声明式,命令式关注过程,声明式关注结果
- 声明式的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗
- 虚拟dom存在的意义在于使找出差异的性能消耗最小化
- 运行时和编译时的差异,vue3是编译时+进行时的框架。
相关文章:
【Vue.js设计与实现解读-1】
Vue设计与实现阅读-1 1、命令式和声明式2、性能3、虚拟DOM性能4、运行时和编译时5、总结 前言 最近工作清闲了些,想着很久没有看书,Vue.js设计与实现这本书看了好几次都没有读完,趁着这个机会边读边记录一下吧。如果有理解的不正确的地方&…...

苗情生态自动监测系统-科普知识
随着科技的飞速发展,智能化技术在各个领域的应用越来越广泛。在农业领域,苗情生态自动监测系统的出现,为农业生产带来了革命性的变革。它不仅能够实时监测植物的生长状况,还能对环境因素进行全面监控,为农业生产提供科…...

test 系统学习-04-test converate 测试覆盖率 jacoco 原理介绍
测试覆盖率 测试覆盖率(test coverage)是衡量软件测试完整性的一个重要指标。掌握测试覆盖率数据,有利于客观认识软件质量,正确了解测试状态,有效改进测试工作。 当然,要发挥这些作用,前提是我们掌握了真实的测试覆盖…...

小型企业成为网络犯罪分子获取数据的目标
在过去十年的大部分时间里,网络犯罪的巨额资金来自针对大型组织的勒索软件攻击。这种威胁仍然存在。但犯罪分子可能会将注意力转向中小企业 (SMB)。这对消费者的影响将是巨大的。 将软件即服务 (SaaS) 技术用于核心业务功能继续将中小企业整合到全球供应链中。由于…...

PyTorch的Tensor(张量)
一、Tensor概念 什么是张量? 张量是一个多维数组,它是标量、向量、矩阵的高维拓展 Tensor与Variable Variable是torch.autograd中的数据类型,主要用于封装Tensor,进行自动求导。 data: 被包装的Tensorgrad: data的梯度&…...

spug发布问题汇总记录
问题导览 1. [vite]: Rollup failed to resolve import "element-plus" from "src/main.js". 项目框架简介 vue3viteelement-plus 解决方案 - 1. 配置淘宝镜像源:npm config set registry https://registry.npm.taobao.org/ - 2. npm inst…...

SpringBoot-搭建集成Mybatis的项目
本文介绍了如何在IntelliJ IDEA中使用SpringBoot和Mybatis构建Java Web应用程序。通过本文的学习,读者将了解如何使用IntelliJ IDEA快速搭建一个基于SpringBoot和Mybatis的Java Web应用程序,提高开发效率。IntelliJ IDEA是一款功能强大的Java集成开发环境…...
mysql隐式转换规则
MySQL 中的隐式类型转换发生在比较操作或者其他一些需要特定数据类型参数的上下文中,如果参与操作的表达式或列的数据类型不匹配,MySQL 就会自动进行数据类型转换以适配预期的数据类型。 以下是 MySQL 的一些常见隐式转换规则: 字符串和数字…...

怎么解决 Nginx反向代理加载速度慢?
Nginx反向代理加载速度慢可能由多种原因引起,以下是一些可能的解决方法: 1,网络延迟: 检查目标服务器的网络状况,确保其网络连接正常。如果目标服务器位于不同的地理位置,可能会有较大的网络延迟。考虑使用…...
Eureka工作原理超详细讲解介绍
Eureka 是 Netflix 开源的一款服务注册与发现框架,主要用于构建分布式系统中的服务治理和负载均衡。下面是关于 Eureka 工作原理的详细介绍:1.Eureka 架构: Eureka 采用了客户端-服务器架构,包括 Eureka Server 和 Eureka Client …...
SQL WHERE 语句(条件选择)
WHERE 子句用于过滤记录。 SQL WHERE 子句 WHERE 子句用于提取那些满足指定条件的记录。 SQL WHERE 语法 SELECT column1, column2, ... FROM table_name WHERE condition; 参数说明: column1, column2, ...:要选择的字段名称,可以为多…...

用UCLI(TCL)控制verdi dump 波形
UCLI(Unified Command-line Interface)为Synopsys验证工具了提供一组通用命令,通过UCLI可以执行任意TCL(Tool Command Language)命令。在我们的验证环境中,通常跟ucli打交道的地方是用来控制开始dump和结束…...

如何使用 Python+selenium 进行 web 自动化测试?
Selenium是一个自动化测试工具,它可以模拟用户在浏览器中的操作,比如点击、输入、选择等等。它支持多种浏览器,包括Chrome、Firefox、Safari等等,并且可以在多个平台上运行。 安装和配置Selenium 在使用Selenium之前,…...
约瑟夫问题
约瑟夫问题 题目描述 n n n 个人围成一圈,从第一个人开始报数,数到 m m m 的人出列,再由下一个人重新从 1 1 1 开始报数,数到 m m m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。…...

文件管理方法:利用文件大小进行筛选,高效移动文件至目标文件夹
在日常工作中,文件管理是一项至关重要的任务。为了更高效地管理文件,可以利用文件大小进行筛选,并将文件快速移动至目标文件夹。接下来一起来看看云炫文件管理器如何利用文件大小进行筛选,以及如何高效移动文件至目标文件夹的方法…...

python报错:TypeError: Descriptors cannot be created directly.
问题 报错提示: TypeError:不能直接创建描述符。 如果此调用来自 _pb2.py 文件,则您生成的代码已过期,必须使用 protoc > 3.19.0 重新生成。 如果您不能立即重新生成原型,其他一些可能的解决方法是: 1.…...

Linux 内核调试
文章目录 一、方法论 一、方法论 qemu 虚拟机 Linux内核学习 Linux 内核调试 一:概述 Linux 内核调试 二:ubuntu20.04安装qemu Linux 内核调试 三:《QEMU ARM guest support》翻译 Linux 内核调试 四:qemu-system-arm功能选项整…...

Prometheus-AlertManager 邮件告警
环境,软件准备 本次演示环境,我是在虚拟机上安装 Linux 系统来执行操作,以下是安装的软件及版本: System: CentOS Linux release 7.6Docker: 24.0.5Prometheus: v2.37.6Consul: 1.6.1 docker 安装prometheus,alertmanage,说明一下这里直接将…...

Volcano Controller控制器源码解析
Volcano Controller控制器源码解析 本文从源码的角度分析Volcano Controller相关功能的实现。 本篇Volcano版本为v1.8.0。 Volcano项目地址: https://github.com/volcano-sh/volcano controller命令main入口: cmd/controller-manager/main.go controller相关代码目录: pkg/co…...

开源协议简介和选择
软件国产化已经提到日程上了,先来研究一下开源协议。 引言 在追求“自由”的开源软件领域的同时不能忽视程序员的权益。为了激发程序员的创造力,现今世界上有超过60种的开源许可协议被开源促进组织(Open Source Initiative)所认可…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...