当前位置: 首页 > news >正文

不只是mini-react第一节:实现最简单mini-react

项目总结构:
├─ 📁core
│  ├─ 📄React.js
│  └─ 📄ReactDom.js
├─ 📁node_modules
├─ 📁tests
│  └─ 📄createElement.spec.js
├─ 📄App.js
├─ 📄index.html
├─ 📄main.js
├─ 📄package-lock.json
├─ 📄package.json
└─ 📄pnpm-lock.yaml
原生js怎么写?
const dom = document.createElement("div")
dom.id="app"
document.querySelector('#root').append(dom)const textNode = document.createTextNode("")
textNode.nodeValue = "app"
dom.append(textNode)
为什么需要虚拟dom?

通过以上原生写法可以看出,虚拟dom可以简化开发,使代码更加框架、结构化,清晰可读易于维护。

框架层面:频繁的dom操作会一直导致浏览器重排和重绘,会严重影响性能。

原生层面:相对于原生层面虚拟dom对性能提示微乎其微,并且在复杂情况会损耗性能。

当然虚拟dom还有个非常重要的作用就是跨端运行。

面试怎么答?

  • 减少浏览器重排和重绘(框架层面、原生层面)
  • 跨平台运行,不局限于浏览器
  • 减少心智负担,提高开发效率

当然不能干巴巴的把这几点甩出去,记得拓展。

极简版React内核

此处代码实现了一个极简的创建虚拟dom和虚拟dom转真实dom

/core/React.js

//创建文本对象虚拟dom
function createTextNode(text) {return {type: "TEXT_ELEMENT",props: {nodeValue: text,children: [],},};
}//创建虚拟dom对象
function createElement(type, props, ...children) {return {type,props: {...props,children: children.map((child) => {return typeof child === "string" ? createTextNode(child) : child;}),},};
}//渲染器,vdom->tdom
function render(el, container) {const dom =el.type === "TEXT_ELEMENT"? document.createTextNode(""): document.createElement(el.type);// id classObject.keys(el.props).forEach((key) => {if (key !== "children") {dom[key] = el.props[key];}});const children = el.props.children;children.forEach((child) => {render(child, dom);});container.append(dom);
}const React = {render,createElement,
};export default React;

可以看到以上代码中createElement函数就实现了一个极简版的虚拟dom,里面有三个元素分别是:

  • type:类型
  • props:传入的值
  • children:子节点

render函数则实现了一个极简版的渲染器,用于将虚拟dom转化成真实dom,传入的值分别是:

  • el:虚拟dom对象
  • container:具体在哪个真实dom节点渲染
创建虚拟dom对象

/App.js

通过上面定义的createElement函数来创建一个虚拟dom对象app并将其导出

import React from './core/React.js';
const App = React.createElement("div", { id: "app" }, "hi- ", "mini-react");
export default App
极简React渲染组件

定义了一个createRoot函数,通过传入的渲染容器container和虚拟dom对象App来将虚拟dom元素渲染到真实dom上。

/core/ReactDom.js

import React from "./React.js";
const ReactDOM = {createRoot(container) {return {render(App) {React.render(App, container);},};},
};export default ReactDOM;
创建ReactDOM

/main.js

import ReactDOM from "./core/ReactDom.js";
import App from "./App.js";ReactDOM.createRoot(document.querySelector("#root")).render(App);
使用vitest单元测试验证

控制台运行npm i vitest下载依赖。

然后再package.json中自定义测试脚本

/package.json

{"scripts": {"test": "vitest"},"devDependencies": {"vitest": "^1.1.3"}
}

然后创建tests文件夹并编写测试文件,具体路径于代码如下:

/tests/createElement.spec.js

import React from "../core/React";
import { it, expect, describe } from "vitest";describe("createElement", () => {it("props is null", () => {const el = React.createElement("div", null, "hi");expect(el).toMatchInlineSnapshot(`{"props": {"children": [{"props": {"children": [],"nodeValue": "hi",},"type": "TEXT_ELEMENT",},],},"type": "div",}`)});it("should return element vdom", () => {const el = React.createElement("div", {id:"id"}, "hi");expect(el).toMatchInlineSnapshot(`{"props": {"children": [{"props": {"children": [],"nodeValue": "hi",},"type": "TEXT_ELEMENT",},],"id": "id",},"type": "div",}`)});
});

创建完成后在控制台输入npm test即可运行测试脚本。

