vdom

vdom
vdom
virtual dom,虚拟DOM

用JS模拟DOM结构

DOM变化的对比,放在JS层来做(图灵完备语言)

提高重绘性能

1、知识点一

一个简单的dom结构

<ul id='list'>
    <li class='item'>Item 1</li>
    <li class='item'>Item 2</li>
</ul>
用js表示上面Dom结构

{
    tag:'ul',
    attrs: {
        id:'list'
    },
    childern:[
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 1']
        },
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 2']
        }
    ]
}
2、知识点二

设计一个需求场景

将该数据展示成一个表

随便修改一个信息,表格也跟着修改

[
    {
        name:'张三',
        age:'20',
        address:'北京'
    },
     {
        name:'李四',
        age:'21',
        address:'上海'
    },
     {
        name:'王五',
        age:'22',
        address:'广州'
    }
]
用jQuery实现上面场景

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="container"></div>
    <button id="btn-change">change</button>
​
    <script type="text/javascript"                                                                  src="https://cdn.bootcss.com/jquery/3.2.0/jquery.js"></script>
    <script type="text/javascript">
        var data = [
            {
                name: '张三',
                age: '20',
                address: '北京'
            },
            {
                name: '李四',
                age: '21',
                address: '上海'
            },
            {
                name: '王五',
                age: '22',
                address: '广州'
            }
        ]
​
        // 渲染函数
        function render(data) {
            var $container = $('#container')
​
            // 清空容器,重要!!!
            $container.html('')
​
            // 拼接 table
            var $table = $('<table>')
​
            $table.append($('<tr><td>name</td><td>age</td><td>address</td>/tr>'))
            data.forEach(function (item) {
                $table.append($('<tr><td>' + item.name + '</td><td>' + item.age +                                       '</td><td>' + item.address + '</td>/tr>'))
            })
​
            // 渲染到页面
            $container.append($table)
        }
​
        $('#btn-change').click(function () {
            data[1].age = 30
            data[2].address = '深圳'
            // re-render  再次渲染
            render(data)
        })
​
        // 页面加载完立刻执行(初次渲染)
        render(data)
​
    </script>
</body>
</html>
3、知识点三

遇到的问题浏览器为div节点创建了很多的属性,修改DOM是昂贵的

var div=document.createElement('div');
var item,result=''
for(item in div){
    result +=' | '+item;
}
console.log(result);
打印出来的log



DOM 操作是“昂贵”的,js 运行效率高

尽量减少 DOM 操作,而不是“推倒重来”

项目越复杂,影响就越严重

vdom 即可解决这个问题

snabbdom
介绍snabbdom

重做之前的demo

核心API

1、知识点一

用snabbdom模拟上面的vdom

{
    tag:'ul',
    attrs: {
        id:'list'
    },
    childern:[
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 1']
        },
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 2']
        }
    ]
}
var vnode=h('ul#list',{},[
    h('li.item',{},'Item 1'),
    h('li.item',{},'Item 2')
])
官方例子

var snabbdom = require('snabbdom');
var patch = snabbdom.init([ // Init patch function with chosen modules
  require('snabbdom/modules/class').default, // makes it easy to toggle classes
  require('snabbdom/modules/props').default, // for setting properties on DOM elements
  require('snabbdom/modules/style').default, // handles styling on elements with                                                    //support for animations
  require('snabbdom/modules/eventlisteners').default, // attaches event listeners
]);
var h = require('snabbdom/h').default; // helper function for creating vnodes
​
var container = document.getElementById('container');
​
var vnode = h('div#container.two.classes', {on: {click: someFn}}, [
  h('span', {style: {fontWeight: 'bold'}}, 'This is bold'),
  ' and this is just normal text',
  h('a', {props: {href: '/foo'}}, 'I\'ll take you places!')
]);
// Patch into empty DOM element – this modifies the DOM as a side effect
patch(container, vnode);
​
var newVnode = h('div#container.two.classes', {on: {click: anotherEventHandler}}, [
  h('span', {style: {fontWeight: 'normal', fontStyle: 'italic'}}, 'This is now italic type'),
  ' and this is still just normal text',
  h('a', {props: {href: '/bar'}}, 'I\'ll take you places!')
]);
// Second `patch` invocation
patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state
snabbdom的patch函数



2、知识点二

简单demo

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="container"></div>
    <button id="btn-change">change</button>
​
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.js">       </script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.js"></script>
    <script type="text/javascript">
        var snabbdom = window.snabbdom
​
        // 定义 patch
        var patch = snabbdom.init([
            snabbdom_class,
            snabbdom_props,
            snabbdom_style,
            snabbdom_eventlisteners
        ])
​
        // 定义 h
        var h = snabbdom.h
​
        var container = document.getElementById('container')
