8.3 模拟渲染过程

接下来需要创建另一个类模拟将render函数转换为Vnode,并将Vnode渲染为真实DOM的过程,我们将这个类定义为Vn,Vn具有两个基本的方法createVnode, createElement, 分别实现创建虚拟Vnode,和创建真实DOM的过程。

8.3.1 createVnode

createVnode模拟Vuerender函数的实现思路,目的是将数据转换为虚拟的Vnode,先看具体的使用和定义。

  1. // index.html
  2. <script src="diff.js">
  3. <script>
  4. // 创建Vnode
  5. let createVnode = function() {
  6. let _c = vn.createVnode;
  7. return _c('div', { attrs: { id: 'test' } }, arr.map(a => _c(a.tag, {}, a.text)))
  8. }
  9. // 元素内容结构
  10. let arr =
  11. [{
  12. tag: 'i',
  13. text: 2
  14. }, {
  15. tag: 'span',
  16. text: 3
  17. }, {
  18. tag: 'strong',
  19. text: 4
  20. }]
  21. </script>
  22. // diff.js
  23. (function(global) {
  24. class Vn {
  25. constructor() {}
  26. // 创建虚拟Vnode
  27. createVnode(tag, data, children) {
  28. return new VNode(tag, data, children)
  29. }
  30. }
  31. global.vn = new Vn()
  32. }(this))

这是一个完整的Vnode对象,我们已经可以用这个对象来简单的描述一个DOM节点,而createElement就是将这个对象对应到真实节点的过程。最终我们希望的结果是这样的。

Vnode对象

8.3 模拟渲染过程 - 图1

渲染结果

8.3 模拟渲染过程 - 图2

8.3.2 createElement

渲染真实DOM的过程就是遍历Vnode对象,递归创建真实节点的过程,这个不是本文的重点,所以我们可以粗糙的实现。

  1. class Vn {
  2. createElement(vnode, options) {
  3. let el = options.el;
  4. if(!el || !document.querySelector(el)) return console.error('无法找到根节点')
  5. let _createElement = vnode => {
  6. const { tag, data, children } = vnode;
  7. const ele = document.createElement(tag);
  8. // 添加属性
  9. this.setAttr(ele, data);
  10. // 简单的文本节点,只要创建文本节点即可
  11. if (util._isPrimitive(children)) {
  12. const testEle = document.createTextNode(children);
  13. ele.appendChild(testEle)
  14. } else {
  15. // 复杂的子节点需要遍历子节点递归创建节点。
  16. children.map(c => ele.appendChild(_createElement(c)))
  17. }
  18. return ele
  19. }
  20. document.querySelector(el).appendChild(_createElement(vnode))
  21. }
  22. }

8.3.3 setAttr

setAttr是为节点设置属性的方法,利用DOM原生的setAttribute为每个节点设置属性值。

  1. class Vn {
  2. setAttr(el, data) {
  3. if (!el) return
  4. const attrs = data.attrs;
  5. if (!attrs) return;
  6. Object.keys(attrs).forEach(a => {
  7. el.setAttribute(a, attrs[a]);
  8. })
  9. }
  10. }

至此一个简单的 数据 -> Virtual DOM => 真实DOM的模型搭建成功,这也是数据变化、比较、更新的基础。