如何快速实现一个虚拟 DOM 系统
虚拟 DOM 是目前主流前端框架的技术核心之一,本文阐述如何实现一个简单的虚拟 DOM 系统。
为什么需要虚拟 DOM?
虚拟 DOM 就是一棵由虚拟节点组成的树,这棵树展现了真实 DOM 的结构。这些虚拟节点是轻量的、无状态的,一般是字符串或者仅仅包含必要字段的 JavaScript 对象。虚拟节点可以被组装成节点树树,通过特定的 "diff" 算法对两个节点树进行对比,找出其中细微的变更点,然后更新到真实 DOM 上去。
之所以会有虚拟 DOM,是因为直接更新真实 DOM 非常昂贵。通过新比对虚拟 DOM,然后只将变化的部分更新到真实 DOM 上去。这么做都是操作纯 JavaScript 对象,尽量避免了直接操作 DOM,读写成本低很多。
如何实现虚拟 DOM
在开始之前,我们需要明确一个虚拟 DOM 系统应该包含哪些必要的组成部分?
首先,我们要定义清楚什么是虚拟节点。一个虚拟节点可以是一个普通 JavaScript 对象,也可以是一个字符串。
我们定义一个函数 createNode 来创建虚拟节点。一个虚拟节点至少包含三个信息:
tag:保存虚拟节点的标签名,字符串props:保存虚拟节点的 properties/attributes,普通对象children:保存虚拟节点的子节点,数组
下面的代码是 createNode 实现样例:
const createNode = (tag, props, children) => ({
tag,
props,
children,
});
我们通过 createNode 可以轻松的创建虚拟节点:
createNode('div', { id: 'app' }, ['Hello World']);
// 返回如下:
{
tag: 'div',
props: { id: 'app' },
children: ['Hello World'],
}
现在,我们需要定义一个 createElement 函数来根据虚拟节点创建真实的 DOM 元素。
在 createElement 中,我们需要创建一个新的 DOM 元素,然后遍历虚拟节点的 props 属性,将其中的属性添加到 DOM 元素上去,之后再遍历 children 属性。如下代码是一个实现样例:
const createElement = vnode => {
if (typof vnode === 'string') {
return document.createTextNode(vnode); // 如果是字符串就直接返回文本元素
}
const el = document.createElement(vnode.tag);
if (vnode.props) {
Object.entries(vnode.props).forEach(([name, value]) => {
el[name] = value;
});
}
if (vnode.children) {
vnode.children.forEach(child => {
el.appendChild(createElement(child));
});
}
return el;
}
现在,我们可以通过 createElement 将虚拟节点转变成真实 DOM 了。
createElement(createNode("div", { id: "app" }, ["Hello World"]));
// 输出: <div id="app">Hello World</div>
我们再来定义一个 diff 函数来实现 'diff' 算法。这个 diff 函数接收三个参数,一个是已经存在的 DOM 元素,一个是旧的虚拟节点,一个是新的虚拟节点。在这个函数中,我们将对比两个虚拟节点,在需要的时候,将旧的元素替换掉。
const diff = (el, oldVNode, newVNode) => {
const replace = () => el.replaceWith(createElement(newVNode));
if (!newVNode) return el.remove();
if (!oldVNode) return el.appendChild(createElement(newVNode));
// 处理纯文本的情况
if (typeof oldVNode === 'string' || typeof newVNode === 'string') {
if (oldVNode !== newVNode) return replace();
} else {
// 对比标签名
if (oldVNode.tag !== newVNode.tag) return replace();
// 对比 props
if (!oldVNode.props?.some((prop) => oldVNode.props?[prop] === newVNode.props?[prop])) return replace();
// 对比 children
[...el.childNodes].forEach((child, i) => {
diff(child, oldVNode.children?[i], newVNode.children?[i]);
});
}
}
在这个函数中,我们先处理纯文本的情况,如果新旧两个字符串不相同,则直接替换。之后,我们就可以假定两个虚拟节点都是对象了。我们先对比两个节点的标签名是否相同,不同则直接替换。之后对比两个节点的 props 是否相同,不同也直接替换。最后我们在递归的使用 diff 函数对比两个虚拟节点的 children。
至此,我们就实现了一个简版虚拟 DOM 系统所必须的所有功能。下面是使用样例:
const oldVNode = createNode("div", { id: "app" }, ["Hello World"]);
const newVNode = createNode("div", { id: "app" }, ["Goodbye World"]);
const el = createElement(oldVNode);
// <div id="app">Hello World</div>
diff(el, oldVNode, newVNode);
// el will become: <div id="app">Goodbye World</div>
文中的实现侧重于展示虚拟 DOM 的实现原理,在实现代码中并未考虑性能等其他因素。
如何快速实现一个虚拟 DOM 系统的更多相关文章
- vue 快速入门 系列 —— 虚拟 DOM
其他章节请看: vue 快速入门 系列 虚拟 DOM 什么是虚拟 dom dom 是文档对象模型,以节点树的形式来表现文档. 虚拟 dom 不是真正意义上的 dom.而是一个 javascript 对 ...
- 手撸一个虚拟DOM,不错
大家好,我是半夏,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注 点赞 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师-关注公众号:搞前端的半夏,了解更多前端知 ...
- 手动实现一个虚拟DOM算法
发现一个好文:<深度剖析:如何实现一个 Virtual DOM 算法> 源码 文章写得非常详细,仔细看了一遍代码,加了一些注释.其实还有有一些地方看的不是很懂(毕竟我菜qaq 先码 有时间 ...
- 手写一个虚拟DOM库,彻底让你理解diff算法
所谓虚拟DOM就是用js对象来描述真实DOM,它相对于原生DOM更加轻量,因为真正的DOM对象附带有非常多的属性,另外配合虚拟DOM的diff算法,能以最少的操作来更新DOM,除此之外,也能让Vue和 ...
- PHP7 学习笔记(十一)使用phpstudy快速配置一个虚拟主机
说明:为了windows本地开发php方便,这里推荐使用PHP集成环境phpstudy. 目的:使用域名访问项目(tinywan.test) 1.官网:http://www.phpstudy.net ...
- 虚拟DOM介绍
[转自]:https://www.jianshu.com/p/616999666920 为什么需要虚拟DOM 先介绍浏览器加载一个HTML文件需要做哪些事,帮助我们理解为什么我们需要虚拟DOM.web ...
- 虚拟dom?diff算法?key?Vue原理的核心三问?打包教你搞定。
为什么需要虚拟DOM 先介绍浏览器加载一个HTML文件需要做哪些事,帮助我们理解为什么我们需要虚拟DOM.webkit引擎的处理流程,如下图所示: 所有浏览器的引擎工作流程都差不多,如上图大致分5步: ...
- 2.ReactJS基础(虚拟DOM,JSX语法)
将脚手架(create-react-app)创建的todolist项目精简为hello world示例 即,删除自动生成的样式文件.logo.svt.App.test.js.serviceWorker ...
- 虚拟DOM与DOM diff算法
虚拟DOM是什么? 一个虚拟DOM(元素)是一个一般的js对象, 准确的说是一个对象树(倒立的) 虚拟DOM保存了真实DOM的层次关系和一些基本属性,与真实DOM一一对应,如果只是更新虚拟DOM, 页 ...
随机推荐
- SSL数字证书颁发
一.数字证书 1.数字证书实际上是存在于计算机上的一个记录,是由CA签发的一个声明,证明证书主体("证书申请者"拥有了证书后即成为"证书主体")与证书中所包含的 ...
- STM32F4-IAP学习笔记--(转)
花了断断续续两天时间在STM32上面写了一个IAP(In Application Programing)Boot,期间多多少少还是遇到的了不少问题.现在就花点时间把这两天写的东西整理一下,就当是学习笔 ...
- 【错误解决】Error creating bean with name 'transactionManager' :nested exception is java.lang.NoClassDefFoundError: org/springframework/jdbc/datasource/
搭建ssh框架中新建JUint测试出现的问题.这个问题实在太伤脑筋....因为不好找到解决办法 直接先说解决方式:添加org.springframework.jdbc-XX.jar,然后build p ...
- Python数模笔记-StatsModels 统计回归(4)可视化
1.如何认识可视化? 图形总是比数据更加醒目.直观.解决统计回归问题,无论在分析问题的过程中,还是在结果的呈现和发表时,都需要可视化工具的帮助和支持. 需要指出的是,虽然不同绘图工具包的功能.效果会有 ...
- 机器学习实战二:波士顿房价预测 Boston Housing
波士顿房价预测 Boston housing 这是一个波士顿房价预测的一个实战,上一次的Titantic是生存预测,其实本质上是一个分类问题,就是根据数据分为1或为0,这次的波士顿房价预测更像是预测一 ...
- nlp任务中的传统分词器和Bert系列伴生的新分词器tokenizers介绍
layout: blog title: Bert系列伴生的新分词器 date: 2020-04-29 09:31:52 tags: 5 categories: nlp mathjax: true ty ...
- 【greys使用】阿里greys在线诊断工具
Greys是一个Java进程的异常诊断工具,可以在不停止程序的前提下,对一些问题进行检测.这个框架主要是采用Java的探针技术,可以做到动态修改java的字节码技术.前提是Jdk版本6+.(prema ...
- Python+Selenium - 定位策略
一个元素定位可能会在多个地方用到,用loc变量接收元素的定位方式和相对路径.使用时再拆包(*loc) loc = (定位方式,'定位方式对应的元素路径') =>元素定位方式及路径 driv ...
- ICCV2019论文点评:3D Object Detect疏密度点云三维目标检测
ICCV2019论文点评:3D Object Detect疏密度点云三维目标检测 STD: Sparse-to-Dense 3D Object Detector for Point Cloud 论文链 ...
- 3D点云深度学*
3D点云深度学* 在自动驾驶中关于三维点云的深度学*方法应用.三维场景语义理解的方法以及对应的关键技术介绍. 1. 数据 但是对于3D点云,数据正在迅速增长.大有从2D向3D发展的趋势,比如在open ...