​
        // 生成 vnode
        var vnode = h('ul#list', {}, [
            h('li.item', {}, 'Item 1'),
            h('li.item', {}, 'Item 2')
        ])
        patch(container, vnode)
​
        document.getElementById('btn-change').addEventListener('click', function () {
            // 生成 newVnode
            var newVnode = h('ul#list', {}, [
                h('li.item', {}, 'Item 1'),
                h('li.item', {}, 'Item B'),
                h('li.item', {}, 'Item 3')
            ])
            patch(vnode, newVnode)
        })
​
    </script>
    
</body>
</html>
重做demo

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="container"></div>
    <button id="btn-change">change</button>
​
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom-class.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom-props.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom-style.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom-eventlisteners.js">       </script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/h.js"></script>
    <script type="text/javascript">
        var snabbdom = window.snabbdom
        // 定义关键函数 patch
        var patch = snabbdom.init([
            snabbdom_class,
            snabbdom_props,
            snabbdom_style,
            snabbdom_eventlisteners
        ])
​
        // 定义关键函数 h
        var h = snabbdom.h
​
        // 原始数据
        var data = [
            {
                name: '张三',
                age: '20',
                address: '北京'
            },
            {
                name: '李四',
                age: '21',
                address: '上海'
            },
            {
                name: '王五',
                age: '22',
                address: '广州'
            }
        ]
        // 把表头也放在 data 中
        data.unshift({
            name: '姓名',
            age: '年龄',
            address: '地址'
        })
​
        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) {
                // re-render
                patch(vnode, newVnode)
            } else {
                // 初次渲染
                patch(container, newVnode)
            }
​
            // 存储当前的 vnode 结果
            vnode = newVnode
        }
​
        // 初次渲染
        render(data)
​
​
        var btnChange = document.getElementById('btn-change')
        btnChange.addEventListener('click', function () {
            data[1].age = 30
            data[2].address = '深圳'
            // re-render
            render(data)
        })
​
    </script>
</body>
</html> 
3、知识点三

snabbdom的h函数两种用法

h(‘<标签名>’,{…属性…},[…子元素…])

h(‘<标签名>’,{…属性…},‘….’)

snabbdom的patch函数两种用法

patch(container,vnode)

patch(vnode, newVnode)

diff算法
1、知识点一

vdom 为何使用 diff 算法

DOM 操作是“昂贵”的,因此尽量减少 DOM 操作

找出本次 DOM 必须更新的节点来更新,其他的不更新

这个“找出”的过程,就需要 diff 算法

patch(vnode, newVnode)

vnode内容

{
    tag:'ul',
    attrs: {
        id:'list'
    },
    childern:[
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 1']
        },
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 2']
        }
    ]
}
newVnode内容

{
    tag:'ul',
    attrs: {
        id:'list'
    },
    childern:[
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 1']
        },
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 222']
        },
        {
            tag:'li',
            attrs:{className:'item'}
            children:['Item 3']
        }
    ]
}
2、知识点二

updateChildren函数

function updateChildren(vnode, newVnode) {
    var children = vnode.children || []
    var newChildren = newVnode.children || []
​
    children.forEach(function (childVnode, index) {
        var newChildVnode = newChildren[index]
        if (childVnode.tag === newChildVnode.tag) {
            // 深层次对比,递归
            updateChildren(childVnode, newChildVnode)
        } else {
            // 替换
            replaceNode(childVnode, newChildVnode)
        }
    })
}
​
function replaceNode(vnode, newVnode) {
    var elem = vnode.elem  // 真实的 DOM 节点
    var newElem = createElement(newVnode)
​
    // 替换
}
createElement函数

function createElement(vnode) {
    var tag = vnode.tag  // 'ul'
    var attrs = vnode.attrs || {}
    var children = vnode.children || []
    if (!tag) {
        return null
    }
​
    // 创建真实的 DOM 元素
    var elem = document.createElement(tag)
    // 属性
    var attrName
    for (attrName in attrs) {
        if (attrs.hasOwnProperty(attrName)) {
            // 给 elem 添加属性
            elem.setAttribute(attrName, attrs[attrName])
        }
    }
    // 子元素
    children.forEach(function (childVnode) {
        // 给 elem 添加子元素
        elem.appendChild(createElement(childVnode))  // 递归
    })
​
    // 返回真实的 DOM 元素
    return elem
}
3、知识点三

diff 实现过程

patch(container, vnode) 和 patch(vnode, newVnode)

createElment

updateChildren

4、知识点四

问题解答

知道什么是 diff 算法,是 linux 的基础命令

vdom 中应用 diff 算法是为了找出需要更新的节点

vdom 实现过程,createElement 和 updateChildren

与核心函数 patch 的关系

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值