问题
I have two pieces of code below
Snippet#1
const doubleWidth = (element) => {
const width = element.offsetWidth;
element.style.width = `${width * 2}px`;
};
button.addEventListener('click', (event) => {
boxes.forEach(doubleWidth);
});
Snippet#2
button.addEventListener('click', (event) => {
var widths = boxes.map(item => item.offsetWidth);
boxes.forEach((element, index) => {
element.style.width = `${widths[index] * 2}px`;
});
});
Snippet #1 has a lot 48ms compared to snippet #2 which is only 18.4ms. Why is that behaviour?
After all, I am still doing two operation of calculating and settings ( which forcers reflows ) each.
Here's the complete code - https://codepen.io/kushalmahajan/pen/mjXVqp?editors=0010
Update - So , let me explain a bit more
In Snippet #1, I see each time a pattern like calculate, Reset, Calculate, Reset ...so forth
In Snippet #2. That's not the case.
Please base you answers around render pipeline please
回答1:
in Snippet 1 each time you are executing 2 commands:
const width = element.offsetWidth;
element.style.width = `${width * 2}px`;
while in Snippet 2 each time you are executing only 1 command:
element.style.width = `${widths[index] * 2}px`;
Therefore I think it's natural to have more than double the time for the execution. depending on how much time it take to execute the following command:
const width = element.offsetWidth;
For a fair comparison between the 2 snippets I would suggest removing the above command from the loop and check how long it would take to execute snippet 1 in that case.
I hope that helps.
回答2:
I'm not sure about the reason, but apparently if you store offsetWidth
first, it doesn't matter performance-wise whether you use forEach
or map
: here's a pen to illustrate this behavior.
You can see that I timed three combinations:
forEach
with immediately gettingoffsetWidth
and settingwidth
forEach
with storingoffsetWidth
and the corresponding element first and then setting width it in a secondforEach
map
with storingoffsetWidth
and then settingwidth
on the elements in aforEach
loop
Option 2. an 3. were basically the same performance wise. Judging from that, I would say that the combination of getting offsetWidth
and setting width
is a performance bottleneck. Can't really tell you much more than that, sorry!
window.onload = () => {
const boxes = Array.from(document.querySelectorAll('.box'));
document.getElementById('double-sizes-forEach')
.addEventListener('click', (event) => {
console.time('Double Sizes ForEach');
boxes.forEach((element, index) => {
const width = element.offsetWidth;
element.style.width = `${width * 2}px`;
});
console.timeEnd('Double Sizes ForEach');
});
document.getElementById('double-sizes-forEach-2')
.addEventListener('click', (event) => {
console.time('Double Sizes ForEach 2');
let a = [];
boxes.forEach((element, index) => {
a.push([element, element.offsetWidth]);
});
a.forEach(([e, w]) => {
e.style.width = `${w * 2}px`;
});
console.timeEnd('Double Sizes ForEach 2');
});
document.getElementById('double-sizes-map')
.addEventListener('click', (event) => {
console.time('Double Sizes Map');
var widths = boxes.map(item => item.offsetWidth);
boxes.forEach((element, index) => {
element.style.width = `${widths[index] * 2}px`;
});
console.timeEnd('Double Sizes Map');
});
};
OUTPUT:
Double Sizes ForEach: 12.341064453125ms
Double Sizes ForEach 2: 0.539794921875ms
Double Sizes Map: 0.590087890625ms
NOTES:
- what forces layout/reflow
- article on layout performance issues and layout thrashing
The second article argues that you should always separate changing style (e.g. setting width
) and taking measurements (e.g. getting offsetWidth
) to avoid layout thrashing. Seems that's the problem in your code as well.
来源:https://stackoverflow.com/questions/51609846/checking-width-and-setting-width-inside-foreach-loop-performance