Virtual DOM (Virtual DOM)
What is the vdom
- It’s called the virtual DOM, not the real DOM
- Use JS to simulate the DOM structure
- Comparison of DOM variations, done in the JS layer (Turing-complete language)
- Use VDOM to improve redraw performance
Turing-complete languages: languages that process logic, implement algorithms, use judgments, use recursion, etc
Why vDOM
- DOM manipulation is expensive and can affect page performance
Example: jquery renders a table and changes the contents of the table
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Jquery emulates the virtual DOM</title>
</head>
<body>
<div id="container"></div>
<button id="change-btn">change</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
var data = [{
name: 'Joe'.age: 20.address: 'Beijing'
}, {
name: 'bill'.age: 21.address: 'Shanghai'
}, {
name: 'Pockmarked seed'.age: 30.address: 'nanjing'
}]
// Render function
function render(data) {
var $container = $('#container')
// Empty the container
$container.html(' ')
/ / stitching table
var $table = $('<table>')
$table.append($('<tr><td>name</td><td>age</td><td>address</td></tr>'))
data.forEach(value= > {
$table.append($('<tr><td>'+value.name+'</td><td>'+value.age+'</td><td>'+value.address+'</td></tr>'))});// Render to the page, and render elements to the page after they have been added to help provide performance
$container.append($table)
}
$('#change-btn').on('click'.function () {
data[1].age = 23;
data[2].address = 'shenzhen';
// Render again
render(data)
})
// First render, executed immediately after the page loads
render(data)
</script>
</body>
</html>
Copy the code
When the table is generated according to the data or the content of the table is modified, the container carrying the table will be completely emptied, and then the good table append will be generated again. Such operation has a great loss on the page performance. When making a large project, frequent DOM operation will make the project run more and more card, so the VDOM is derived
How does VDOM work
Snabbdom introduction
In VUe2.0 above, VDOM is implemented based on SNabbDOM
Snabbdom core API
-
H function, simulate vnode, pass three parameters
- The type and name of the first argument, DOM
- The second parameter, DOM, needs to add the inline style, bound events, and built-in properties
- The content of the third argument, DOM
-
Patch function, two parameters, two ways to use
- The first is to add elements to a blank DOM
- The first parameter is a blank node
- The second argument is that the h function generates the element to be added
- The second use is to update the content of the existing DOM
- The first argument is that the h function generates the old node
- The second argument is the newly generated node of the h function, which is used to update the old node
- The first is to add elements to a blank DOM
Snabbdom case
Example: Use snabbDOM to rewrite the jQuery rendering table example above
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>snabbdom</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>
var data = [{
name: 'name'.age: 'age'.address: 'address'
}, {
name: 'Joe'.age: 20.address: 'Beijing'
}, {
name: 'bill'.age: 21.address: 'Shanghai'
}, {
name: 'Pockmarked seed'.age: 30.address: 'nanjing'
}]
var snabbdom = window.snabbdom
/ / define the patch
var patch = snabbdom.init([
snabbdom_class,
snabbdom_props,
snabbdom_style,
snabbdom_eventlisteners
])
/ / define h
var h = snabbdom.h
var container = document.getElementById('container')
var changeBtn = document.getElementById('btn-change')
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)
}))
/** * Determine if it is the first time to render the vDOM * if it is the first time to add vNode to the blank element * if it is not the first time to compare the newly generated vnode with the old vnode, modify the changes */
if (vnode) {
patch(vnode, newVnode)
} else {
patch(container, newVnode)
}
// Assign the generated vNode to oldVnode after each rendering
vnode = newVnode
}
changeBtn.addEventListener('click'.function () {
data[1].age = 30
data[2].address = 'shenzhen'
render(data)
})
// First render
render(data)
</script>
</body>
</html>
Copy the code
Create vDOM with SNabbDOM. When you click modify, compare the old VDOM with the newly generated VDOM and replace different places. The whole large element will not be emptied and re-rendered, but only partial modification, which effectively reduces DOM operations and improves performance
The diff algorithm
The usefulness of the diff
- Diff command in Linux to see the difference between two text files
- Git diff compares the differences between two files
- The diff algorithm in VUE and React is different from that in VDOM
Why is the DIff algorithm used in VDOM
Because DOM manipulation is “expensive”, it is necessary to minimize DOM manipulation and find out which nodes the DOM must update this time. Otherwise, the process of “finding out” requires the DIff algorithm
Diff algorithm in VDOM implementation principle
The patch function in snabbdom.js implements the DIff algorithm
patch(dom, vnode)
Add the generated VDOM to the blank DOM element
Using simple code to achieve patch(DOM, Vnode), just a simple implementation idea, and can not run directly.
// VNode data model
// vnode = {
// "tag": 'ul',
// "attrs": {"id": 'list'},
// "children": [{
// "tag": 'li',
// "attrs": { "className": 'item'},
// "children": ['item1']
/ /}, {
// "tag": 'li',
// "attrs": { "className": 'item'},
// "children": ['item2']
/ /}]
// }
function createElement(vnode) {
var tag = vnode.tag;
var attrs = vnode.attrs || {};
var children = vnode.children || [];
if (tag == null) {
return null;
}
// Create a real DOM element
var elem = document.createElement(tag);
/ / property
var attrName;
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
// Add attributes to elemelem.setAttribute(attrName, attrs[attrName]); }}/ / child elements
children.forEach(childVnode= > {
// Add child elements to elem
elem.appendChild(createElement(childVnode)); / / recursion
});
// Return the actual DOM element
return elem;
}
Copy the code
patch(vnode, newVnode)
The newly generated VDOM is compared to the existing VDOM, changing only the changes
Use the following code to achieve a simple element detection of different and replacement logic, just simple logic, you can go to the Internet to find a special diff algorithm
// VNode changes the data model
// vnode = {
// "tag": 'ul',
// "attrs": {"id": 'list'},
// "children": [{
// "tag": 'li',
// "attrs": { "className": 'item'},
// "children": ['item1']
/ /}, {
// "tag": 'li',
// "attrs": { "className": 'item'},
// "children": ['item2']
/ /}]
// }
// newVnode = {
// "tag": 'ul',
// "attrs": {"id": 'list'},
// "children": [{
// "tag": 'li',
// "attrs": { "className": 'item'},
// "children": ['item1']
/ /}, {
// "tag": 'li',
// "attrs": { "className": 'item'},
// "children": ['itemB']
/ /}, {
// "tag": 'li',
// "attrs": { "className": 'item'},
// "children": ['item23]
/ /}]
// }
function updateChildren(vnode, newVnode) {
var children = vnode.children || [];
var newChildren = newVnode.children || [];
children.forEach((childVnode, index) = > {
var newChildVnode = newChildren[index];
if (childVnode.tag == newChildVnode.tag) {
// No changes continue to compare, deep contrast
updateChildren(childVnode, newChildVnode);
} else {
// Change the substitutionreplaceNode(childVnode, newChildVnode); }}); }function replaceNode(vnode, newVnode) {
var elem = vnode.elem;
var newElem = createElement(newVnode);
/ / replace
}
Copy the code