• 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);
                    }
                })
            }
  • 相关阅读:
    python经典笔试、面试题-01
    python-实现简单区块链
    灰狼优化算法——MATLAB
    Linux 2.6内核Makefile浅析
    自动化专业学习路线不再迷茫
    进程、线程与处理器的调度
    关于线程与进程以及线程和进程控制块理解进程和线程的概念
    Linux下nm和ldd 命令
    bss、data和rodata区别与联系
    open与fopen的区别
  • 原文地址:https://www.cnblogs.com/zhonghonglin1997/p/10667989.html
Copyright © 2020-2023  润新知