在上面代码中,describe用于创建测试组,每个it是测试组内的单个测试单元,而it内就是具体测试内容。

这里使用的是toMatchInlineSnapshot快照测试,用于比对el结果是否与快照函数toMatchInlineSnapshot内容一致,如果一致则测试通过。

总结

createRoot用于将虚拟dom渲染成真实dom

createElement用于创建虚拟dom对象

rendercreateRoot的内核

贴上main.js

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div id="root"></div><script type="module" src="main.js"></script>
</body></html>

相关文章:

不只是mini-react第一节:实现最简单mini-react

项目总结构&#xff1a; ├─ &#x1f4c1;core │ ├─ &#x1f4c4;React.js │ └─ &#x1f4c4;ReactDom.js ├─ &#x1f4c1;node_modules ├─ &#x1f4c1;tests │ └─ &#x1f4c4;createElement.spec.js ├─ &#x1f4c4;App.js ├─ &#x1f4c4;in…...

前端路由layout布局处理以及菜单交互(三)

上篇介绍了前端项目部署以及基本依赖的应用&#xff0c;这次主要对于路由以及布局进行模块化处理 一、 创建layout模块 1、新建src/layout/index.vue <template><el-container class"common-layout"><!-- <el-aside class"aside">&l…...

小结:DNS,HTTP,SMTP,IMAP,FTP,Telnet,TCP,ARP,ICMP

DNS&#xff08;Domain Name System&#xff0c;域名系统&#xff09; 是互联网的重要组成部分&#xff0c;它负责将人类易读的域名&#xff08;如 www.google.com&#xff09;转换为机器可以识别的 IP 地址&#xff08;如 142.250.72.206&#xff09;。这一过程被称为域名解析…...

【C++】P2550 [AHOI2001] 彩票摇奖

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述输入格式&#xff1a;输出格式&#xff1a;输入输出样例&#xff1a; &#x1f4af;题解思路1. 问题解析 &#x1f4af;我的实现实现逻辑问题分析 &#x1f4af;老…...

并发服务器框架——zinx

zinx框架 Zinx 是一个用 Go 语言编写的高性能、轻量级的 TCP 服务器框架&#xff0c;它被设计为简单、快速且易于使用。Zinx 提供了一系列的功能&#xff0c;包括但不限于连接管理、数据编解码、业务处理、负载均衡等&#xff0c;适用于构建各种 TCP 网络服务&#xff0c;如游戏…...

Unity 中计算射线和平面相交距离的原理

有此方法 能够计算射线和平面是否相交以及射线起点到平面交点的距离 代码分析 var dot Vector3.Dot(ray.direction, plane.normal);计算射线和平面法线的点积&#xff0c;如果大于等于0&#xff0c;则说明射线和平面没有相交&#xff0c;否则&#xff0c;说明射线和平面相交…...

浅谈棋牌游戏开发流程七:反外挂与安全体系——守护游戏公平与玩家体验

一、前言&#xff1a;为什么反外挂与安全这么重要&#xff1f; 对于任何一款线上棋牌游戏而言&#xff0c;公平性和玩家安全都是最重要的核心要素之一。如果游戏环境充斥着各式各样的外挂、作弊方式&#xff0c;不仅会毁坏玩家体验&#xff0c;更会导致游戏生态崩塌、口碑下滑…...

《无力逃脱》V1.0.15.920(59069)官方中文版

艾丹是一名三臂赏金猎人&#xff0c;他必须追捕银河系中最危险、最难以捉摸的割喉者。 有些悬赏是金钱&#xff0c;有些则是有价值的信息。艾丹可以利用这些信息找到让他走上这条路的人&#xff0c;同时也会卷入一个全银河系的阴谋中。 拥有三条手臂可以让你同时对付更多的敌…...

六种主流服务器的选择与使用

网络的运行离不开各种服务器&#xff0c;它们各司其职&#xff0c;为我们提供稳定的网络服务。本文带大家了解6种常见服务器类型。 服务器的六大种类 第一种&#xff1a;Web服务器 Web服务器是互联网的核心。当你打开一个网站&#xff0c;比如百度或淘宝&#xff0c;浏览器会…...

TiDB 升级至高版本提示'mysql.tidb_runaway_watch' doesn't exist 问题处理

