跳到主要内容

根据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 和调度。