看看这个例子:
const startTime = performance.now();
setTimeout(() => console.log(`taken time: ${performance.now() - startTime}ms`))
for(let i = 0; i < 1000; i++){
const element = document.createElement("div");
document.body.appendChild(element);
element.textContent = `Текст n${i}`;
window.getComputedStyle(document.body);
}
看看另一个:
const startTime = performance.now();
setTimeout(() => console.log(`taken time: ${performance.now() - startTime}ms`))
for(let i = 0; i < 1000; i++){
const element = document.createElement("div");
document.body.appendChild(element);
element.textContent = `Текст n${i}`;
window.getComputedStyle(document.body).width;
}
差异很小:在第一种情况下,我只调用window.getComputedStyle(document.body)
而不获取属性,在第二种情况下我使用width
属性进行调用。因此,在第一个例子中,我们没有看到重新计算样式和回流,但在第二个例子中我们看到了相反的情况。为什么?
getComputedStyle(element)
实际上返回了一个live对象。
const el = document.querySelector("div");
const style = getComputedStyle(el);
console.log("original color:", style.color);
el.classList.add("colored");
// we use the same 'style' object as in the first step
console.log("with class color:", style.color);
.colored {
color: orange;
}
<div>hello</div>
获取此对象不需要执行完全的recalc,只有其getters
会强制它。
但现在的浏览器甚至比这更聪明,它们甚至不会触发CSSOM树之外的某些属性的回流,从而影响被检查的CSSTyleDeclaration对象。
例如,在下面的示例中,我们可以看到,从检查器内部元素的CSSTYLEDeclaration中获取fontSize
属性将强制回流影响检查器,而在外部获取一个表单则不会,因为与width
不同,fontSize
属性只受祖先的影响,而不受兄弟姐妹的影响。
function testReflow(func) {
return new Promise( (res, rej) => {
const elem = document.querySelector(".reflow-tester");
// set "intermediary" values
elem.style.opacity = 1;
elem.style.transition = "none";
try { func(elem); } catch(err) { rej(err) }
elem.style.opacity = 0;
elem.style.transition = "opacity 0.01s";
// if the tested func does trigger a reflow
// the transition will start from 1 to 0
// otherwise it won't happen (from 0 to 0)
elem.addEventListener("transitionstart", (evt) => {
res(true); // let the caller know the result
}, { once: true });
// if the transition didn't start in 100ms, it didn't cause a reflow
setTimeout(() => res(false), 100);
});
}
(async () => {
await new Promise(res=>setTimeout(res, 1000));
let styles;
const gCS_recalc_inner = await testReflow(() => {
return (styles = getComputedStyle(document.querySelector("#inner")));
});
console.log("getComputedStyle inner recalc:", gCS_recalc_inner);
const gCS_inner_prop_recalc = await testReflow(() => {
return styles.fontSize;
});
console.log("getComputedStyle inner getter recalc:", gCS_inner_prop_recalc);
const gCS_recalc_outer = await testReflow(() => {
return (styles = getComputedStyle(document.querySelector("#outer")));
});
console.log("getComputedStyle outer recalc:", gCS_recalc_outer);
const gCS_outer_prop_recalc = await testReflow(() => {
return styles.fontSize;
});
console.log("getComputedStyle outer getter recalc:", gCS_outer_prop_recalc);
})().catch(console.error);
.reflow-tester {
opacity: 0;
}
.hidden {
display: none;
}
<div class="reflow-tester">Tester<div id="inner"></div></div>
<div id="outer"></div>
在这两种情况下都会触发对width
的相同检查,因为width
可能会受到兄弟姐妹的影响:
function testReflow(func) {
return new Promise( (res, rej) => {
const elem = document.querySelector(".reflow-tester");
// set "intermediary" values
elem.style.opacity = 1;
elem.style.transition = "none";
try { func(elem); } catch(err) { rej(err) }
elem.style.opacity = 0;
elem.style.transition = "opacity 0.01s";
// if the tested func does trigger a reflow
// the transition will start from 1 to 0
// otherwise it won't happen (from 0 to 0)
elem.addEventListener("transitionstart", (evt) => {
res(true); // let the caller know the result
}, { once: true });
// if the transition didn't start in 100ms, it didn't cause a reflow
setTimeout(() => res(false), 100);
});
}
(async () => {
await new Promise(res=>setTimeout(res, 1000));
let styles;
const gCS_recalc_inner = await testReflow(() => {
return (styles = getComputedStyle(document.querySelector("#inner")));
});
console.log("getComputedStyle inner recalc:", gCS_recalc_inner);
const gCS_inner_prop_recalc = await testReflow(() => {
return styles.width;
});
console.log("getComputedStyle inner getter recalc:", gCS_inner_prop_recalc);
const gCS_recalc_outer = await testReflow(() => {
return (styles = getComputedStyle(document.querySelector("#outer")));
});
console.log("getComputedStyle outer recalc:", gCS_recalc_outer);
const gCS_outer_prop_recalc = await testReflow(() => {
return styles.width;
});
console.log("getComputedStyle outer getter recalc:", gCS_outer_prop_recalc);
})().catch(console.error);
.reflow-tester {
opacity: 0;
}
.hidden {
display: none;
}
<div class="reflow-tester">Tester<div id="inner"></div></div>
<div id="outer"></div>