仿VUE实现DOM转换为虚拟DOM,虚拟DOM转换为DOM

2,320 阅读2分钟

任何好学的技能都不值钱,加油!

下面的代码我自己觉得是挺难的,不过孰能生巧,加油!

将真实DOM转换为虚拟DOM

为什么要使用虚拟DOM?

1、提高性能(直接在页面中操作DOM,页面要不断的去更新)

2、虚拟DOM所有的操作都在内存中进行

代码
<div id="root">
    <div class="c1">
        <div>hello1</div>
			<div>hello2</div>
			<div>hello3</div>            
			<ul class="u1">
    			<li>1</li>
                 <li>2</li>
                 <li>3</li>
			</ul>
		</div>
	</div>
</div>

class VNode {
    constructor(tag,data,value,type) {
        this._tag = tag && tag.toLowerCase();
        this._data = data;
        this._value = value;
        this._type = type;
        this.children = [];               
    }
    appendChild(vnode) {
        this.children.push(vnode);
    }
 }
关于上面👆代码中class介绍
  • ES6中的类,实质上就是一个函数
  • 类自身指向的就是构造函数
  • 类其实就是构造函数的另一种写法
class Person2 {
  // 用constructor构造方法接收参数
  constructor(name, age) {
    this.name = name; // this代表的是实例对象
    this.age = age;
  }
  // 类的方法,此处不能加function
  say() {
    return "My name is " + this.name + ", I'm " + this.age + " years old."
  }
}
var obj = new Person2("Coco", 26);
console.log(obj.say()); // My name is Coco, I'm 26 years old.

构造函数的prototype属性,在ES6的class中依然存在:

constructor方法是类的构造函数的默认方法 new生成对象实例时,自动调用该方法

class Box {
  constructor() {
    console.log("自动调用constructor方法"); // 实例化对象时,该行代码自动执行
  }
}
var obj = new Box();
DOM转换成虚拟DOM最关键的代码
/**
        * 将DOM转换为虚拟DOM(这里使用的是递归的方法,但是在真正的vue源码中使用的 栈结构,使用栈存储 )
        */

       function getVNode(node) {
            /**
             * 1、获取到节点的类型,判断是元素节点还是文本节点 1 元素节点 3 文本节点
             *    - 元素节点
             *      + 获取到元素的 name 与 attr
             *      + 获取到的属性是一个伪数组,将伪书组转换为对象
             *    - 文本节点    
            */
           let nodeType = node.nodeType;
           
           let _vnode = null;
           if(nodeType === 1) { // 元素节点              
                let nodeName = node.nodeName;
                
                let attrs = node.attributes;
                let attrObj = {};    
                for(let i = 0;i<attrs.length;i++) {
                   attrObj[attrs[i].nodeName] = attrs[i].nodeValue;
                }
                // 元素节点没有 value
                _vnode = new VNode(nodeName,attrObj,undefined,nodeType); 

                // 有子元素的情况
                let childNodes = node.childNodes;
                for(let i = 0; i < childNodes.length; i++ ) {
                    let childNode = getVNode(childNodes[i]);
                    _vnode.appendChild(childNode);
                }

           } else if(nodeType === 3) { // 文本节点没有标签与属性
               _vnode = new VNode(undefined,undefined,node.nodeValue,nodeType); 
           }
           return _vnode;
       }
使用
let root = document.querySelector('#root');
let vnode = getVNode(root);
console.log(vnode);

将虚拟DOM转换为DOM

 /*
         将虚拟DOM转化为DOM   

         递归会影响性能,
         真正的vue源码使用的是 vue + 栈 数据类型
       */
        function parse(vnode) {
            let _node = null;
            
            let type = vnode._type;
            if(type === 1 ) { // 元素节点
                _node = document.createElement(vnode._tag);
                // 属性
                let _data = vnode._data;
                // console.log(Object.keys(_data)); 得到一个使用key组成的数据
                Object.keys(_data).forEach(key => {
                    let attrName = key;
                    let attrValue = _data[key];
                    _node.setAttribute(attrName,attrValue);
                })
                
                // 含有子节点
                let children = vnode.children;
                children.forEach(item => {
                    let childNode = parse(item);
                    _node.appendChild(childNode);
                })
                
            } else if(type === 3) { // 文本节点
                return document.createTextNode(vnode._value); // 创建一个文本节点
            }
            return _node;
        }
        let $dom = parse(vnode);
        console.log($dom);

大家要加油哦!