1. Что такое вдом?
vdom — это аббревиатура Virtual DOM (Virtual DOM), которая относится к структуре DOM, моделируемой JS, и сравнение изменений DOM выполняется на уровне JS. Другими словами, vdom — это JS-объект.
Следующая структура DOM:
<ul id="list">
<li class="item">Item1</li>
<li class="item">Item2</li>
</ul>
Отображение в виртуальный DOM выглядит следующим образом:
{
tag: "ul",
attrs: {
id: "list"
},
children: [
{
tag: "li",
attrs: { className: "item" },
children: ["Item1"]
}, {
tag: "li",
attrs: { className: "item" },
children: ["Item2"]
}
]
}
2. Зачем использовать вдом?
Теперь есть сценарий, реализующий следующие требования:
[
{
name: "张三",
age: "20",
address: "北京"
},
{
name: "李四",
age: "21",
address: "武汉"
},
{
name: "王五",
age: "22",
address: "杭州"
},
]
Отобразите данные в виде таблицы и измените любую информацию по своему желанию, и таблица также будет изменена. Реализовано с помощью jQuery следующим образом:
<!DOCTYPE html>
<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>Document</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">改变</button>
<script src="https://cdn.bootcss.com/jquery/3.2.0/jquery.js"></script>
<script>
const data = [{
name: "张三",
age: "20",
address: "北京"
},
{
name: "李四",
age: "21",
address: "武汉"
},
{
name: "王五",
age: "22",
address: "杭州"
},
];
//渲染函数
function render(data) {
const $container = $('#container');
$container.html('');
const $table = $('<table>');
// 重绘一次
$table.append($('<tr><td>name</td><td>age</td><td>address</td></tr>'));
data.forEach(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 = '深圳';
render(data);
});
</script>
</body>
</html>
Нажмите кнопку таким образом, будут соответствующие изменения представления, но если вы просмотрите следующие элементы, после каждого изменения метка таблицы должна быть воссоздана, то есть каждый столбец под таблицей, являются ли данные одинаковыми как оригинал или нет, должен быть воссоздан Рендеринг, это не идеальная ситуация.Когда столбец данных совпадает с оригиналом, мы надеемся, что этот столбец не будет повторно визуализирован, потому что перерисовка DOM потребляет производительность браузера.
Поэтому мы используем метод моделирования объектов JS и помещаем операцию сравнения DOM на уровень JS, чтобы уменьшить ненужную перерисовку браузера и повысить эффективность.
Конечно, некоторые люди говорят, что виртуальный DOM не быстрее реального DOM, что тоже верно. При изменении каждого фрагмента данных в приведенной выше таблице очевидно, что реальная работа DOM выполняется быстрее, потому что виртуальный DOM также имеет процесс сравнения алгоритма diff в js. Таким образом, указанные выше преимущества производительности применимы только при рендеринге большого объема данных и изменении лишь небольшой части данных.
Виртуальный дом более превосходной в:
1. Он открывает дверь для функционального программирования пользовательского интерфейса, то есть пользовательский интерфейс = f (данные), способ создания пользовательского интерфейса.
2. Объекты JS можно рендерить в среды, отличные от DOM браузера, то есть поддерживается кроссплатформенная разработка, например ReactNative.
Кроме того, вы можете обратиться к некоторым ответам Юды:Ууху. Call.com/question/31…
3. Используйте snabbdom для реализации vdom
адрес снабдома:Github.com/ принимает BB DOM / Suning ...
Это простая библиотека, которая реализует функции vdom, по сравнению с vue и react она проще для vdom и подходит нам для изучения vdom. В vdom есть два основных API: одна — функция h, а другая — функция patch.Первая используется для создания объекта vdom, а вторая — для сравнения виртуального DOM и монтирования vdom к реальному DOM.
Кратко опишите использование этих двух функций:
- h('имя тега', {атрибут}, [дочерний элемент])
- h('имя тега', {свойство}, [текст])
- patch(container, vnode) container — элемент контейнера DOM
- patch(vnode, newVnode)
Теперь давайте перепишем предыдущий пример со snabbdom:
<!DOCTYPE html>
<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>Document</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">改变</button>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.js"></script>
<script>
let snabbdom = window.snabbdom;
// 定义patch
let patch = snabbdom.init([
snabbdom_class,
snabbdom_props,
snabbdom_style,
snabbdom_eventlisteners
]);
//定义h
let h = snabbdom.h;
const data = [{
name: "张三",
age: "20",
address: "北京"
},
{
name: "李四",
age: "21",
address: "武汉"
},
{
name: "王五",
age: "22",
address: "杭州"
},
];
data.unshift({name: "姓名", age: "年龄", address: "地址"});
let container = document.getElementById('container');
let vnode;
const render = (data) => {
let newVnode = h('table', {}, data.map(item => {
let tds = [];
for(let 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);
let btnChnage = document.getElementById('btn-change');
btnChnage.addEventListener('click', function() {
data[1].age = 30;
data[2].address = "深圳";
//re-render
render(data);
})
</script>
</body>
</html>
Затем зайдите на страницу:
Вы обнаружите, что только измененные столбцы мерцают, то есть перерисовываются, а столбцы, данные которых не изменились, остаются прежними, что значительно экономит браузеру накладные расходы на повторный рендеринг.В-четвертых, алгоритм сравнения
1. Что такое алгоритм сравнения?
Так называемый алгоритм сравнения — это алгоритм, используемый для поиска различий между двумя текстами.
В качестве front-end вы часто слышите слово diff алгоритм.На самом деле, diff не является оригинальным алгоритмом front-end.На самом деле, этот алгоритм уже был отражен в команде diff Linux, и обычно используемой git diff также является используемым алгоритмом diff.
2. Почему VDOM использует алгоритм Diff
Операция DOM очень дорого, поэтому нам нужно максимально уменьшить операцию DOM. Это необходимо найти узел, который этот DOM должен обновлять, чтобы обновить. Другие неравные, эта процедура найден, вам нужно применить алгоритм diff.
3. Простая реализация алгоритма diff в vdom
Следующий код предназначен только для того, чтобы помочь вам понять принцип и процесс алгоритма сравнения, и его нельзя использовать в производственной среде.
Преобразование vdom в реальный дом:
const createElement = (vnode) => {
let tag = vnode.tag;
let attrs = vnode.attrs || {};
let children = vnode.children || [];
if(!tag) {
return null;
}
//创建元素
let elem = document.createElement(tag);
//属性
let attrName;
for (attrName in attrs) {
if(attrs.hasOwnProperty(attrName)) {
elem.setAttribute(attrName, attrs[attrName]);
}
}
//子元素
children.forEach(childVnode => {
//给elem添加子元素
elem.appendChild(createElement(childVnode));
})
//返回真实的dom元素
return elem;
}
Используйте простой алгоритм diff для выполнения операции обновления:
function updateChildren(vnode, newVnode) {
let children = vnode.children || [];
let newChildren = newVnode.children || [];
children.forEach((childVnode, index) => {
let newChildVNode = newChildren[index];
if(childVnode.tag === newChildVNode.tag) {
//深层次对比, 递归过程
updateChildren(childVnode, newChildVNode);
} else {
//替换
replaceNode(childVnode, newChildVNode);
}
})
}
Использованная литература:
Секретный уровень интернет-компаний Advanced Advanced WhavaScript Front-Enter-интервью