根据jsx写出vnode和render函数
面试速答
- 这题本质是在考你是否真的理解 JSX -> vnode -> 真实 DOM 这条链路。
- 以下示例不是 React 源码,而是一个帮助面试表达的极简实现。
- 核心思路是:
- JSX 先变成对象结构
render递归把对象结构转成真实 DOM
先把 JSX 改写成 vnode
假设 JSX 是:
const app = (
<div id="app">
<span>Hello</span>
<button>Click</button>
</div>
)
它可以被理解成下面这种 vnode:
const app = {
type: 'div',
props: {
id: 'app',
children: [
{
type: 'span',
props: {
children: 'Hello',
},
},
{
type: 'button',
props: {
children: 'Click',
},
},
],
},
}
这里的重点不是字段名必须一模一样,而是你要表达出 3 个信息:
- 节点类型
type - 属性
props - 子节点
children
一个极简 h 函数
function h(type: string, props: Record<string, unknown> | null, ...children: unknown[]) {
return {
type,
props: {
...(props || {}),
children,
},
}
}
这样:
h('div', {id: 'app'}, h('span', null, 'Hello'))
就能产出类似 vnode。
一个极简 render 函数
type VNode =
| string
| number
| {
type: string
props: {
[key: string]: unknown
children?: VNode[]
}
}
function render(vnode: VNode, container: HTMLElement) {
if (typeof vnode === 'string' || typeof vnode === 'number') {
container.appendChild(document.createTextNode(String(vnode)))
return
}
const el = document.createElement(vnode.type)
const {children = [], ...restProps} = vnode.props
Object.entries(restProps).forEach(([key, value]) => {
if (value != null) {
el.setAttribute(key, String(value))
}
})
children.forEach(child => render(child, el))
container.appendChild(el)
}
面试里还要主动补一句
真实 React 远不止这个程度,它还会处理:
- 函数组件
- 事件绑定
- Diff
- Fiber 调度
- 属性特殊处理
- 更新而不是只挂载
所以这题通常只要求你把“基本思路”写出来。
典型题标准答法
问:这题的核心考点是什么?
答:不是手写完整 React,而是证明你理解 JSX 最终会编译成对象树,再由渲染器把对象树递归转换成真实 DOM。
易错点
- 直接把 JSX 当字符串处理。
- vnode 结构里漏掉
children。 render只会处理元素节点,不会处理文本节点。
速记要点
- JSX 先变 vnode,再由 render 递归挂载。
- vnode 至少要能表达 type、props、children。
- 真正 React 还会在这之上叠加 Diff 和调度。