一、什么是虚拟dom
vnode 就是一个普通的javascript对象,用来描述我们的真实dom,举一个🌰哈。有一个<div class='extra'>我是div标签<div>
,其对应的vnode为
javascript
const vnode = {
tagName: 'div',
props: {class:'extra'},
children:['我是div标签']
}
我们发现上面的vnode有tagName,props,children。下面写一个render
函数,将上面的vnode渲染为真实dom
二、render 的实现
2.1 简单版本的实现
javascript
const render = (vnode) => {
let dom = document.createElement(vnode.tagName)
if(vnode.props){
Object.keys(vnode.props).forEach((key) => {
let value = vnode.props[key]
dom.setAttribute(key, value)
})
}
if(vnode.children) {
dom.innerText = vnode.children[0]
}
return dom
}
2.2 children 是多个且嵌套的实现
html
<div class='layout-1'>
我是第一个层
<div class="layout-2">
我是第二层
<div class="layout-3">
我是第三层
</div>
</div>
</div>
上面结构对应的vnode如下
javascript
const vnode = {
tagName:"div",
props:{class:'layout-1'},
children:[
'我是第一个层',
{
tagName:'div',
props: {class: 'layout-2'},
children:[
'我是第二层',
{
tagName:"div",
props:{class:"layout-3"},
children:[
'我是第三层'
]
}
]
}
]
}
将上面的vnode渲染为真实dom,只需要递归操作就可以了。代码如下
javascript
const render = (vnode) => {
if (typeof vnode == 'string') {
return document.createTextNode(vnode)
}
let dom = document.createElement(vnode.tagName)
if (vnode.props) {
Object.keys(vnode.props).forEach((key) => {
let value = vnode.props[key]
dom.setAttribute(key, value)
})
}
vnode.children &&
vnode.children.forEach((element) => {
dom.appendChild(render(element))
})
return dom
}
上面这样我们就完成啦。当有children我们递归的去render,然后添加到child中,就可以完成嵌套的渲染了。
2.3 组件化渲染
我们日常开发用的框架Vue、React等框架,都是组件化开发,这些框架会讲我们写的jsx或者模板编译为渲染函数,最后调用这个渲染函数生成vnode。所以如果 vnode 是以function时只需要调用然后再render即可。
javascript
let vnode = {
tagName: 'ul',
props: { class: 'list' },
children: [
{
tagName: 'li',
children: ['item1'],
},
{
tagName: 'li',
children: ['item1'],
},
() => ({
tagName: 'h1',
children: [
{
tagName: 'div',
children: ['h1-div1'],
},
{
tagName: 'div',
children: ['h1-div2'],
},
],
}),
],
}
render 实现
javascript
const render = (vnode) => {
if (typeof vnode === 'function') {
vnode = vnode()
}
if (typeof vnode == 'string') {
return document.createTextNode(vnode)
}
let dom = document.createElement(vnode.tagName)
if (vnode.props) {
Object.keys(vnode.props).forEach((key) => {
let value = vnode.props[key]
dom.setAttribute(key, value)
})
}
vnode.children &&
vnode.children.forEach((element) => {
dom.appendChild(render(element))
})
return dom
}
上面我们就实现了一个简单的渲染器哈。可以将vnode渲染为真实dom。