作者&#xff1a; asd80703406 原文来源&#xff1a; https://tidb.net/blog/90394c97 背景 近期发现很多人从低版本升级至TiDB v7 或者v8版本&#xff0c;均遇到了tidb-server启动失败&#xff0c;提示报错如下&#xff1a; ["get runaway watch record failed"…...

GRU-PFG:利用图神经网络从股票因子中提取股票间相关性

“GRU-PFG: Extract Inter-Stock Correlation from Stock Factors with Graph Neural Network” 论文地址&#xff1a;https://arxiv.org/pdf/2411.18997 摘要 股票预测模型可以分为两个主要类别&#xff1a;第一类&#xff0c;例如GRU和ALSTM&#xff0c;这些模型仅基于股票…...

数字化供应链创新解决方案在零售行业的应用研究——以开源AI智能名片S2B2C商城小程序为例

摘要&#xff1a; 在数字化转型的浪潮中&#xff0c;零售行业正经历着前所未有的变革。特别是在供应链管理方面&#xff0c;线上线下融合、数据孤岛、消费者需求多样化等问题日益凸显&#xff0c;对零售企业的运营效率与市场竞争力构成了严峻挑战。本文深入探讨了零售行业供应…...

安卓Activity执行finish后onNewIntent也执行了

测试反应投屏时下一集可能播放不成功。 首先看一下日志&#xff1a; onCompletion onCast handlerMessage: 2 finish: PlayerActivityabc7fdc onPause: PlayerActivityabc7fdc onNewIntent: PlayerActivityabc7fdc onResume: PlayerActivityabc7fdc onPause: PlayerActivityab…...

数据结构.期末复习.学习笔记(c语言)

《数据结构》复习概要 一、概论 二、基础1. 基本概念2. 四种逻辑结构及特点3. 算法的概念、特性4. 算法设计的4个要求 三、线性结构1.顺序表2.单链表3.循环链表双向链表4.栈&#xff08;后进先出&#xff09;5.队列&#xff08;先进先出&#xff09; 四、树和二叉树1.树2.二叉…...

Kafaka安装与启动教程

1.下载 先去官网Apache Kafka可以查看到每个版本的发布时间。选择你要安装的版本。 然后进入linux建立要存放的文件夹&#xff0c;用wget命令下载 2.安装 先解压缩&#xff1a; tar -xvzf kafka_2.12-3.5.1.tgz -C ../ 3.配置文件 修改server.properties&#xff1a; cd .…...

根据docker file 编译镜像

比如给到一个Dockerfile 第一步编译镜像 cd /path/to/Dockerfiledocker build -t <DOCKER_IMAGE_NAME> . build 命令编译镜像 -t 镜像名字 . 指dockerfile 所在目录 如果遇到报错 [] Building 0.3s (3/3) FINISHED …...

联邦学习的 AI 大模型微调中,加性、选择性、重参数化和混合微调

联邦学习的 AI 大模型微调中,加性、选择性、重参数化和混合微调 在联邦学习的 AI 大模型微调中,加性、选择性、重参数化和混合微调是不同的操作方式,具体如下: 加性微调 定义与原理:加性微调是在原始模型的基础上添加额外的可训练参数来进行模型调整。这种方式不会改变原…...

android 外挂modem模块实现Telephony相关功能(上网,发短信,打电话)

一.背景 当前模块不支持Telephony相关的功能,例如上网、发短信等功能,就需要外挂另一个模块实现此功能,这就是外挂modem模块实现Telephony功能,此篇主要就是说实现外挂modem模块功能中的Framework层实现逻辑,如下流程是在Android 13中实现的外挂pcie模块的流程 二.ril库相…...

【计算机视觉技术 - 人脸生成】2.GAN网络的构建和训练

GAN 是一种常用的优秀的图像生成模型。我们使用了支持条件生成的 cGAN。下面介绍简单 cGAN 模型的构建以及训练过程。 2.1 在 model 文件夹中新建 nets.py 文件 import torch import torch.nn as nn# 生成器类 class Generator(nn.Module):def __init__(self, nz100, nc3, n…...

数据中台与数据治理服务方案[50页PPT]

本文概述了数据中台与数据治理服务方案的核心要点。数据中台作为政务服务数据化的核心&#xff0c;通过整合各部门业务系统数据&#xff0c;进行建模与加工&#xff0c;以新数据驱动政府管理效率提升与政务服务能力增强。数据治理则聚焦于解决整体架构问题&#xff0c;确保数据…...

LangGraph + Studio 组合拳实战:从零构建一个带‘质检员’的文档处理智能体

