虚拟DOM转真实DOM
实现 render 函数将虚拟DOM转换为真实DOM
问题
实现一个 render 函数,将虚拟 DOM 对象转换为真实 DOM 节点。
解答
虚拟 DOM 结构
// 虚拟 DOM 的数据结构
const vnode = {
tag: 'div',
props: {
id: 'app',
class: 'container'
},
children: [
{
tag: 'h1',
props: { style: 'color: red' },
children: ['Hello']
},
{
tag: 'p',
props: null,
children: ['World']
}
]
}
实现 render 函数
function render(vnode) {
// 处理文本节点
if (typeof vnode === 'string' || typeof vnode === 'number') {
return document.createTextNode(vnode)
}
const { tag, props, children } = vnode
// 创建元素
const el = document.createElement(tag)
// 设置属性
if (props) {
for (const key in props) {
const value = props[key]
if (key.startsWith('on')) {
// 事件绑定:onClick -> click
const eventName = key.slice(2).toLowerCase()
el.addEventListener(eventName, value)
} else if (key === 'style' && typeof value === 'object') {
// style 对象处理
Object.assign(el.style, value)
} else {
// 普通属性
el.setAttribute(key, value)
}
}
}
// 递归处理子节点
if (children) {
children.forEach(child => {
el.appendChild(render(child))
})
}
return el
}
完整示例
const vnode = {
tag: 'div',
props: {
id: 'app',
class: 'container'
},
children: [
{
tag: 'h1',
props: {
style: { color: 'red', fontSize: '24px' }
},
children: ['Hello Virtual DOM']
},
{
tag: 'button',
props: {
onClick: () => alert('clicked!')
},
children: ['Click me']
},
{
tag: 'ul',
props: null,
children: [
{ tag: 'li', props: null, children: ['Item 1'] },
{ tag: 'li', props: null, children: ['Item 2'] },
{ tag: 'li', props: null, children: ['Item 3'] }
]
}
]
}
// 渲染到页面
const realDOM = render(vnode)
document.body.appendChild(realDOM)
支持 Fragment 和空节点
function render(vnode) {
// 空节点
if (vnode == null || typeof vnode === 'boolean') {
return document.createTextNode('')
}
// 文本节点
if (typeof vnode === 'string' || typeof vnode === 'number') {
return document.createTextNode(vnode)
}
// 数组(Fragment)
if (Array.isArray(vnode)) {
const fragment = document.createDocumentFragment()
vnode.forEach(child => {
fragment.appendChild(render(child))
})
return fragment
}
const { tag, props, children } = vnode
const el = document.createElement(tag)
// 设置属性
if (props) {
for (const key in props) {
setProp(el, key, props[key])
}
}
// 递归子节点
if (children) {
children.forEach(child => {
el.appendChild(render(child))
})
}
return el
}
function setProp(el, key, value) {
if (key.startsWith('on')) {
const eventName = key.slice(2).toLowerCase()
el.addEventListener(eventName, value)
} else if (key === 'style') {
if (typeof value === 'object') {
Object.assign(el.style, value)
} else {
el.setAttribute('style', value)
}
} else if (key === 'className') {
el.setAttribute('class', value)
} else {
el.setAttribute(key, value)
}
}
关键点
- 虚拟 DOM 是描述 DOM 结构的 JS 对象,包含
tag、props、children - 文本节点用
createTextNode,元素节点用createElement - 属性需要区分处理:事件(on 开头)、style(对象或字符串)、普通属性
- 子节点递归调用
render处理 - 可扩展支持 Fragment、空节点、组件等场景
目录