大家好,我是半夏,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注 点赞 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师~关注公众号:搞前端的半夏,了解更多前端知识,回复 ”网站模板“,免费送N++网站模板!!点我探索新世界!

什么是DOM

DOM(文档对象模型)是一种树状结构,包含有关 HTML(或 XML)页面结构的信息。树中的每个单独的节点代表网页上的一个元素。

在 Javascript 中,可以通过window.document对象访问和修改 DOM。让我们看看如何使用 DOM 接口向网页添加元素。

我们有下面HTML模板代码。

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<title>DOM</title>
</head>
<body>
<div id="app"></div>
<script src="./main.js"></script>
</body>
</html>

PS: 写过Vue项目的同学可能比较熟悉,在Vue脚手架生成的项目中,public文件夹下的index.html也是定义了一个div#app.

要通过 DOM 接口更改页面的内容,我们可以执行以下操作:

const app = document.querySelector('#app');

app.innerHTML = `
<h1>Hello from DOM</h1>
`;

首先,我们从 DOM 中获取一个 id 为“app”的元素,然后我们更改该元素的内容。

这种修改DOM的方法,在以前是我们经常使用的,尤其是JQuery时代。这种方式适用于不经常更新UI的小型应用程序,如果我们想要构建一个高响应的网站,这种方法就会出现问题。

JS操作DOM 是很慢的。每次都重新创建整棵树会浪费时间和资源。如果我们想构建一个高反应性的网页,我们需要寻找另一种解决方案。

一种方法是通过比较新旧树来查看哪些元素需要更新。这正是 Virtual DOM 的目标

创建一个虚拟 DOM

在真实的 DOM 中,有一个document.createElement创建新节点的方法。对于我们的虚拟 DOM,我们也需要这样一个方法。

view方法

让我们创建一个名为h(约定)的函数

const h = (type, props={}, children=[]) => ({
type,
props,
children,
});
  • type参数描述了 HTML 元素的类型,例如h1div等等...

  • props参数的工作方式与 React/Vue 中的 props 完全相同——它允许我们将数据(属性)传递给元素

  • children当前元素内其他子节点。

让我们看看它是如何使用的。


const view = () =>
h('div', {}, [
h('h1', {}, ['Hello']),
h('p', {}, ['from virtual DOM!']),
h('p', {}, ['from virtual DOM!']),
h('p', {}, ['from virtual DOM!']),
h('p', {}, ['from virtual DOM!']),
]);

我们创建了一个div元素,里面有h1p元素。这些元素中的每一个都有一个文本节点作为其子节点。

现在是时候将这个虚拟树转换为实际的 DOM。

render方法

让我们实现一个render功能。


