日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

JavaScript从初级往高级走系列————Virtual Dom

發布時間:2025/3/17 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JavaScript从初级往高级走系列————Virtual Dom 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
原文博客地址:https://finget.github.io/2018/05/22/virtualDom/

什么是虛擬DOM

  • 用JS模擬DOM結構
  • DOM變化的對比,放在JS層來做(圖靈完備語言)
  • 提高重繪性能

重繪和回流

頁面渲染過程:

  • 當render tree中的一部分(或全部)因為元素的規模尺寸,布局,隱藏等改變而需要重新構建。這就稱為回流(reflow)。
  • 當render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風格,而不會影響布局的,比如background-color。則就叫稱為重繪。

模擬虛擬DOM

<ul id="list"><li class="item">Item 1</li><li class="item">Item 2</li> </ul> // js模擬虛擬DOM {tag: 'ul',attrs:{id: 'list'},children:[{tag: 'li',attrs: {className: 'item'},children: ['Item 1']},{tag: 'li',attrs: {className: 'item'},children: ['Item 2']}] } <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Document</title><script src="https://cdn.bootcss.com/jquery/2.2.0/jquery.min.js"></script> </head> <body><div id="container"></div><button id="btn-change">change</button><script>var data = [{name: '張三',age: '20',address: '北京'},{name: '王五',age: '22',address: '成都'},{name: '李四',age: '21',address: '上海'}]// 渲染函數function render(data) {var $container = $('#container');// 清空容器,重要!!!$container.html('');// 拼接 tablevar $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>

雖然只改變了兩個數據,但是整個table都閃爍了(回流&重繪)

  • DOM操作是‘昂貴’的,js運行效率高
  • 盡量減少DOM操作,盡量減少回流重繪

虛擬DOM如何應用,核心API是什么

介紹 snabbdom

snabbdom GitHub地址

官網例子:

var snabbdom = require('snabbdom'); var patch = snabbdom.init([ // Init patch function with chosen modulesrequire('snabbdom/modules/class').default, // makes it easy to toggle classesrequire('snabbdom/modules/props').default, // for setting properties on DOM elementsrequire('snabbdom/modules/style').default, // handles styling on elements with support for animationsrequire('snabbdom/modules/eventlisteners').default, // attaches event listeners ]); var h = require('snabbdom/h').default; // helper function for creating vnodesvar container = document.getElementById('container');// h函數生成一個虛擬節點 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); // 把vnode加入到container中// 數據改變,重新生成一個newVnode 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 // 將newVnode更新到之前的vnode中,從而更新視圖 patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state

snabbdom h 函數

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

snabbdom patch 函數

var vnode = h('ul#list',{},[h('li.item',{},'Item 1'),h('li.item',{},'Item 2') ]) var container = document.getElementById('container'); patch(container, vnode);// 模擬改變 var btnChange = document.getElementById('btn-change'); btnChange.addEventListener('click',function(){var newVnode = h('ul#list',{},[h('li.item',{},'Item 111'),h('li.item',{},'Item 222'),h('li.item',{},'Item 333')])patch(vnode, newVnode); })

snabbdom例子

<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Document</title><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.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/h.js"></script><script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.js"></script> </head> <body><div id="container"></div><button id="btn-change">change</button><script>var snabbdom = window.snabbdom;// 定義 patchvar patch = snabbdom.init([snabbdom_class,snabbdom_props,snabbdom_style,snabbdom_eventlisteners])// 定義 hvar h = snabbdom.h;var container = document.getElementById('container');// 生成 vnodevar vnode = h('ul#list',{},[h('li.item',{},'Item 1'),h('li.item',{},'Item 2')])patch(container, vnode);// 模擬數據改變var btnChange = document.getElementById('btn-change');btnChange.addEventListener('click',function(){var newVnode = h('ul#list',{},[h('li.item',{},'Item 1'),h('li.item',{},'Item 222'),h('li.item',{},'Item 333')])patch(vnode, newVnode);})</script> </body> </html>

看圖,只有修改了的數據才進行了刷新,減少了DOM操作,這其實就是vnode與newVnode對比,找出改變了的地方,然后只重新渲染改變的

重做之前的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;// 定義關鍵函數 patchvar patch = snabbdom.init([snabbdom_class,snabbdom_props,snabbdom_style,snabbdom_eventlisteners]);// 定義關鍵函數 hvar h = snabbdom.h;// 原始數據var data = [{name: '張三',age: '20',address: '北京'},{name: '王五',age: '22',address: '成都'},{name: '李四',age: '21',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-renderpatch(vnode, newVnode);} else {// 初次渲染patch(container, newVnode);}// 存儲當前的 vnode 結果vnode = newVnode;}// 初次渲染render(data)var btnChange = document.getElementById('btn-change')btnChange.addEventListener('click', function () {data[1].age = 30data[2].address = '深圳'// re-renderrender(data)})</script> </body> </html>

核心API

  • h('<標簽名>',{...屬性...},[...子元素...])
  • h('<標簽名>',{...屬性...},'...')
  • patch(container,vnode)
  • patch(vnode,newVnode)

簡單介紹 diff 算法

什么是 diff 算法

這里有兩個文本文件:

借用git bash中 diff 命令可以比較兩個文件的區別:

在線diff工具

虛擬DOM ---> DOM

// 一個實現流程,實際情況還很復雜 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 attrNamefor (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 }

vnode ---> newVnode

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);// 替換 }

最后

創建了一個前端學習交流群,感興趣的朋友,一起來嗨呀!

總結

以上是生活随笔為你收集整理的JavaScript从初级往高级走系列————Virtual Dom的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。