Yayınlanma tarihi: 2 Ekim 2024
Yeni bir CSS özelliğini kullanmaya başlarken, bu özelliğin web sitelerinizin performansı üzerindeki olumlu veya olumsuz etkisini anlamanız önemlidir. @property
artık Temel Değer'de. Bu yayında, @property
'in performans üzerindeki etkisi ve olumsuz etkileri önlemek için neler yapabileceğiniz ele alınmaktadır.
PerfTestRunner
ile CSS performansını karşılaştırma
CSS'nin performansını karşılaştırmak için "CSS Seçici Karşılaştırması" test paketini oluşturduk. Chromium'un PerfTestRunner
tarafından desteklenir ve CSS'nin performans üzerindeki etkisini karşılaştırmalı olarak gösterir. Chromium'un temel oluşturma motoru olan Blink, dahili performans testleri için bu PerfTestRunner
değerini kullanır.
Koşucu, testler için kullanılan bir measureRunsPerSecond
yöntemi içerir. Saniye başına çalıştırma sayısı ne kadar yüksek olursa o kadar iyidir. Bu kitaplıkla yapılan temel bir measureRunsPerSecond
karşılaştırması aşağıdaki gibi görünür:
const testResults = PerfTestRunner.measureRunsPerSecond({
"Test Description",
iterationCount: 5,
bootstrap: function() {
// Code to execute before all iterations run
// For example, you can inject a style sheet here
},
setup: function() {
// Code to execute before a single iteration
},
run: function() {
// The actual test that gets run and measured.
// A typical test adjusts something on the page causing a style or layout invalidation
},
tearDown: function() {
// Code to execute after a single iteration has finished
// For example, undo DOM adjustments made within run()
},
done: function() {
// Code to be run after all iterations have finished.
// For example, remove the style sheets that were injected in the bootstrap phase
},
});
measureRunsPerSecond
ile ilgili her seçenek, kod bloğundaki yorumlar aracılığıyla açıklanır. run
işlevi, ölçülen temel parçadır.
CSS seçici karşılaştırmaları için DOM ağacı gerekir
CSS seçicilerinin performansı DOM'un boyutuna da bağlı olduğundan bu karşılaştırmalar için makul boyutta bir DOM ağacı gerekir. Bu DOM ağacı manuel olarak oluşturulmak yerine oluşturulur.
Örneğin, aşağıdaki makeTree
işlevi @property
karşılaştırmalarının bir parçasıdır. Her biri içinde bazı alt öğeler bulunan 1.000 öğeden oluşan bir ağaç oluşturur.
const $container = document.querySelector('#container');
function makeTree(parentEl, numSiblings) {
for (var i = 0; i <= numSiblings; i++) {
$container.appendChild(
createElement('div', {
className: `tagDiv wrap${i}`,
innerHTML: `<div class="tagDiv layer1" data-div="layer1">
<div class="tagDiv layer2">
<ul class="tagUl">
<li class="tagLi"><b class="tagB"><a href="/" class="tagA link" data-select="link">Select</a></b></li>
</ul>
</div>
</div>`,
})
);
}
}
makeTree($container, 1000);
CSS seçici karşılaştırmaları DOM ağacını değiştirmediğinden bu ağaç oluşturma işlemi yalnızca bir kez, karşılaştırmalar çalıştırılmadan önce yürütülür.
Karşılaştırma yapma
Test paketinin bir parçası olan bir karşılaştırma testi çalıştırmak için önce bir web sunucusu başlatmanız gerekir:
npm run start
Başladıktan sonra, yayınlanmış URL'sinde karşılaştırmayı ziyaret edebilir ve window.startTest()
'ü manuel olarak çalıştırabilirsiniz.
Bu karşılaştırmaları, herhangi bir uzantı veya başka faktörler olmadan tek başına çalıştırmak için, iletilen karşılaştırmayı yükleyip yürütmek üzere Puppeteer CLI'den tetiklenir.
Bu @property
karşılaştırmaları özellikle yapmak için URL'sinde ilgili sayfayı ziyaret etmek yerine http://localhost:3000/benchmarks/at-rule/at-property.html
CLI'de aşağıdaki komutları çağırın:
npm run benchmark at-rule/at-property
Bu işlem, sayfayı Puppeteer aracılığıyla yükler, window.startTest()
işlevini otomatik olarak çağırır ve sonuçları bildirir.
CSS özelliklerinin performansını karşılaştırma
Bir CSS özelliğinin performansını karşılaştırmak için stil geçersizliği ve ardından tarayıcının yapması gereken stili yeniden hesaplama görevini ne kadar hızlı işleyebileceğini karşılaştırırsınız.
Stil geçersizliği, DOM'daki bir değişikliğe yanıt olarak stillerinin yeniden hesaplanması gereken öğeleri işaretleme işlemidir. Olası en basit yaklaşım, her değişiklik için her şeyi geçersiz kılmaktır.
Bu durumda, devralan CSS özellikleri ile devralmayan CSS özellikleri arasında bir ayrım yapılması gerekir.
- Bir CSS özelliği, hedeflenen öğede devralınan değişiklikleri içeriyorsa hedeflenen öğenin altındaki alt ağaçtaki tüm öğelerin stillerinin de değiştirilmesi gerekir.
- Devralmayan bir CSS özelliği, hedeflenen bir öğede değiştiğinde yalnızca söz konusu öğenin stilleri geçersiz kılınır.
Miras alan mülkleri miras almayan mülklerle karşılaştırmak adil olmayacağından, çalıştırılacak iki referans grubu vardır:
- Devralan mülkler içeren bir karşılaştırma grubu.
- Devralmayan özellikler içeren bir dizi karşılaştırma ölçütü.
Karşılaştırma yapılacak mülkleri dikkatlice seçmeniz önemlidir. Bazı özellikler (accent-color
gibi) yalnızca stilleri geçersiz kılarken düzen veya boya gibi diğer öğeleri de geçersiz kılan birçok özellik (writing-mode
gibi) vardır. Yalnızca stilleri geçersiz kılan mülkleri istiyorsunuz.
Bunu belirlemek için Blink'in CSS özellikleri listesinde arama yapın. Her mülkte, geçersiz kılınanların listeleneceği bir invalidate
alanı bulunur.
Ayrıca, bu tür bir mülkle karşılaştırma yapmak sonuçları çarpıtacağından, listeden independent
olarak işaretlenmemiş bir mülk seçmek de önemlidir. Bağımsız mülkler, diğer mülkler veya işaretler üzerinde herhangi bir yan etki oluşturmaz. Yalnızca bağımsız mülkler değiştiğinde Blink, alt öğenin stilini kopyalayan ve bu kopyadaki yeni değerleri güncelleyen hızlı bir kod yolu kullanır. Bu yaklaşım, tam bir yeniden hesaplama yapmaktan daha hızlıdır.
Devralınan CSS özelliklerinin performansını karşılaştırma
İlk karşılaştırma grubu, devralınan CSS özelliklerine odaklanır. Test etmek ve birbiriyle karşılaştırmak için devralınan üç tür mülk vardır:
- Aşağıdakileri devralan normal bir mülk:
accent-color
. - Kayıtlı olmayan özel mülk:
--unregistered
. inherits: true
:--registered
adresine kayıtlı özel mülk.
Kayıtlı olmayan özel mülkler varsayılan olarak devraldığı için bu listeye eklenir.
Daha önce de belirtildiği gibi, devralan mülk, yalnızca stilleri geçersiz kılan ve independent
olarak işaretlenmeyen bir mülk olacak şekilde dikkatlice seçilmiştir.
Kayıtlı özel mülkler söz konusu olduğunda, bu çalıştırmada yalnızca inherits
tanımlayıcısının değeri true olarak ayarlanmış olan mülkler test edilir. inherits
açıklayıcısı, mülkün alt öğelere devralınıp devralınmayacağını belirler. Kaydın kendisi karşılaştırmanın bir parçası olmadığından, bu mülkün CSS @property
veya JavaScript CSS.registerProperty
aracılığıyla kaydedilip kaydedilmediği önemli değildir.
Karşılaştırmalar
Daha önce de belirtildiği gibi, karşılaştırmaları içeren sayfa, bir DOM ağacı oluşturarak başlar. Böylece sayfa, değişikliklerin etkisini görecek kadar büyük bir düğüm grubuna sahip olur.
Her karşılaştırma, bir mülkün değerini değiştirdikten sonra stil geçersizliğini tetikler. Karşılaştırma temel olarak, geçersiz kılınan tüm stillerin yeniden değerlendirilmesi için sayfanın bir sonraki yeniden hesaplanmasının ne kadar süreceğini ölçer.
Tek bir karşılaştırma tamamlandıktan sonra, yerleştirilen tüm stiller sıfırlanır ve bir sonraki karşılaştırmanın başlaması sağlanır.
Örneğin, --registered
stilini değiştirmenin performansını ölçen karşılaştırma şu şekilde görünür:
let i = 0;
PerfTestRunner.measureRunsPerSecond({
description,
iterationCount: 5,
bootstrap: () => {
setCSS(`@property --registered {
syntax: "<number>";
initial-value: 0;
inherits: true;
}`);
},
setup: function() {
// NO-OP
},
run: function() {
document.documentElement.style.setProperty('--registered', i);
window.getComputedStyle(document.documentElement).getPropertyValue('--registered'); // Force style recalculation
i = (i == 0) ? 1 : 0;
},
teardown: () => {
document.documentElement.style.removeProperty('--registered');
},
done: (results) => {
resetCSS();
resolve(results);
},
});
Diğer mülk türlerini test eden karşılaştırmalar aynı şekilde çalışır ancak kaydedilecek mülk olmadığından boş bir bootstrap
içerir.
Sonuçlar
Bu karşılaştırmaları 16 GB RAM'e sahip 2021 MacBook Pro (Apple M1 Pro) üzerinde 20 iterasyonla çalıştırdığınızda aşağıdaki ortalamalar elde edilir:
- Devralınan normal mülk (
accent-color
): Saniyede 163 çalıştırma (= çalıştırma başına 6,13 ms) - Kayıtlı olmayan özel mülk (
--unregistered
): Saniyede 256 çalıştırma (= çalıştırma başına 3,90 ms) inherits: true
ile kayıtlı özel mülk (--registered
): Saniyede 252 çalıştırma (= çalıştırma başına 3,96 ms)
Birden çok çalıştırmayla karşılaştırmalar benzer sonuçlar verir.
Sonuçlar, özel bir mülkü kaydetmenin, özel mülkü kaydetmemeye kıyasla çok küçük bir maliyete neden olduğunu göstermektedir. Devralan kayıtlı özel özellikler, kaydedilmemiş özel mülklerin hızında% 98 oranında çalışır. Mutlak sayılarla, özel mülkün kaydedilmesi 0,06 ms ek yük ekler.
Devralmayan CSS özelliklerinin performansını karşılaştırma
Karşılaştırma için diğer özellikler, devralınmayan mülklerdir. Burada karşılaştırma yapılabilen yalnızca iki tür mülk vardır:
- Miras almayan normal mülk:
z-index
. inherits: false
ile kayıtlı bir özel mülk:--registered-no-inherit
.
Kayıtlı olmayan özel mülkler her zaman devralındığından bu karşılaştırmaya dahil edilemez.
Karşılaştırmalar
Karşılaştırmalar önceki senaryolara çok benzer. --registered-no-inherit
içeren test için karşılaştırmanın bootstrap
aşamasına aşağıdaki mülk kaydı eklenir:
@property --registered-no-inherit {
syntax: "<number>";
initial-value: 0;
inherits: false;
}
Sonuçlar
Bu karşılaştırmaları 16 GB RAM'e sahip 2021 MacBook Pro (Apple M1 Pro) üzerinde 20 iterasyonla çalıştırdığınızda aşağıdaki ortalamalar elde edilir:
- Devralmayan normal mülk: Saniyede 290.269 çalıştırma (= çalıştırma başına 3,44 µs)
- Devralmayan kayıtlı Özel Mülk: Saniyede 214.110 çalıştırma (= çalıştırma başına 4,67 μs)
Test, birden fazla çalıştırma boyunca tekrarlandı ve tipik sonuçlar bunlardı.
Burada dikkat çeken nokta, devralmayan mülklerin devralan mülklerden çok daha hızlı performans göstermesidir. Bu durum, normal mülkler için beklenebilir olsa da özel mülkler için de geçerlidir.
- Normal mülkler için çalıştırma sayısı saniyede 163 çalıştırmadan saniyede 290 binin üzerine çıktı. Bu da performansta %1.780 artış anlamına geliyor.
- Özel mülklerde, saniyede 252 çalıştırma olan koşu sayısı 214 binin üzerine çıktı. Bu da performansta% 848 artış anlamına geliyor.
Bundan temel çıkarım, özel mülk kaydederken inherits: false
kullanmanın anlamlı bir etkiye sahip olmasıdır. Özel mülkünüzü inherits: false
ile kaydettirebiliyorsanız kesinlikle bunu yapmalısınız.
Bonus karşılaştırma: Birden fazla özel mülk kaydı
Karşılaştırılacak bir başka ilginç nokta da, çok sayıda özel mülk kaydının etkisidir. Bunu yapmak için testi, önceden 25.000 diğer özel mülk kaydı yapan --registered-no-inherit
ile yeniden çalıştırın. Bu özel özellikler :root
'te kullanılır.
Bu kayıtlar karşılaştırmanın setup
adımında yapılır:
setup: () => {
const propertyRegistrations = [];
const declarations = [];
for (let i = 0; i < 25000; i++) {
propertyRegistrations.push(`@property --custom-${i} { syntax: "<number>"; initial-value: 0; inherits: true; }`);
declarations.push(`--custom-${i}: ${Math.random()}`);
}
setCSS(`${propertyRegistrations.join("\n")}
:root {
${declarations.join("\n")}
}`);
},
Bu karşılaştırma için saniye başına çalıştırma sayısı, "Devralmayan kayıtlı özel mülk" sonucuna çok benzer (saniye başına 214.110 çalıştırma ve saniye başına 213.158 çalıştırma) ancak dikkate alınması gereken ilginç kısım bu değildir. Sonuçta bir özel özelliğin değiştirilmesi, diğer mülklerdeki kayıtlardan etkilenmez.
Bu testin ilginç kısmı, kayıtların etkisini ölçmektir. DevTools'a döndüğünüzde, 25.000 özel mülk kaydının ilk stil yeniden hesaplama maliyetinin 30ms
'ün biraz üzerinde olduğunu görebilirsiniz. Bu işlem tamamlandıktan sonra, bu kayıtların varlığının başka bir etkisi olmaz.
Sonuç ve ana fikirler
Özetle, tüm bunlardan çıkarılacak üç ders vardır:
@property
ile özel bir mülk kaydettirmenin biraz performans maliyeti vardır. Özel mülkleri kaydettiğinizde tüm potansiyellerini açığa çıkardığınız için bu maliyet genellikle göz ardı edilebilir. Bunu yapmadan bu potansiyele ulaşmak mümkün değildir.Özel bir mülkü kaydederken
inherits: false
kullanmak anlamlı bir etki sağlar. Bu sayede mülkün devralınmasını engellemiş olursunuz. Bu nedenle, mülkün değeri değiştiğinde alt ağacın tamamı yerine yalnızca eşleşen öğenin stilleri etkilenir.@property
kayıtlarının çok az veya çok sayıda olması, stil yeniden hesaplama işlemini etkilemez. Kayıt işleminde yalnızca çok küçük bir ön maliyet vardır ancak bu işlem tamamlandıktan sonra sorun yaşamazsınız.