const render = (root, view) => {
const rendered = view(); diff(root, null, rendered);
}; const diff = (root, oldNode, newNode, index) => {
// 判断节点是否变化,有变化则更新 }; render(app, view);

渲染函数首先调用view函数,然后运行 diff 函数,该函数接受一个根元素(来自真实 DOM)、旧的虚拟节点(因为我们第一次渲染它是null)和新的虚拟节点 。

diff方法

基本上,该diff函数只会将 oldNode 与 newNode 进行比较,看看它是否需要更新root.

现在让我们看看如何实现 diff 函数。

const diff = (root, oldNode, newNode, index) => {
// 判断节点是否变化,有变化则更新
if (!oldNode) {
root.appendChild(createElement(newNode));
}
};

如果没有oldNode,我们需要创建这个元素并将其插入 DOM。首先,我们使用该函数创建一个元素createElement,然后我们在第二个实现该函数,然后我们appendChild在一个真实的 DOM 元素上使用该方法,将该节点附加为其子节点。

让我们实现createElement功能。

const createElement = (node) => {
if (typeof node === 'string') {
return document.createTextNode(node);
} const el = document.createElement(node.type);
node.children.map(createElement).forEach(el.appendChild.bind(el)); return el;
};

如果一个节点是一个文本节点(例如“Hello”),我们只需使用document.createTextNode函数渲染它。

如果不是,我们创建给定类型的元素,document.createElement然后循环遍历它的每个子元素,createElement递归调用函数。这样我们就创建了整个树并返回它。

让我们看看到目前为止我们编写的完整代码:


const app = document.querySelector('#app'); const h = (type, props = {}, children = []) => ({
type,
props,
children,
}); const view = () =>
h("div", {}, [
h("h1", {}, ["Hello"]),
h("p", {}, ["from virtual DOM!"]),
h("p", {}, ["from virtual DOM!"]),
h("p", {}, ["from virtual DOM!"]),
h("p", {}, ["from virtual DOM!"]),
]); const render = (root, view) => {
const rendered = view(); diff(root, null, rendered);
}; const diff = (root, oldNode, newNode, index) => {
// 判断节点是否变化,有变化则更新
if (!oldNode) {
root.appendChild(createElement(newNode));
}
}; const createElement = (node) => {
if (typeof node === 'string') {
return document.createTextNode(node);
} const el = document.createElement(node.type);
node.children.map(createElement).forEach(el.appendChild.bind(el)); return el;
};
render(app, view);

现在,在浏览器中,我们可以检查我们的应用程序是否正常工作 - 如果我们运行此代码,我们将看到以下内容:

结论

耶。现在使用viewh函数,我们可以构建无限复杂的 UI。

当然,我们还没有实现状态管理,所以我们不能改变 DOM 中的任何东西。而且我们没有将任何属性传递给 DOM,因此我们无法真正设置应用程序的样式。这个我们会在下一篇文章中继续实现!

手撸一个虚拟DOM,不错的更多相关文章

  1. 手写一个虚拟DOM库,彻底让你理解diff算法

    所谓虚拟DOM就是用js对象来描述真实DOM,它相对于原生DOM更加轻量,因为真正的DOM对象附带有非常多的属性,另外配合虚拟DOM的diff算法,能以最少的操作来更新DOM,除此之外,也能让Vue和 ...

  2. 放弃antd table,基于React手写一个虚拟滚动的表格

    缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...

  3. 使用Java Socket手撸一个http服务器

    原文连接:使用Java Socket手撸一个http服务器 作为一个java后端,提供http服务可以说是基本技能之一了,但是你真的了解http协议么?你知道知道如何手撸一个http服务器么?tomc ...

  4. 【手撸一个ORM】MyOrm的使用说明

    [手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...

  5. 第二篇-用Flutter手撸一个抖音国内版,看看有多炫

    前言 继上一篇使用Flutter开发的抖音国际版 后再次撸一个国内版抖音,大部分功能已完成,主要是Flutter开发APP速度很爽,  先看下图 项目主要结构介绍 这次主要的改动在api.dart 及 ...

  6. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  7. C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架

    C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...

  8. 如何快速实现一个虚拟 DOM 系统

    虚拟 DOM 是目前主流前端框架的技术核心之一,本文阐述如何实现一个简单的虚拟 DOM 系统. 为什么需要虚拟 DOM? 虚拟 DOM 就是一棵由虚拟节点组成的树,这棵树展现了真实 DOM 的结构.这 ...

  9. 手撸一个SpringBoot-Starter

    1. 简介 通过了解SpringBoot的原理后,我们可以手撸一个spring-boot-starter来加深理解. 1.1 什么是starter spring官网解释 starters是一组方便的依 ...

随机推荐

  1. 内网渗透----使用mimikatz获取windows登陆口令

    使用mimikatz提取windows密码 以管理员运行后,可以随机打一些字符,进入如下界面 输入aaa::aaa,可展示所有模块: 可采用log命令,保存日志 获取hash与明文用户口令 privi ...

  2. Java的自动装箱与拆箱(Autoboxing and unboxing)

    一.什么是自动装箱拆箱 很简单,下面两句代码就可以看到装箱和拆箱过程 1 //自动装箱 2 Integer total = 99; 3 4 //自动拆箱 5 int totalprim = total ...

  3. C 上楼梯 中国石油大学新生训练赛#11

    问题 C: 上楼梯 时间限制: 1 Sec  内存限制: 128 MB提交 状态 题目描述 明明上n 级台阶可用四种步幅, 当然每种步幅花费的体力也不一样, 对应关系如下明明开始有m 个体力, 求他最 ...

  4. 洛谷P1091 [NOIP2004 提高组] 合唱队形

    本题是一个简单的 LIS(最长上升子序列)问题     只是要求俩次最长上子序列而已   很容易的 首先由于是最长上升子序列 所以朴素法的动态规划表达式为  f[i] = max( f[i] , f[ ...

  5. git tag、gitignore和git撤销提交

    前言 最近在git的使用过程中遇到了一些新的问题,所以写下来方便自己回忆. git tag 打标签 git tag -a v1.00 -m "注释" git tag 打标签命令 - ...

  6. 什么是内部类? Static Nested Class 和 Inner Class的不同?

    内部类就是在一个类的内部定义的类,内部类中不能定义静态成员. 内部类作为其外部类的一个成员,因此内部类可以直接访问外部类的成员.但有一点需要指出:静态成员不能访问非静态成员,因此静态内部类不能访问外部 ...

  7. 为什么需要域驱动设计(DDD)?

    我们需要 DDD 的因素 – 微服务面试问题

  8. elasticsearch 了解多少,说说你们公司 es 的集群架构,索 引数据大小,分片有多少,以及一些调优手段 。

    面试官:想了解应聘者之前公司接触的 ES 使用场景.规模,有没有做过比较大 规模的索引设计.规划.调优. 解答: 如实结合自己的实践场景回答即可. 比如:ES 集群架构 13 个节点,索引根据通道不同 ...

  9. C语言小游戏——2048

      2048   2048这款游戏的玩法很简单,每次可以选择上下左右滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢.相撞时会相加. ...

  10. stylus css tooltips 工具提示

    tooltips 纯css工具提示 bubbles-tooltips 查看效果 演示 安装 npm install tooltips --save 使用 在 gulp 中使用 gulp var gul ...