我知道如果在vanilla js中更改DOM,整个浏览器每次都会进行中继输出和重新绘制。
这是vanillajs测试代码
'use strict';
function _random(max) {
return Math.round(Math.random()*1000)%max;
}
const rowTemplate = document.createElement("tr");
rowTemplate.innerHTML = "<td class='col-md-1'></td><td class='col-md-4'><a class='lbl'></a></td><td class='col-md-1'><a class='remove'><span class='remove glyphicon glyphicon-remove' aria-hidden='true'></span></a></td><td class='col-md-6'></td>";
class Store {
constructor() {
this.data = [];
this.backup = null;
this.selected = null;
this.id = 1;
}
buildData(count = 1000) {
var adjectives = ["pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain", "quaint", "clean", "elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive", "cheap", "expensive", "fancy"];
var colours = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"];
var nouns = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", "keyboard"];
var data = [];
for (var i = 0; i < count; i++)
data.push({id: this.id++, label: adjectives[_random(adjectives.length)] + " " + colours[_random(colours.length)] + " " + nouns[_random(nouns.length)] });
return data;
}
updateData(mod = 10) {
for (let i=0;i<this.data.length;i+=10) {
this.data[i].label += ' !!!';
// this.data[i] = Object.assign({}, this.data[i], {label: this.data[i].label +' !!!'});
}
}
delete(id) {
const idx = this.data.findIndex(d => d.id==id);
this.data = this.data.filter((e,i) => i!=idx);
return this;
}
run() {
this.data = this.buildData();
this.selected = null;
}
add() {
this.data = this.data.concat(this.buildData(1000));
this.selected = null;
}
update() {
this.updateData();
this.selected = null;
}
select(id) {
this.selected = id;
}
hideAll() {
this.backup = this.data;
this.data = [];
this.selected = null;
}
showAll() {
this.data = this.backup;
this.backup = null;
this.selected = null;
}
runLots() {
this.data = this.buildData(10000);
this.selected = null;
}
clear() {
this.data = [];
this.selected = null;
}
swapRows() {
if(this.data.length > 998) {
var a = this.data[1];
this.data[1] = this.data[998];
this.data[998] = a;
}
}
}
var getParentId = function(elem) {
while (elem) {
if (elem.tagName==="TR") {
return elem.data_id;
}
elem = elem.parentNode;
}
return undefined;
}
class Main {
constructor(props) {
this.store = new Store();
this.select = this.select.bind(this);
this.delete = this.delete.bind(this);
this.add = this.add.bind(this);
this.run = this.run.bind(this);
this.update = this.update.bind(this);
this.start = 0;
this.rows = [];
this.data = [];
this.selectedRow = undefined;
document.getElementById("main").addEventListener('click', e => {
//console.log("listener",e);
if (e.target.matches('#add')) {
e.preventDefault();
//console.log("add");
this.add();
}
else if (e.target.matches('#run')) {
e.preventDefault();
//console.log("run");
this.run();
}
else if (e.target.matches('#update')) {
e.preventDefault();
//console.log("update");
this.update();
}
else if (e.target.matches('#hideall')) {
e.preventDefault();
//console.log("hideAll");
this.hideAll();
}
else if (e.target.matches('#showall')) {
e.preventDefault();
//console.log("showAll");
this.showAll();
}
else if (e.target.matches('#runlots')) {
e.preventDefault();
//console.log("runLots");
this.runLots();
}
else if (e.target.matches('#clear')) {
e.preventDefault();
//console.log("clear");
this.clear();
}
else if (e.target.matches('#swaprows')) {
e.preventDefault();
//console.log("swapRows");
this.swapRows();
}
else if (e.target.matches('.remove')) {
e.preventDefault();
let id = getParentId(e.target);
let idx = this.findIdx(id);
//console.log("delete",idx);
this.delete(idx);
}
else if (e.target.matches('.lbl')) {
e.preventDefault();
let id = getParentId(e.target);
let idx = this.findIdx(id);
//console.log("select",idx);
this.select(idx);
}
});
this.tbody = document.getElementById("tbody");
}
findIdx(id) {
for (let i=0;i<this.data.length;i++){
if (this.data[i].id === id) return i;
}
return undefined;
}
run() {
this.removeAllRows();
this.store.clear();
this.rows = [];
this.data = [];
this.store.run();
this.appendRows();
this.unselect();
}
add() {
this.store.add();
this.appendRows();
}
update() {
this.store.update();
for (let i=0;i<this.data.length;i+=10) {
this.rows[i].childNodes[1].childNodes[0].innerText = this.store.data[i].label;
}
}
unselect() {
if (this.selectedRow !== undefined) {
this.selectedRow.className = "";
this.selectedRow = undefined;
}
}
select(idx) {
this.unselect();
this.store.select(this.data[idx].id);
this.selectedRow = this.rows[idx];
this.selectedRow.className = "danger";
}
recreateSelection() {
let old_selection = this.store.selected;
let sel_idx = this.store.data.findIndex(d => d.id === old_selection);
if (sel_idx >= 0) {
this.store.select(this.data[sel_idx].id);
this.selectedRow = this.rows[sel_idx];
this.selectedRow.className = "danger";
}
}
delete(idx) {
// Remove that row from the DOM
this.store.delete(this.data[idx].id);
this.rows[idx].remove();
this.rows.splice(idx, 1);
this.data.splice(idx, 1);
this.unselect();
this.recreateSelection();
}
removeAllRows() {
// ~258 msecs
// for(let i=this.rows.length-1;i>=0;i--) {
// tbody.removeChild(this.rows[i]);
// }
// ~251 msecs
// for(let i=0;i<this.rows.length;i++) {
// tbody.removeChild(this.rows[i]);
// }
// ~216 msecs
// var cNode = tbody.cloneNode(false);
// tbody.parentNode.replaceChild(cNode ,tbody);
// ~212 msecs
this.tbody.textContent = "";
// ~236 msecs
// var rangeObj = new Range();
// rangeObj.selectNodeContents(tbody);
// rangeObj.deleteContents();
// ~260 msecs
// var last;
// while (last = tbody.lastChild) tbody.removeChild(last);
}
runLots() {
this.removeAllRows();
this.store.clear();
this.rows = [];
this.data = [];
this.store.runLots();
this.appendRows();
this.unselect();
}
clear() {
this.store.clear();
this.rows = [];
this.data = [];
// This is actually a bit faster, but close to cheating
// requestAnimationFrame(() => {
this.removeAllRows();
this.unselect();
// });
}
swapRows() {
if (this.data.length>10) {
this.store.swapRows();
this.data[1] = this.store.data[1];
this.data[998] = this.store.data[998];
this.tbody.insertBefore(this.rows[998], this.rows[2])
this.tbody.insertBefore(this.rows[1], this.rows[999])
let tmp = this.rows[998];
this.rows[998] = this.rows[1];
this.rows[1] = tmp;
}
// let old_selection = this.store.selected;
// this.store.swapRows();
// this.updateRows();
// this.unselect();
// if (old_selection>=0) {
// let idx = this.store.data.findIndex(d => d.id === old_selection);
// if (idx > 0) {
// this.store.select(this.data[idx].id);
// this.selectedRow = this.rows[idx];
// this.selectedRow.className = "danger";
// }
// }
}
appendRows() {
// Using a document fragment is slower...
// var docfrag = document.createDocumentFragment();
// for(let i=this.rows.length;i<this.store.data.length; i++) {
// let tr = this.createRow(this.store.data[i]);
// this.rows[i] = tr;
// this.data[i] = this.store.data[i];
// docfrag.appendChild(tr);
// }
// this.tbody.appendChild(docfrag);
// ... than adding directly
var rows = this.rows, s_data = this.store.data, data = this.data, tbody = this.tbody;
for(let i=rows.length;i<s_data.length; i++) {
let tr = this.createRow(s_data[i]);
rows[i] = tr;
data[i] = s_data[i];
tbody.appendChild(tr);
}
}
createRow(data) {
const tr = rowTemplate.cloneNode(true),
td1 = tr.firstChild,
a2 = td1.nextSibling.firstChild;
tr.data_id = data.id;
td1.textContent = data.id;
a2.textContent = data.label;
return tr;
}
}
new Main();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>VanillaJS-"keyed"</title>
<link href="/css/currentStyle.css" rel="stylesheet"/>
</head>
<body>
<div id='main'>
<div class="container">
<div class="jumbotron">
<div class="row">
<div class="col-md-6">
<h1>VanillaJS-"keyed"</h1>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-sm-6 smallpad">
<button type='button' class='btn btn-primary btn-block' id='run'>Create 1,000 rows</button>
</div>
<div class="col-sm-6 smallpad">
<button type='button' class='btn btn-primary btn-block' id='runlots'>Create 10,000 rows</button>
</div>
<div class="col-sm-6 smallpad">
<button type='button' class='btn btn-primary btn-block' id='add'>Append 1,000 rows</button>
</div>
<div class="col-sm-6 smallpad">
<button type='button' class='btn btn-primary btn-block' id='update'>Update every 10th row</button>
</div>
<div class="col-sm-6 smallpad">
<button type='button' class='btn btn-primary btn-block' id='clear'>Clear</button>
</div>
<div class="col-sm-6 smallpad">
<button type='button' class='btn btn-primary btn-block' id='swaprows'>Swap Rows</button>
</div>
</div>
</div>
</div>
</div>
<table class="table table-hover table-striped test-data">
<tbody id="tbody">
</tbody>
</table>
<span class="preloadicon glyphicon glyphicon-remove" aria-hidden="true"></span>
</div>
</div>
<script src='src/Main.js'></script>
</body>
</html>
这是react测试代码
var React = require('react');
var ReactDOM = require('react-dom');
function random(max) {
return Math.round(Math.random() * 1000) % max;
}
const A = ["pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain", "quaint", "clean",
"elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive",
"cheap", "expensive", "fancy"];
const C = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"];
const N = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse",
"keyboard"];
let nextId = 1;
function buildData(count) {
const data = new Array(count);
for (let i = 0; i < count; i++) {
data[i] = {
id: nextId++,
label: `${A[random(A.length)]} ${C[random(C.length)]} ${N[random(N.length)]}`,
};
}
return data;
}
const GlyphIcon = <span className="glyphicon glyphicon-remove" aria-hidden="true"></span>;
class Row extends React.Component {
onSelect = () => {
this.props.select(this.props.item);
}
onRemove = () => {
this.props.remove(this.props.item);
}
shouldComponentUpdate(nextProps) {
return nextProps.item !== this.props.item || nextProps.selected !== this.props.selected;
}
render() {
let { selected, item } = this.props;
return (<tr className={selected ? "danger" : ""}>
<td className="col-md-1">{item.id}</td>
<td className="col-md-4"><a onClick={this.onSelect}>{item.label}</a></td>
<td className="col-md-1"><a onClick={this.onRemove}>{GlyphIcon}</a></td>
<td className="col-md-6"></td>
</tr>);
}
}
function Button({ id, cb, title }) {
return (
<div className="col-sm-6 smallpad">
<button type="button" className="btn btn-primary btn-block" id={id} onClick={cb}>{title}</button>
</div>
);
}
class Jumbotron extends React.Component {
shouldComponentUpdate() {
return false;
}
render() {
const { run, runLots, add, update, clear, swapRows } = this.props;
return (
<div className="jumbotron">
<div className="row">
<div className="col-md-6">
<h1>React keyed</h1>
</div>
<div className="col-md-6">
<div className="row">
<Button id="run" title="Create 1,000 rows" cb={run} />
<Button id="runlots" title="Create 10,000 rows" cb={runLots} />
<Button id="add" title="Append 1,000 rows" cb={add} />
<Button id="update" title="Update every 10th row" cb={update} />
<Button id="clear" title="Clear" cb={clear} />
<Button id="swaprows" title="Swap Rows" cb={swapRows} />
</div>
</div>
</div>
</div>
);
}
}
class Main extends React.Component {
state = {
data: [],
selected: 0,
};
run = () => {
this.setState({ data: buildData(1000), selected: 0 });
}
runLots = () => {
this.setState({ data: buildData(10000), selected: 0 });
}
add = () => {
this.setState({ data: this.state.data.concat(buildData(1000)), selected: this.state.selected });
}
update = () => {
const data = this.state.data;
for (let i = 0; i < data.length; i += 10) {
const item = data[i];
data[i] = { id: item.id, label: item.label + ' !!!' };
}
this.forceUpdate();
}
select = (item) => {
this.setState({ selected: item.id });
}
remove = (item) => {
const data = this.state.data;
data.splice(data.indexOf(item), 1);
this.forceUpdate();
}
clear = () => {
this.setState({ data: [], selected: 0 });
}
swapRows = () => {
const data = this.state.data;
if (data.length > 998) {
let temp = data[1];
data[1] = data[998];
data[998] = temp;
}
this.forceUpdate();
}
render() {
return (<div className="container">
<Jumbotron run={this.run} runLots={this.runLots} add={this.add} update={this.update} clear={this.clear} swapRows={this.swapRows} />
<table className="table table-hover table-striped test-data"><tbody>
{this.state.data.map((item) => (
<Row key={item.id} item={item} selected={this.state.selected === item.id} select={this.select} remove={this.remove}></Row>
))}
</tbody></table>
<span className="preloadicon glyphicon glyphicon-remove" aria-hidden="true"></span>
</div>);
}
}
ReactDOM.render(
<Main />,
document.getElementById('main'),
);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>React</title>
<link href="/css/currentStyle.css" rel="stylesheet"/>
</head>
<body>
<div id='main'></div>
<script src='dist/main.js'></script>
</body>
</html>
基准结果站点js-framework-benchmark result
和githubsite js-framework-benchmark github
React使用虚拟DOM来提高性能。
React的构建是为了帮助开发人员从称为“组件”的孤立代码片段中创建复杂的UI。因此,它的主要目的是帮助创建大型项目,而不是比香草JS更快。然而,为了提高它的性能,它使用了虚拟DOM。
本文向大家介绍什么是虚拟DOM?相关面试题,主要包含被问及什么是虚拟DOM?时的应答技巧和注意事项,需要的朋友参考一下 虚拟 dom 是相对于浏览器所渲染出来的真实 dom 的,在react,vue等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 dom 树的方式找到需要修改的 dom 然后修改样式行为或者结构,来达到更新 ui 的目的。 这种方式相当消耗计算资源,因为每次查询 dom 几
我知道React创建一个虚拟DOM并比较差异,然后只是更新真实DOM的实际元素,但是如果手动更改它,如何更有效呢?通过还是使用jQuery函数?
本文向大家介绍react的虚拟DOM和vue的虚拟DOM有什么区别?相关面试题,主要包含被问及react的虚拟DOM和vue的虚拟DOM有什么区别?时的应答技巧和注意事项,需要的朋友参考一下 React 是数据先生成 vdom,然后对比 vdom(实现上并非先后顺序,两棵树边遍历边生成新的树),最后通过新的 DOM 树渲染。 Vue 是数据先进性对比,先通过数据的不同,再去更新 vdom(这不知道
前言 vdom 是 vue 和 React 的核心,先讲哪个都绕不开它。 vdom 比较独立,使用也比较简单。 如果面试问到 vue 和 React 和实现,免不了问 vdom: vdom 是什么?为何会存在 vdom? vdom 的如何应用,核心 API 是什么 介绍一下 diff 算法 什么是 vdom 什么是 vdom DOM操作是昂贵的。 步骤一:用JS对象模拟DOM树 步骤二:比较两棵虚
基础 虚拟 DOM 节点(vnode)是用于表示 DOM 元素(或 DOM 的一部分)的 JavaScript 对象。Mithril 的虚拟 DOM 引擎使用 vnode 树来生成 DOM 树。 vnode 通过 m() hyperscript 工具来创建: m("div", {id: "test"}, "hello") Hyperscript 也可以直接使用组件: // 定义一个组件 var E
本文向大家介绍为什么虚拟dom会提高性能?相关面试题,主要包含被问及为什么虚拟dom会提高性能?时的应答技巧和注意事项,需要的朋友参考一下 虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提髙性能。 具体实现步骤如下: 1. 用JavaScript对象结构表示DOM树的结构;然后用这个树构建一个真正的DOM树,插到文档当中 2.当状态变更