前言
使用 vite
, typescript
从零开始创建自己的 React,命名为 hypereact
。
起步
通过 vite
创建一个项目,不选择任何框架
sh
pnpm create vite
创建 vite.config.js
文件,添加 esbuild.loader
选项,设置为 tsx
js
export default defineConfig({
esbuild: {
loader: 'tsx',
},
});
createElement 函数
新建 hypereact.ts
,在这个文件实现功能代码。
首先需要一个创建 createElement
函数,在 JSX
转换成 JS
的时候调用它,生成一个具有 type
和 props
和 children
的对象,使用这个对象就能生成真实的 DOM
节点。
这个对象的 type
指的是DOM
节点的类型,props
是节点的属性,children
是一个数组,可以包含多种类型的数据,如果是数字或者字符串这种原始类型的值,就将它设置为 TEXT_ELEMENT
类型。
ts
/// hypereact.ts
const TEXT_ELEMENT = 'TEXT_ELEMENT';
export function createElement(type: string, props: any, ...children: any[]) {
return {
type,
...props,
children: children.map((child) =>
typeof child === 'object' ? child : createTextElement(child),
),
};
}
function createTextElement(text: string | number) {
return {
type: TEXT_ELEMENT,
props: {
nodeValue: text,
children: [],
},
};
}
render 函数
render
函数的作用是将元素渲染到页面上,它接收 element
和 container
两个参数。
首先需要判断 element
的类型,如果是原始类型,就调用 document.createTextNode
创建文本节点,否则根据 element.type
创建对应的 DOM
节点。
然后将 props
属性添加到 DOM
节点上,对 element
的 children
递归调用 render
函数,从而生成完整的节点树。
最后将生成的 DOM
节点添加到 container
里面。
ts
export function render(element, container) {
const dom: HTMLElement =
element.type == TEXT_ELEMENT
? document.createTextNode('')
: document.createElement(element.type);
const isProperty = (key) => key !== 'children';
Object.keys(element.props)
.filter(isProperty)
.forEach((name) => {
if (dom.setAttribute) {
dom.setAttribute(name, element.props[name]);
} else {
dom[name] = element.props[name];
}
});
element.props.children.forEach((child) => render(child, dom));
container.appendChild(dom);
}
使用
导出 Hypereact
对象
ts
/// hypereact.ts
/// ...
const Hypereact = {
createElement,
render,
};
export default Hypereact;
添加 App.tsx
文件,使用 /** @jsx Hypereact.createElement */
注释,告诉构建工具在翻译 JSX
时使用这个函数。
tsx
/// App.tsx
/// import Hypereact from './hypereact';
/// ...
/** @jsx Hypereact.createElement */
const App = (
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} class="logo" alt="Vite logo" />
</a>
<a
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript"
target="_blank"
>
<img src={javascriptLogo} class="logo vanilla" alt="JavaScript logo" />
</a>
<h1>Hello Hypereact!</h1>
<div class="card">
<button id="counter" type="button">
{`count is ${counter}`}
</button>
</div>
<p class="read-the-docs">Click on the Vite logo to learn more</p>
</div>
);
export default App;
在 main.js
中使用 render
函数,App.jsx
的内容被渲染到页面上了。
js
import './style.css';
import App from './App';
import Hypereact from './hypereact';
const container = document.getElementById('app');
Hypereact.render(App, container);
参考
本文完,感谢阅读 🌹