xdm,又要到饭了,又更新代码了!
在现代前端开发中,React 以其高效的组件化和虚拟 DOM 技术广受欢迎。本文将按照以下提纲,详细介绍如何从零实现一个简化版的 React(以下简称 Mini React),帮助读者深入理解其核心原理和实现方法。
为了最大化的考虑广大读者,
- 代码不会使用typescript,
- 代码的实现考虑了所有读者的层次,哪怕你是新手也依然没有什么难度,
- 实现的细节不会包含所有边界情况(mini react的目的让你理解react关键技术实现原理),
- 你如果觉得自己行了,就不要读了,浪费你时间
1.1 创建虚拟 DOM
虚拟 DOM 通过描述 UI 的结构和状态变化,配合 dom diff算法,利用cpu计算能力换取渲染效率(减少不必要的渲染)。
1.1.1 定义一个简单的虚拟 DOM 结构
首先,我们需要定义一个虚拟 DOM(Virtual DOM)的结构。虚拟 DOM 通常是一个 JavaScript 对象,描述了真实 DOM 的结构,包括元素类型、属性和子节点。
function createElement(type, props, ...children) {
return {
type,
props: props || {},
children: children.map(child => typeof child === 'object' ? child : createTextElement(child) ),
};
}
function createTextElement(text) {
return {
type: 'TEXT_ELEMENT',
props: { nodeValue: text },
children: [],
};
}
上述代码中,createElement 函数用于创建一个虚拟 DOM 节点,如果子节点是文本,则通过 createTextElement 转换为特殊的文本节点。
1.1.2 渲染虚拟 DOM 到真实 DOM
接下来,实现将虚拟 DOM 渲染到真实 DOM 的功能。这涉及到将虚拟节点转换为真实的 DOM 元素,并插入到页面中。
function render(vdom, container) {
const dom =
vdom.type === 'TEXT_ELEMENT'
? document.createTextNode('')
: document.createElement(vdom.type);
// 设置属性
Object.keys(vdom.props).forEach(name => {
if (name.startsWith('on')) {
const eventType = name.slice(2).toLowerCase();
dom.addEventListener(eventType, vdom.props[name]);
} else if (name === 'style') {
Object.assign(dom.style, vdom.props[name]);
} else {
dom[name] = vdom.props[name];
}
});
// 递归渲染子节点
vdom.children.forEach(child => render(child, dom));
container.appendChild(dom);
}
render 函数负责将虚拟 DOM 转换为真实 DOM,并处理属性和事件绑定。通过递归调用,可以渲染嵌套的子节点。
2.1 实现组件
组件化是 React 的核心特性之一,Mini React 也需要支持组件的定义和使用。
2.2.1 定义组件的结构
组件可以定义为函数或类。这里,我们以函数组件为例,实现一个简单的组件结构。
function MyComponent(props) {
return createElement('div', { style: { color: 'blue' } }, `Hello, ${props.name}!`);
}
组件接收 props 作为参数,并返回一个虚拟 DOM 节点。这样,组件就可以像普通的虚拟 DOM 节点一样被渲染。
测试我们的代码
<!DOCTYPE html>
<html>
<head>
<title>Mini React 测试</title>
<style>
#app-container {
border: 2px solid #000;
padding: 20px;
margin: 20px;
}
.greeting {
font-size: 20px;
margin: 10px 0;
}
button {
padding: 10px 20px;
font-size: 16px;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
// 定义 createElement 和 createTextElement 函数
function createElement(type, props, ...children) {
return {
type,
props: props || {},
children: children.map(child => typeof child === 'object' ? child : createTextElement(child) ),
};
}
function createTextElement(text) {
return {
type: 'TEXT_ELEMENT',
props: { nodeValue: text },
children: [],
};
}
// 定义 render 函数,支持函数组件
function render(vdom, container) {
// 如果 vdom.type 是函数,调用它以获取子虚拟 DOM
if (typeof vdom.type === 'function') {
const component = vdom.type;
const props = vdom.props;
const childVdom = component(props);
render(childVdom, container);
return;
}
// 创建真实 DOM 节点
const dom =
vdom.type === 'TEXT_ELEMENT'
? document.createTextNode(vdom.props.nodeValue || '')
: document.createElement(vdom.type);
// 设置属性
Object.keys(vdom.props).forEach(name => {
if (name.startsWith('on')) {
const eventType = name.slice(2).toLowerCase();
dom.addEventListener(eventType, vdom.props[name]);
} else if (name === 'style') {
Object.assign(dom.style, vdom.props[name]);
} else {
dom[name] = vdom.props[name];
}
});
// 递归渲染子节点
vdom.children.forEach(child => render(child, dom));
container.appendChild(dom);
}
// 定义一个简单的组件
function Greeting(props) {
return createElement('div', { className: 'greeting' }, `Hello, ${props.name}!`);
}
// 定义另一个组件,包含事件处理
function App() {
return createElement('div', { id: 'app-container' },
createElement('h1', null, '欢迎使用 Mini React!'),
createElement(Greeting, { name: 'Alice' }),
createElement(Greeting, { name: 'Bob' }),
createElement('button', { onClick: () => alert('按钮被点击了!') }, '点击我')
);
}
// 渲染 App 组件到页面上
const container = document.getElementById('root');
const appVdom = createElement(App, null);
render(appVdom, container);
</script>
</body>
</html>
粘贴代码自己测试一下,相信你对虚拟dom以及函数组件的使用以及作用会比之前有更加深入的了解
下一篇简介
这里已经完成了一个简单的组件函数,然而实现的组件函数没有使用任何react 提供的hook。 比如, useState / useEffect。
下一篇,我们将开始自己一个一个实现所有react 18常用的hook, 还有react 19 的新hook,以帮助我们增强对其原理的理解。
如果这样的长度/强度你觉得可以接受,觉得有帮助,可以继续阅读下一篇,实现一个 Mini React:核心功能详解 - useState / useEffect等hook的实现。
如果文章对你有帮助,请点个赞支持一下!
啥也不是,散会。