virtual dom

什么是vdom,为何使用vdom

  • virtual dom,虚拟dom
  • 通过js模拟dom结构
  • 目的是为了减少dom操作,dom操作是浏览器中最耗费性能的事。我们在使用操作dom的时候,比如要将ul中的3个li中的第二个li删除,我们需要删除3个li,再引入2个没删除的li,但这样太耗费性能,我们想要直接删除中间的li,不用操作另外两个,这样dom的变化就更少,性能更高,想要找出哪些dom该操作,哪些不该操作,就要运行很多逻辑,很多运算。在前端3门语言中,只有js是图灵完备语言,可以实现各种逻辑,循环,算法,js运行效率高,所以通过js语言来实现virtual dom
  • 用js模拟出的dom结构需要进行对比才知道哪些需要修改,哪些不要修改。用到diff算法。
  • 用来提升重绘repaint性能。

vdom如何应用,它的核心API是什么

  • vdom可以通过各种开源库来实现,vue中通过snabbdom库来实现的vdom,下面以snabbdom库的使用为例子来应用
  • 核心API:h('<标签名>', {...属性...}, [...子元素...]);
  • h('<标签名>', {...属性...}, '文本节点');
  • patch(container, vnode)
  • patch(vnode, newVnode)

示例代码:

需引入snabbdom库再继续,

var data = [{
                name: '张三',
                age: 18,
                address: '北京'
            },{
                name: '李四',
                age: 19,
                address: '上海'
            }, {
                name: '王五',
                age: 20,
                address: '广州'
            }];

            data.unshift({
                name: '姓名',
                age: '年龄',
                address: '地址'
            });

            var snabbdom = window.snabbdom;

            var patch = snabbdom.init([
                    snabbdom_class,
                    snabbdom_props,
                    snabbdom_style,
                    snabbdom_eventlisteners
                ]);

            var h = snabbdom.h;

            var container = document.getElementById('container');

            var vnode;
            function render(data) {
                var newVnode = h('table', {}, data.map(function(item) {
                    var tds = [];
                    var i;
                    for (i in item) {
                        if (item.hasOwnProperty(i)) {
                            tds.push(h('td', {}, item[i]));            
                        }
                    }
                    return h('tr', {}, tds);
                }));

                if (vnode) { 
                    patch(vnode, newVnode);
                } else {// 初次渲染
                    patch(container, newVnode);
                }

                vnode = newVnode;
            }

            render(data);

            var btn = document.getElementById('btn');

            btn.addEventListener('click', function(e) {
                data[2].name = 'xiaoming';
                data[3].age = 60;
                render(data);
            })

 

diff算法小了解,vdom为何用diff算法,以及实现的核心流程

  • dom操作昂贵,减少dom操作
  • 找出本次dom必须更新的节点来更新,其他的不更新
  • 这个"找出"的过程就需要diff算法。

实现的核心流程分成两种情况,一种是从无到有,一种是新节点替换旧节点,核心流程(只是大概流程,没有细节)如下:

var vnode = {
                        tag: 'ul',
                        attrs: {
                            id: 'idname'
                        },
                        children: [{
                            tag: 'li',
                            attrs: {}
                        },{
                            tag: 'li',
                            attrs: {
                                className: 'item2'
                            },
                            children: []
                        }]
                    };

        // patch(container, vnode); 从无到有
        function createElement(vnode) {
            var tag = vnode.tag;
            var attrs = vnode.attrs || {};
            var children = vnode.children || [];
            if (!tag) {
                return null;
            }
            // 创建元素
            var elem = document.createElement(tag);
            // 属性
            for (var p in attrs) {
                if (attrs.hasOwnProperty(p)) {
                    // 给 elem 添加属性
                    elem.setAttribute(p, attrs[p]);
                }
            }
            // 子元素
            children.forEach(function(childVnode) {
                // if (!childVnode) {
                //     return elem;
                // }
                // 递归调用 createElement 创建子元素
                elem.appendChild(createElement(childVnode));
            })
            return elem;
        }

        document.body.appendChild(createElement(vnode));

        // patch(vnode, newVnode) 新节点替换旧节点
        function updateChildren(vnode, newVnode) {
            var children = vnode.children || [];
            var newChildren = newVnode.children || [];

            children.forEach(function(child, index) {
                var newChild = newChildren[index];
                if (newChild == null) {
                    return;
                }
                if (newChild.tag === child.tag) {
                    updateChildren(child, newChild);
                } else {
                    replaceNode(child, newChild);
                }
            })
        }

 

posted @ 2019-04-08 18:11  骨子里的钟  阅读(351)  评论(0编辑  收藏  举报