LangGraph Studio 组合拳实战&#xff1a;构建带质检环节的文档处理智能体 在数字化转型浪潮中&#xff0c;企业每天需要处理海量文档的翻译、摘要和内容审核工作。传统人工处理不仅效率低下&#xff0c;质量也参差不齐。现在&#xff0c;通过LangGraph框架与LangGraph Studio…...

乙巳马年皇城大门春联生成终端W与嵌入式系统结合:在STM32平台上展示春节祝福

乙巳马年皇城大门春联生成终端W与嵌入式系统结合&#xff1a;在STM32平台上展示春节祝福 春节贴春联是咱们的传统习俗&#xff0c;但你想过让一块小小的开发板也能“写”春联、送祝福吗&#xff1f;最近我琢磨着把网上一个挺火的春联生成工具&#xff0c;和手头的STM32开发板结…...

2026mathorcup妈妈杯数学建模挑战赛B题思路详解

大家好呀&#xff0c;2026年mathorcup妈妈杯数学建模挑战赛今天早上开赛啦&#xff0c;在这里先带来初步的选题建议及思路。 目前团队正在写B题完整论文&#xff0c;后续还会持续更新哈。以下只是简略的图文版初步思路&#xff0c;更详细的选题建议及B题思路完整版讲解视频请移…...

清音听真Qwen3-ASR-1.7B效果惊艳:粤语+英语混合演讲→自动语种切换+术语统一校准

清音听真Qwen3-ASR-1.7B效果惊艳&#xff1a;粤语英语混合演讲→自动语种切换术语统一校准 想象一下这个场景&#xff1a;一位来自广东的工程师&#xff0c;正在用粤语夹杂着大量英语专业术语&#xff0c;向国际团队做技术分享。他的演讲录音里&#xff0c;“呢个API嘅through…...

笔试训练48天:删除公共字符

REAL507 删除公共字符 https://www.nowcoder.com/practice/f0db4c36573d459cae44ac90b90c6212?tpId182&tqId34789&ru/exam/oj 简单 通过率&#xff1a;32.96% 时间限制&#xff1a;1秒 空间限制&#xff1a;32M 知识点Java工程师字符串2017模拟C工程师 描述 输…...

Payment扩展开发:如何自定义支付网关和添加新支付渠道

Payment扩展开发&#xff1a;如何自定义支付网关和添加新支付渠道 【免费下载链接】payment Payment是php版本的支付聚合第三方sdk&#xff0c;集成了微信支付、支付宝支付、招商一网通支付。提供统一的调用接口&#xff0c;方便快速接入各种支付、查询、退款、转账能力。服务端…...

fake2db多数据库支持:一次配置生成MySQL、PostgreSQL、MongoDB测试数据

fake2db多数据库支持&#xff1a;一次配置生成MySQL、PostgreSQL、MongoDB测试数据 【免费下载链接】fake2db create custom test databases that are populated with fake data 项目地址: https://gitcode.com/gh_mirrors/fa/fake2db 在软件开发过程中&#xff0c;测试…...

51万行核心代码一夜“开源”,信仰崩塌:“我不想用Ai了”

点击“开发者技术前线”&#xff0c;选择“星标”让一部分开发者看到未来来源丨开发者技术前线Claude Code 51万行核心代码一夜“开源”&#xff0c;以“AI安全”为信仰的 Anthropic 因一个 .map 文件翻车。随后官方立马修复了这个问题。但一场人为失误引发的连锁反应&#xff…...

Hunyuan-MT-7B开源镜像:Pixel Language Portal与LangChain集成构建翻译Agent

Hunyuan-MT-7B开源镜像&#xff1a;Pixel Language Portal与LangChain集成构建翻译Agent 1. 项目概览 Pixel Language Portal&#xff08;像素语言跨维传送门&#xff09;是一款基于腾讯Hunyuan-MT-7B大模型构建的创新翻译工具。不同于传统翻译软件的呆板界面&#xff0c;它采…...

告别document.querySelector!在Vue3中用ref优雅操作DOM的3个实战场景

告别document.querySelector&#xff01;在Vue3中用ref优雅操作DOM的3个实战场景 在Vue3的生态中&#xff0c;模板ref早已超越了简单的DOM引用工具&#xff0c;成为连接响应式数据与命令式DOM操作的桥梁。许多开发者仍习惯性地在setup中写下document.querySelector——这就像用…...