日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

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

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

什么是虛擬DOM

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

重繪和回流

頁面渲染過程:

  • 當(dāng)render tree中的一部分(或全部)因?yàn)樵氐囊?guī)模尺寸,布局,隱藏等改變而需要重新構(gòu)建。這就稱為回流(reflow)。
  • 當(dāng)render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風(fēng)格,而不會(huì)影響布局的,比如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: '上海'}]// 渲染函數(shù)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);})// 頁面加載完立刻執(zhí)行(初次渲染)render(data);</script> </body> </html>

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

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

虛擬DOM如何應(yīng)用,核心API是什么

介紹 snabbdom

snabbdom GitHub地址

官網(wǎng)例子:

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函數(shù)生成一個(gè)虛擬節(jié)點(diǎn) 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中// 數(shù)據(jù)改變,重新生成一個(gè)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 函數(shù)

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 函數(shù)

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);// 模擬數(shù)據(jù)改變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>

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

重做之前的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;// 定義關(guān)鍵函數(shù) patchvar patch = snabbdom.init([snabbdom_class,snabbdom_props,snabbdom_style,snabbdom_eventlisteners]);// 定義關(guān)鍵函數(shù) hvar h = snabbdom.h;// 原始數(shù)據(jù)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')// 渲染函數(shù)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);}// 存儲(chǔ)當(dāng)前的 vnode 結(jié)果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('<標(biāo)簽名>',{...屬性...},[...子元素...])
  • h('<標(biāo)簽名>',{...屬性...},'...')
  • patch(container,vnode)
  • patch(vnode,newVnode)

簡(jiǎn)單介紹 diff 算法

什么是 diff 算法

這里有兩個(gè)文本文件:

借用git bash中 diff 命令可以比較兩個(gè)文件的區(qū)別:

在線diff工具

虛擬DOM ---> DOM

// 一個(gè)實(shí)現(xiàn)流程,實(shí)際情況還很復(fù)雜 function createElement(vnode) {var tag = vnode.tag // 'ul'var attrs = vnode.attrs || {}var children = vnode.children || []if (!tag) {return null}// 創(chuàng)建真實(shí)的 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)) // 遞歸})// 返回真實(shí)的 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) {// 深層次對(duì)比,遞歸updateChildren(childVnode, newChildVnode);} else {// 替換replaceNode(childVnode, newChildVnode);}}) }function replaceNode(vnode, newVnode) {var elem = vnode.elem; // 真實(shí)的 DOM 節(jié)點(diǎn)var newElem = createElement(newVnode);// 替換 }

最后

創(chuàng)建了一個(gè)前端學(xué)習(xí)交流群,感興趣的朋友,一起來嗨呀!

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。