- 8.3 模拟渲染过程
- 8.3.1 createVnode
- 8.3.2 createElement
- 8.3.3 setAttr
8.3 模拟渲染过程
接下来需要创建另一个类模拟将render函数转换为Vnode,并将Vnode渲染为真实DOM的过程,我们将这个类定义为Vn,Vn具有两个基本的方法createVnode, createElement, 分别实现创建虚拟Vnode,和创建真实DOM的过程。
8.3.1 createVnode
createVnode模拟Vue中render函数的实现思路,目的是将数据转换为虚拟的Vnode,先看具体的使用和定义。
// index.html<script src="diff.js"><script>// 创建Vnodelet createVnode = function() {let _c = vn.createVnode;return _c('div', { attrs: { id: 'test' } }, arr.map(a => _c(a.tag, {}, a.text)))}// 元素内容结构let arr =[{tag: 'i',text: 2}, {tag: 'span',text: 3}, {tag: 'strong',text: 4}]</script>// diff.js(function(global) {class Vn {constructor() {}// 创建虚拟VnodecreateVnode(tag, data, children) {return new VNode(tag, data, children)}}global.vn = new Vn()}(this))
这是一个完整的Vnode对象,我们已经可以用这个对象来简单的描述一个DOM节点,而createElement就是将这个对象对应到真实节点的过程。最终我们希望的结果是这样的。
Vnode对象

渲染结果

8.3.2 createElement
渲染真实DOM的过程就是遍历Vnode对象,递归创建真实节点的过程,这个不是本文的重点,所以我们可以粗糙的实现。
class Vn {createElement(vnode, options) {let el = options.el;if(!el || !document.querySelector(el)) return console.error('无法找到根节点')let _createElement = vnode => {const { tag, data, children } = vnode;const ele = document.createElement(tag);// 添加属性this.setAttr(ele, data);// 简单的文本节点,只要创建文本节点即可if (util._isPrimitive(children)) {const testEle = document.createTextNode(children);ele.appendChild(testEle)} else {// 复杂的子节点需要遍历子节点递归创建节点。children.map(c => ele.appendChild(_createElement(c)))}return ele}document.querySelector(el).appendChild(_createElement(vnode))}}
8.3.3 setAttr
setAttr是为节点设置属性的方法,利用DOM原生的setAttribute为每个节点设置属性值。
class Vn {setAttr(el, data) {if (!el) returnconst attrs = data.attrs;if (!attrs) return;Object.keys(attrs).forEach(a => {el.setAttribute(a, attrs[a]);})}}
至此一个简单的 数据 -> Virtual DOM => 真实DOM的模型搭建成功,这也是数据变化、比较、更新的基础。
