• 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模拟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的模型搭建成功,这也是数据变化、比较、更新的基础。