使用js对象简单模拟虚拟dom的渲染
vnode0数据:
var vnode0 = {
tag: 'ul',
props: {
id :'ul1',
style: {color: 'red', fontSize: '14px'},
'data-index': 0,
},
on: {
click:()=>console.log('click ul')
},
children: [
{
tag: 'li',
children: 'a'
},
{
tag: 'li',
props: {
className: 'list-item'
},
on: {
click(e) {
// e.stopPropagation();
console.log('click li')
}
},
children: [
{
tag: 'a',
children: '好好学习'
}
]
},
]
}
一. 用js模拟dom结构:
1. 核心方法: document.createElement + document.createDocumentFragment(推荐):
let timer2 = new Date().getTime()
const vDom = document.createDocumentFragment()
function render2(vnode) {
const dom = document.createElement(vnode.tag)
const props = _.get(vnode, 'props')
if(!_.isEmpty(props)) {
for(let key in props) {
const item = _.get(props, [key]);
if(key === 'className') {
dom.class = item
}
else {
dom.setAttribute(key, item)
}
}
}
if(_.get(vnode, 'props.style')) {
const styleObj = vnode.props.style
let styleStr = ''
for(let key in styleObj) {
const item = styleObj[key]
styleStr+= `${key.replace(/[A-Z]/g, str=>'-'+str.toLowerCase())}:${item};`
}
dom.style = styleStr
}
if(_.get(vnode, 'on')) {
for(let key in vnode.on) {
const item = vnode.on[key]
dom[`on${key}`]=item
}
}
const children = _.get(vnode, 'children');
if(typeof children === 'string') {
dom.innerText = children
}
if(Array.isArray(children) && !_.isEmpty(children)) {
for(let item of children) {
const dom0 = render2(item);
dom.appendChild(dom0)
}
}
vDom.appendChild(dom)
console.log('render2时间', new Date().getTime()-timer2)
return dom
}
render2(vnode0)
document.getElementById('box').appendChild(vDom)
2. 使用innerHTML
注意事项:
1) 不适合需要在html字符串上加onclick等事件的情况, 要加等到页面渲染完成再需要找到那些dom元素自行添加
2) 适合那些直接把click事件加到父元素上的(比如box.addEventListener('click', e=>{...}), 通过冒泡获取子元素的属性key和属性值, 进行判断处理业务
3) 数据量大的时候比较消耗性能
let timer1 = new Date().getTime()
let str = '';
function render(vnode) {
str += `<${vnode.tag}`;
if (_.get(vnode, 'props.id')) {
str += ` id="${vnode.props.id}"`
}
if (_.get(vnode, 'props.className')) {
str += ` class="${vnode.props.className}"`
}
if (_.get(vnode, 'props.style')) {
const styleObj = vnode.props.style
let styleStr = ''
for (let key in styleObj) {
const item = styleObj[key]
styleStr += `${key.replace(/[A-Z]/g, str => '-' + str.toLowerCase())}:${item};`
}
str += ` style="${styleStr}"`
}
if (_.get(vnode, 'on')) {
for (let key in vnode.on) {
const item = vnode.on[key]
// onclick事件不能添加...
}
}
const children = vnode.children;
if (typeof children === 'string') {
str += `>${children}`
}
if (Array.isArray(children) && !_.isEmpty(children)) {
str += '>'
children.forEach(element => {
render(element)
});
}
str += `</${vnode.tag}>`;
console.log('render时间', new Date().getTime() - timer1)
return str
}
render(vnode0);
document.getElementById('box').innerHTML = str;