[IMP] tools: rework benchmarks application

closes #130
This commit is contained in:
Géry Debongnie
2019-06-11 14:44:30 +02:00
parent 710f42d4e4
commit 2840794fa2
27 changed files with 1796 additions and 459 deletions
+118 -35
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
odoo.define("app", function(require) {
const Widget = require("web.Widget");
@@ -97,14 +102,23 @@ odoo.define("app", function(require) {
"click .o_btn_msg.10000": function() {
this.addMessages(10000);
},
"click .o_btn_msg.50000": function() {
this.addMessages(50000);
"click .o_btn_msg.30000": function() {
this.addMessages(30000);
},
"click .updateSomeMessages": function() {
this.updateSomeMessages();
},
"click .clear": function() {
this.clear();
},
"click .o_multiple": function() {
this.multipleFlag = !this.multipleFlag;
},
"click .o_clear": function() {
this.clearFlag = !this.clearFlag;
},
"click .clear-log": function() {
this.$log[0].innerHTML = "";
}
},
custom_events: {
@@ -116,11 +130,15 @@ odoo.define("app", function(require) {
this.widgets = {};
this.isInDom = false;
this.messageCount = 0;
this.multipleFlag = false;
this.clearFlag = false;
},
start: function() {
this.$content = this.$(".content");
this.$msgCount = this.$(".message_count");
this.$log = this.$(".log-content");
this.log("Benchmarking odoo widgets, 12.0");
},
on_attach_callback: function() {
this.isInDom = true;
@@ -144,34 +162,41 @@ odoo.define("app", function(require) {
},
addMessages: function(n) {
var self = this;
startMeasure("add " + n);
const defs = [];
const messages = buildData(n);
for (let message of messages) {
const widget = new Message(this, message);
this.widgets[message.id] = widget;
defs.push(widget.appendTo("<div>"));
}
$.when
.apply($, defs)
.then(function() {
for (let message of messages) {
let widget = self.widgets[message.id];
dom.append(self.$content, widget.$el, {
in_DOM: this.isInDom,
callbacks: [{ widget: widget }]
});
}
})
.then(function() {
self.messageCount += n;
self.updateMessageCount();
stopMeasure();
});
const self = this;
this.benchmark("add " + n, () => {
const defs = [];
const messages = buildData(n);
for (let message of messages) {
const widget = new Message(this, message);
this.widgets[message.id] = widget;
defs.push(widget.appendTo("<div>"));
}
return $.when
.apply($, defs)
.then(function() {
for (let message of messages) {
let widget = self.widgets[message.id];
dom.append(self.$content, widget.$el, {
in_DOM: this.isInDom,
callbacks: [{ widget: widget }]
});
}
})
.then(function() {
self.messageCount += n;
self.updateMessageCount();
});
});
},
clear: function() {
startMeasure("clear");
this._clear();
stopMeasure(info => {
this.log(info.msg);
});
},
_clear: function() {
this.$content.empty();
for (let key in this.widgets) {
this.widgets[key].destroy();
@@ -179,15 +204,14 @@ odoo.define("app", function(require) {
}
this.messageCount = 0;
this.updateMessageCount();
stopMeasure();
},
updateSomeMessages: function() {
startMeasure("update every 10th");
const widgets = Object.values(this.widgets);
for (let i = 0; i < widgets.length; i += 10) {
widgets[i].update();
}
stopMeasure();
this.benchmark("update every 10th", () => {
const widgets = Object.values(this.widgets);
for (let i = 0; i < widgets.length; i += 10) {
widgets[i].update();
}
});
},
_onRemoveMessage: function(ev) {
startMeasure("remove message");
@@ -196,6 +220,65 @@ odoo.define("app", function(require) {
this.messageCount--;
this.updateMessageCount();
stopMeasure();
},
benchmark: function(message, fn, callback) {
if (this.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.clearFlag) {
this._benchmark("clear", this._clear.bind(this), finalize, false);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
},
_benchmark: function(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
const benchmark = fn();
(benchmark && benchmark.then ? benchmark : $.when()).then(() => {
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
});
},
log: function(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.$log[0].appendChild(div);
this.$log[0].scrollTop = this.$log[0].scrollHeight;
}
});
@@ -1,15 +1,32 @@
<templates>
<div class="main" t-name="root">
<div class="left-thing">
<div class="message_count">
Number of msg: 0
<div class="title">Actions</div>
<div class="panel">
<button class="o_btn_msg 100">Add 100 messages</button>
<button class="o_btn_msg 1000">Add 1k messages</button>
<button class="o_btn_msg 10000">Add 10k messages</button>
<button class="o_btn_msg 30000">Add 30k messages</button>
<button class="updateSomeMessages">Update every 10th message</button>
<button class="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" class="o_multiple" id="multipleflag" />
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" class="o_clear" id="clearFlag" />
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info message_count">
Number of messages: 0
</div>
<div class="title">Log <span class="clear-log">(clear)</span></div>
<div class="log">
<div class="log-content"/>
</div>
<button class="o_btn_msg 100">Add 100 messages</button>
<button class="o_btn_msg 1000">Add 1000 messages</button>
<button class="o_btn_msg 10000">Add 10000 messages</button>
<button class="o_btn_msg 50000">Add 50000 messages</button>
<button class="updateSomeMessages">Update every 10th message</button>
<button class="clear">Clear</button>
</div>
<div class="right-thing">
<div class="content">
@@ -20,7 +37,7 @@
<div t-name="message" class="message">
<span class="author"><t t-esc="widget.author"/></span>
<span class="msg"><t t-esc="widget.msg"/></span>
<button class="remove" >Remove</button>
<button class="remove">Remove</button>
</div>
<div t-name="counter">
+118 -35
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
odoo.define("app", function(require) {
const Widget = require("web.Widget");
@@ -97,14 +102,23 @@ odoo.define("app", function(require) {
"click .o_btn_msg.10000": function() {
this.addMessages(10000);
},
"click .o_btn_msg.50000": function() {
this.addMessages(50000);
"click .o_btn_msg.30000": function() {
this.addMessages(30000);
},
"click .updateSomeMessages": function() {
this.updateSomeMessages();
},
"click .clear": function() {
this.clear();
},
"click .o_multiple": function() {
this.multipleFlag = !this.multipleFlag;
},
"click .o_clear": function() {
this.clearFlag = !this.clearFlag;
},
"click .clear-log": function() {
this.$log[0].innerHTML = "";
}
},
custom_events: {
@@ -116,11 +130,15 @@ odoo.define("app", function(require) {
this.widgets = {};
this.isInDom = false;
this.messageCount = 0;
this.multipleFlag = false;
this.clearFlag = false;
},
start: function() {
this.$content = this.$(".content");
this.$msgCount = this.$(".message_count");
this.$log = this.$(".log-content");
this.log("Benchmarking odoo widgets, 12.3");
},
on_attach_callback: function() {
this.isInDom = true;
@@ -144,34 +162,41 @@ odoo.define("app", function(require) {
},
addMessages: function(n) {
var self = this;
startMeasure("add " + n);
const defs = [];
const messages = buildData(n);
for (let message of messages) {
const widget = new Message(this, message);
this.widgets[message.id] = widget;
defs.push(widget.appendTo("<div>"));
}
$.when
.apply($, defs)
.then(function() {
for (let message of messages) {
let widget = self.widgets[message.id];
dom.append(self.$content, widget.$el, {
in_DOM: this.isInDom,
callbacks: [{ widget: widget }]
});
}
})
.then(function() {
self.messageCount += n;
self.updateMessageCount();
stopMeasure();
});
const self = this;
this.benchmark("add " + n, () => {
const defs = [];
const messages = buildData(n);
for (let message of messages) {
const widget = new Message(this, message);
this.widgets[message.id] = widget;
defs.push(widget.appendTo("<div>"));
}
return $.when
.apply($, defs)
.then(function() {
for (let message of messages) {
let widget = self.widgets[message.id];
dom.append(self.$content, widget.$el, {
in_DOM: this.isInDom,
callbacks: [{ widget: widget }]
});
}
})
.then(function() {
self.messageCount += n;
self.updateMessageCount();
});
});
},
clear: function() {
startMeasure("clear");
this._clear();
stopMeasure(info => {
this.log(info.msg);
});
},
_clear: function() {
this.$content.empty();
for (let key in this.widgets) {
this.widgets[key].destroy();
@@ -179,15 +204,14 @@ odoo.define("app", function(require) {
}
this.messageCount = 0;
this.updateMessageCount();
stopMeasure();
},
updateSomeMessages: function() {
startMeasure("update every 10th");
const widgets = Object.values(this.widgets);
for (let i = 0; i < widgets.length; i += 10) {
widgets[i].update();
}
stopMeasure();
this.benchmark("update every 10th", () => {
const widgets = Object.values(this.widgets);
for (let i = 0; i < widgets.length; i += 10) {
widgets[i].update();
}
});
},
_onRemoveMessage: function(ev) {
startMeasure("remove message");
@@ -196,6 +220,65 @@ odoo.define("app", function(require) {
this.messageCount--;
this.updateMessageCount();
stopMeasure();
},
benchmark: function(message, fn, callback) {
if (this.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.clearFlag) {
this._benchmark("clear", this._clear.bind(this), finalize, false);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
},
_benchmark: function(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
const benchmark = fn();
(benchmark && benchmark.then ? benchmark : $.when()).then(() => {
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
});
},
log: function(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.$log[0].appendChild(div);
this.$log[0].scrollTop = this.$log[0].scrollHeight;
}
});
@@ -1,15 +1,32 @@
<templates>
<div class="main" t-name="root">
<div class="left-thing">
<div class="message_count">
Number of msg: 0
<div class="title">Actions</div>
<div class="panel">
<button class="o_btn_msg 100">Add 100 messages</button>
<button class="o_btn_msg 1000">Add 1k messages</button>
<button class="o_btn_msg 10000">Add 10k messages</button>
<button class="o_btn_msg 30000">Add 30k messages</button>
<button class="updateSomeMessages">Update every 10th message</button>
<button class="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" class="o_multiple" id="multipleflag" />
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" class="o_clear" id="clearFlag" />
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info message_count">
Number of messages: 0
</div>
<div class="title">Log <span class="clear-log">(clear)</span></div>
<div class="log">
<div class="log-content"/>
</div>
<button class="o_btn_msg 100">Add 100 messages</button>
<button class="o_btn_msg 1000">Add 1000 messages</button>
<button class="o_btn_msg 10000">Add 10000 messages</button>
<button class="o_btn_msg 50000">Add 50000 messages</button>
<button class="updateSomeMessages">Update every 10th message</button>
<button class="clear">Clear</button>
</div>
<div class="right-thing">
<div class="content">
@@ -20,7 +37,7 @@
<div t-name="message" class="message">
<span class="author"><t t-esc="widget.author"/></span>
<span class="msg"><t t-esc="widget.msg"/></span>
<button class="remove" >Remove</button>
<button class="remove">Remove</button>
</div>
<div t-name="counter">
+113 -24
View File
@@ -1,11 +1,16 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Likes Counter Widget
//------------------------------------------------------------------------------
class Counter extends owl.Component {
template = "counter";
state = { counter: 0 };
template = "Counter";
increment() {
this.state.counter++;
@@ -16,8 +21,8 @@ class Counter extends owl.Component {
// Message Widget
//------------------------------------------------------------------------------
class Message extends owl.Component {
template = "message";
widgets = { Counter };
template = "Message";
shouldUpdate(nextProps) {
return nextProps !== this.props;
@@ -33,39 +38,123 @@ class Message extends owl.Component {
// Root Widget
//------------------------------------------------------------------------------
class App extends owl.Component {
template = "root";
widgets = { Message };
state = { messages: [] };
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
template = "App";
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
});
}
clear() {
startMeasure("clear");
this.state.messages = [];
stopMeasure();
this._benchmark("clear", () => {
this.state.messages = [];
});
}
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += '!!!';
this.set(messages, i, msg);
}
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
this.set(messages, i, msg);
}
});
}
removeMessage(data) {
startMeasure("remove message");
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
stopMeasure();
this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
});
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
}
}
+27 -9
View File
@@ -1,16 +1,34 @@
<templates>
<div t-name="root" class="main">
<div t-name="App" class="main">
<div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div>
<div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button>
<button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing">
<div class="content">
<div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t>
@@ -18,14 +36,14 @@
</div>
</div>
<div t-name="message" class="message">
<div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/>
</div>
<div t-name="counter">
<div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div>
+110 -24
View File
@@ -1,10 +1,14 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Likes Counter Widget
//------------------------------------------------------------------------------
class Counter extends owl.Component {
template = "counter";
state = { counter: 0 };
increment() {
@@ -16,7 +20,6 @@ class Counter extends owl.Component {
// Message Widget
//------------------------------------------------------------------------------
class Message extends owl.Component {
template = "message";
widgets = { Counter };
shouldUpdate(nextProps) {
@@ -33,39 +36,122 @@ class Message extends owl.Component {
// Root Widget
//------------------------------------------------------------------------------
class App extends owl.Component {
template = "root";
widgets = { Message };
state = { messages: [] };
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
});
}
clear() {
startMeasure("clear");
this.state.messages = [];
stopMeasure();
this._benchmark("clear", () => {
this.state.messages = [];
});
}
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += '!!!';
this.set(messages, i, msg);
}
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
this.set(messages, i, msg);
}
});
}
removeMessage(data) {
startMeasure("remove message");
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
stopMeasure();
this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
});
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
}
}
+27 -9
View File
@@ -1,16 +1,34 @@
<templates>
<div t-name="root" class="main">
<div t-name="App" class="main">
<div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div>
<div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button>
<button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing">
<div class="content">
<div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t>
@@ -18,14 +36,14 @@
</div>
</div>
<div t-name="message" class="message">
<div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/>
</div>
<div t-name="counter">
<div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div>
+110 -24
View File
@@ -1,10 +1,14 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Likes Counter Widget
//------------------------------------------------------------------------------
class Counter extends owl.Component {
template = "counter";
state = { counter: 0 };
increment() {
@@ -16,7 +20,6 @@ class Counter extends owl.Component {
// Message Widget
//------------------------------------------------------------------------------
class Message extends owl.Component {
template = "message";
widgets = { Counter };
shouldUpdate(nextProps) {
@@ -33,39 +36,122 @@ class Message extends owl.Component {
// Root Widget
//------------------------------------------------------------------------------
class App extends owl.Component {
template = "root";
widgets = { Message };
state = { messages: [] };
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() {
this.log(
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
});
}
clear() {
startMeasure("clear");
this.state.messages = [];
stopMeasure();
this.benchmark("clear", () => {
this.state.messages = [];
});
}
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += '!!!';
this.set(messages, i, msg);
}
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
this.set(messages, i, msg);
}
});
}
removeMessage(data) {
startMeasure("remove message");
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
stopMeasure();
this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
});
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
}
}
+27 -9
View File
@@ -1,16 +1,34 @@
<templates>
<div t-name="root" class="main">
<div t-name="App" class="main">
<div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div>
<div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button>
<button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="log"/>
</div>
</div>
<div class="right-thing">
<div class="content">
<div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t>
@@ -18,14 +36,14 @@
</div>
</div>
<div t-name="message" class="message">
<div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/>
</div>
<div t-name="counter">
<div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div>
+113 -27
View File
@@ -1,10 +1,14 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Likes Counter Widget
//------------------------------------------------------------------------------
class Counter extends owl.Component {
template = "counter";
state = { counter: 0 };
increment() {
@@ -16,15 +20,14 @@ class Counter extends owl.Component {
// Message Widget
//------------------------------------------------------------------------------
class Message extends owl.Component {
template = "message";
widgets = { Counter };
shouldUpdate(nextProps) {
return nextProps.message !== this.props.message;
}
removeMessage() {
this.trigger("remove_message", {
id: this.props.id
this.trigger("remove-message", {
id: this.props.message.id
});
}
}
@@ -33,39 +36,122 @@ class Message extends owl.Component {
// Root Widget
//------------------------------------------------------------------------------
class App extends owl.Component {
template = "root";
widgets = { Message };
state = { messages: [] };
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() {
this.log(
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
});
}
clear() {
startMeasure("clear");
this.state.messages = [];
stopMeasure();
this._benchmark("clear", () => {
this.state.messages = [];
});
}
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
this.set(messages, i, msg);
}
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
this.set(messages, i, msg);
}
});
}
removeMessage(data) {
startMeasure("remove message");
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
stopMeasure();
removeMessage(event) {
this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
this.state.messages.splice(index, 1);
});
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
}
}
+28 -12
View File
@@ -1,33 +1,49 @@
<templates>
<div t-name="root" class="main">
<div t-name="App" class="main">
<div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div>
<div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button>
<button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="log"/>
</div>
</div>
<div class="right-thing">
<div class="content">
<div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id"
message="message"
t-on-remove_message="removeMessage"/>
<t t-widget="Message" t-key="message.id" message="message"/>
</t>
</div>
</div>
</div>
<div t-name="message" class="message">
<div t-name="Message" class="message">
<span class="author"><t t-esc="props.message.author"/></span>
<span class="msg"><t t-esc="props.message.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/>
</div>
<div t-name="counter">
<div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div>
+113 -25
View File
@@ -1,11 +1,16 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Likes Counter Widget
//------------------------------------------------------------------------------
class Counter extends owl.Component {
template = "counter";
state = { counter: 0 };
template = "Counter";
increment() {
this.state.counter++;
@@ -16,13 +21,12 @@ class Counter extends owl.Component {
// Message Widget
//------------------------------------------------------------------------------
class Message extends owl.Component {
template = "message";
widgets = { Counter };
template = "Message";
shouldUpdate(nextProps) {
return nextProps !== this.props;
}
removeMessage() {
this.trigger("remove_message", {
id: this.props.id
@@ -34,39 +38,123 @@ class Message extends owl.Component {
// Root Widget
//------------------------------------------------------------------------------
class App extends owl.Component {
template = "root";
widgets = { Message };
state = { messages: [] };
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
template = "App";
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
});
}
clear() {
startMeasure("clear");
this.state.messages = [];
stopMeasure();
this._benchmark("clear", () => {
this.state.messages = [];
});
}
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += '!!!';
this.set(messages, i, msg);
}
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
this.set(messages, i, msg);
}
});
}
removeMessage(data) {
startMeasure('remove message');
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
stopMeasure();
this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
});
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
}
}
+27 -9
View File
@@ -1,16 +1,34 @@
<templates>
<div t-name="root" class="main">
<div t-name="App" class="main">
<div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div>
<div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button>
<button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing">
<div class="content">
<div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t>
@@ -18,14 +36,14 @@
</div>
</div>
<div t-name="message" class="message">
<div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/>
</div>
<div t-name="counter">
<div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div>
+113 -24
View File
@@ -1,11 +1,16 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Likes Counter Widget
//------------------------------------------------------------------------------
class Counter extends owl.Component {
template = "counter";
state = { counter: 0 };
template = "Counter";
increment() {
this.state.counter++;
@@ -16,8 +21,8 @@ class Counter extends owl.Component {
// Message Widget
//------------------------------------------------------------------------------
class Message extends owl.Component {
template = "message";
widgets = { Counter };
template = "Message";
shouldUpdate(nextProps) {
return nextProps !== this.props;
@@ -33,39 +38,123 @@ class Message extends owl.Component {
// Root Widget
//------------------------------------------------------------------------------
class App extends owl.Component {
template = "root";
widgets = { Message };
state = { messages: [] };
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
template = "App";
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
});
}
clear() {
startMeasure("clear");
this.state.messages = [];
stopMeasure();
this._benchmark("clear", () => {
this.state.messages = [];
});
}
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += '!!!';
this.set(messages, i, msg);
}
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
this.set(messages, i, msg);
}
});
}
removeMessage(data) {
startMeasure("remove message");
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
stopMeasure();
this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
});
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
}
}
+27 -9
View File
@@ -1,16 +1,34 @@
<templates>
<div t-name="root" class="main">
<div t-name="App" class="main">
<div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div>
<div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button>
<button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing">
<div class="content">
<div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t>
@@ -18,14 +36,14 @@
</div>
</div>
<div t-name="message" class="message">
<div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/>
</div>
<div t-name="counter">
<div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div>
+113 -24
View File
@@ -1,11 +1,16 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Likes Counter Widget
//------------------------------------------------------------------------------
class Counter extends owl.Component {
template = "counter";
state = { counter: 0 };
template = "Counter";
increment() {
this.state.counter++;
@@ -16,8 +21,8 @@ class Counter extends owl.Component {
// Message Widget
//------------------------------------------------------------------------------
class Message extends owl.Component {
template = "message";
widgets = { Counter };
template = "Message";
shouldUpdate(nextProps) {
return nextProps !== this.props;
@@ -33,39 +38,123 @@ class Message extends owl.Component {
// Root Widget
//------------------------------------------------------------------------------
class App extends owl.Component {
template = "root";
widgets = { Message };
state = { messages: [] };
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
template = "App";
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
});
}
clear() {
startMeasure("clear");
this.state.messages = [];
stopMeasure();
this._benchmark("clear", () => {
this.state.messages = [];
});
}
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += '!!!';
this.set(messages, i, msg);
}
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
this.set(messages, i, msg);
}
});
}
removeMessage(data) {
startMeasure("remove message");
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
stopMeasure();
this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
});
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
}
}
+27 -9
View File
@@ -1,16 +1,34 @@
<templates>
<div t-name="root" class="main">
<div t-name="App" class="main">
<div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div>
<div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button>
<button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing">
<div class="content">
<div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t>
@@ -18,14 +36,14 @@
</div>
</div>
<div t-name="message" class="message">
<div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/>
</div>
<div t-name="counter">
<div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div>
+115 -26
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Likes Counter Widget
@@ -18,11 +23,11 @@ class Message extends owl.Component {
widgets = { Counter };
shouldUpdate(nextProps) {
return nextProps !== this.props;
return nextProps.message !== this.props.message;
}
removeMessage() {
this.trigger("remove_message", {
id: this.props.id
this.trigger("remove-message", {
id: this.props.message.id
});
}
}
@@ -32,38 +37,122 @@ class Message extends owl.Component {
//------------------------------------------------------------------------------
class App extends owl.Component {
widgets = { Message };
state = { messages: [] };
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() {
this.log(
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages);
});
}
clear() {
startMeasure("clear");
this.state.messages = [];
stopMeasure();
this._benchmark("clear", () => {
this.state.messages = [];
});
}
updateSomeMessages() {
startMeasure("update every 10th");
const setState = owl.Observer.set;
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += '!!!';
setState(messages, i, msg);
}
stopMeasure();
this.benchmark("update every 10th", () => {
const setState = owl.Observer.set;
const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]);
msg.author += "!!!";
setState(messages, i, msg);
}
});
}
removeMessage(data) {
startMeasure("remove message");
const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1);
stopMeasure();
removeMessage(event) {
this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
this.state.messages.splice(index, 1);
});
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
}
}
-1
View File
@@ -7,7 +7,6 @@
<script src='../../owl.js'></script>
</head>
<body>
<div id='main'></div>
<script src='app.js' type="module"></script>
</body>
</html>
+25 -7
View File
@@ -1,18 +1,36 @@
<templates>
<div t-name="App" class="main">
<div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div>
<div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button>
<button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="log"/>
</div>
</div>
<div class="right-thing">
<div class="content">
<div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" message="message" t-on-remove_message="removeMessage"/>
<t t-widget="Message" t-key="message.id" message="message"/>
</t>
</div>
</div>
+186 -62
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Counter Widget
@@ -86,9 +91,33 @@ class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
messages: []
messages: [],
multipleFlag: false,
clearAfterFlag: false
};
this.removeMessage = this.removeMessage.bind(this);
this.toggleMultipleFlag = this.toggleMultipleFlag.bind(this);
this.toggleClearAfterFlag = this.toggleClearAfterFlag.bind(this);
}
componentDidMount() {
this.log(`Benchmarking React v${React.version}`);
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.logref.appendChild(div);
this.refs.logref.scrollTop = this.refs.logref.scrollHeight;
}
toggleMultipleFlag() {
this.setState({ multipleFlag: !this.state.multipleFlag });
}
toggleClearAfterFlag() {
this.setState({ clearAfterFlag: !this.state.clearAfterFlag });
}
render() {
@@ -109,53 +138,98 @@ class Main extends React.Component {
{
className: "left-thing"
},
React.createElement("div", { className: "title" }, "Actions"),
React.createElement(
"div",
null,
"Number of msg: ",
{ className: "panel" },
React.createElement(
"button",
{
onClick: _ => this.addMessages(100)
},
"Add 100 messages"
),
React.createElement(
"button",
{
onClick: _ => this.addMessages(1000)
},
"Add 1k messages"
),
React.createElement(
"button",
{
onClick: _ => this.addMessages(10000)
},
"Add 10k messages"
),
React.createElement(
"button",
{
onClick: _ => this.addMessages(30000)
},
"Add 30k messages"
),
React.createElement(
"button",
{
onClick: _ => this.updateSomeMessages()
},
"Update every 10th message"
),
React.createElement(
"button",
{
onClick: _ => this.clear()
},
"Clear"
)
),
React.createElement(
"div",
{ className: "flags" },
React.createElement(
"div",
null,
React.createElement("input", {
type: "checkbox",
id: "multipleFlag",
value: this.state.multipleFlag,
onChange: this.toggleMultipleFlag
}),
React.createElement("label", { for: "multipleFlag" }, " Do it 20x")
),
React.createElement(
"div",
null,
React.createElement("input", {
type: "checkbox",
id: "clearFlag",
value: this.state.clearAfterFlag,
onChange: this.toggleClearAfterFlag
}),
React.createElement("label", { for: "clearFlag" }, " Clear after")
)
),
React.createElement(
"div",
{ className: "info" },
"Number of messages: ",
this.state.messages.length
),
React.createElement(
"button",
{
onClick: _ => this.addMessages(100)
},
"Add 100 messages"
"div",
{ className: "title" },
"Log ",
React.createElement("span", { className: "clear-log" }, "(clear)")
),
React.createElement(
"button",
{
onClick: _ => this.addMessages(1000)
},
"Add 1000 messages"
),
React.createElement(
"button",
{
onClick: _ => this.addMessages(10000)
},
"Add 10000 messages"
),
React.createElement(
"button",
{
onClick: _ => this.addMessages(50000)
},
"Add 50000 messages"
),
React.createElement(
"button",
{
onClick: _ => this.updateSomeMessages()
},
"Update every 10th messags"
),
React.createElement(
"button",
{
onClick: _ => this.clear()
},
"Clear"
"div",
{ className: "log" },
React.createElement("div", {
className: "log-content",
ref: "logref"
})
)
),
React.createElement(
@@ -175,30 +249,30 @@ class Main extends React.Component {
}
addMessages(n) {
startMeasure("add " + n);
const newMessages = this.state.messages.concat(buildData(n));
this.setState({
messages: newMessages
this.benchmark("add " + n, () => {
const newMessages = this.state.messages.concat(buildData(n));
this.setState({
messages: newMessages
});
});
stopMeasure();
}
clear() {
startMeasure("clear");
this.setState({
messages: []
this._benchmark("clear", () => {
this.setState({
messages: []
});
});
stopMeasure();
}
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.state.messages;
for (let i = 0; i < messages.length; i += 10) {
messages[i].author += "!!!";
}
this.forceUpdate();
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.state.messages;
for (let i = 0; i < messages.length; i += 10) {
messages[i].author += "!!!";
}
this.forceUpdate();
});
}
removeMessage(id) {
@@ -211,11 +285,61 @@ class Main extends React.Component {
});
stopMeasure();
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.setState({ messages: [] });
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
} //-----------------------------
// INIT
//-----------------------------
ReactDOM.render(
React.createElement(Main, null),
document.getElementById("main")
);
ReactDOM.render(React.createElement(Main, null), document.body);
+76 -11
View File
@@ -1,26 +1,91 @@
.main {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
html,
body {
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial,
sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
body {
margin: 0;
}
.main {
height: 100%;
font-size: 14px;
display: grid;
grid-template-columns: 220px 1fr;
grid-template-columns: 640px 1fr;
}
.left-thing {
background-color: gray;
padding: 20px;
color: white;
display: flex;
flex-direction: column;
}
.left-thing button {
width: 100%;
.left-thing hr {
width: 100%;
flex: 0 0 auto;
}
.title {
padding: 10px;
font-size: 20px;
font-weight: bold;
flex: 0 0 auto;
}
.panel {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
flex: 0 0 auto;
}
.panel button {
flex: 0 0 32%;
height: 32px;
margin-top: 4px;
margin-bottom: 4px;
font-size: 14px;
margin-left: 3px;
margin-right: 3px;
}
.info {
padding: 10px;
flex: 0 0 auto;
}
.flags {
padding: 10px;
}
.log {
flex: 1 1 auto;
position: relative;
}
.log-content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
}
.bold {
font-weight: bold;
}
.clear-log {
font-size: 14px;
color: #eeeeee;
cursor: pointer;
}
.right-thing {
padding: 20px;
overflow: auto;
}
+11 -2
View File
@@ -31,19 +31,28 @@ export function buildData(n = 1000) {
//------------------------------------------------------------------------------
// Measuring helpers
//------------------------------------------------------------------------------
export function formatNumber(n) {
return Number(n).toFixed();
}
let startTime;
let lastMeasure;
export function startMeasure(descr) {
startTime = performance.now();
lastMeasure = descr;
}
export function stopMeasure() {
export function stopMeasure(cb) {
let last = lastMeasure;
if (lastMeasure) {
window.setTimeout(function() {
lastMeasure = null;
const stop = performance.now();
console.log(`[${last}] took ${Number(stop - startTime).toFixed()}ms`);
const delta = stop - startTime;
const msg = `[${last}] took ${formatNumber(delta)}ms`;
console.log(msg);
if (cb) {
cb({msg, delta});
}
}, 0);
}
}
+114 -22
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js";
import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------
// Counter Widget
@@ -45,29 +50,36 @@ Vue.component("my-message", {
const App = {
name: "App",
data() {
return { messages: [] };
return { messages: [], multipleFlag: false, clearAfterFlag: false };
},
mounted() {
this.log(`Benchmarking Vue v${Vue.version}`);
},
methods: {
addMessages(n) {
startMeasure("add " + n);
const newMessages = buildData(n);
this.messages.push.apply(this.messages, newMessages);
stopMeasure();
this.benchmark("add " + n, () => {
const newMessages = buildData(n);
this.messages.push.apply(this.messages, newMessages);
});
},
clear() {
startMeasure("clear");
this.messages = [];
stopMeasure();
this._benchmark("clear", () => {
this.messages = [];
});
},
clearLog() {
this.$refs.log.innerHTML = "";
},
updateSomeMessages() {
startMeasure("update every 10th");
const messages = this.messages;
for (let i = 0; i < this.messages.length; i += 10) {
messages[i].author += "!!!";
}
stopMeasure();
this.benchmark("update every 10th", () => {
const messages = this.messages;
for (let i = 0; i < this.messages.length; i += 10) {
messages[i].author += "!!!";
}
});
},
removeMessage(id) {
@@ -75,18 +87,98 @@ const App = {
const index = this.messages.findIndex(m => m.id === id);
this.messages.splice(index, 1);
stopMeasure();
},
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.$refs.log.appendChild(div);
this.$refs.log.scrollTop = this.$refs.log.scrollHeight;
},
benchmark(message, fn, callback) {
if (this.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
},
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
},
template: `
<div class="main">
<div class="left-thing">
<div>Number of msg: {{messages.length}}</div>
<button @click="addMessages(100)">Add 100 messages</button>
<button @click="addMessages(1000)">Add 1000 messages</button>
<button @click="addMessages(10000)">Add 10000 messages</button>
<button @click="addMessages(50000)">Add 50000 messages</button>
<button @click="updateSomeMessages">Update every 10th message</button>
<button @click="clear">Clear</button>
<div class="title">Actions</div>
<div class="panel">
<button @click="addMessages(100)">Add 100 messages</button>
<button @click="addMessages(1000)">Add 1k messages</button>
<button @click="addMessages(10000)">Add 10k messages</button>
<button @click="addMessages(30000)">Add 30k messages</button>
<button @click="updateSomeMessages">Update every 10th message</button>
<button @click="clear">Clear</button>
</div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" v-model="multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" v-model="clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: {{messages.length}}</div>
<hr/>
<div class="title">Log <span class="clear-log" @click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" ref="log"/>
</div>
</div>
<div class="right-thing">
<div class="content">