Performansı Artırmak için Sass'ta Kullanılmayan CSS'yi Kullanma
Yayınlanan: 2022-03-10Modern ön uç geliştirmede geliştiriciler, ölçeklenebilir ve sürdürülebilir CSS yazmayı hedeflemelidir. Aksi takdirde, kod tabanı büyüdükçe ve daha fazla geliştirici katkıda bulundukça, kademeli ve seçici özgüllük gibi özellikler üzerindeki kontrolü kaybetme riskiyle karşı karşıya kalırlar.
Bunu başarmanın bir yolu, CSS'yi sayfa bağlamı etrafında düzenlemek yerine yapıyı (ızgara sistemleri, boşluklar, genişlikler vb.) dekorasyondan (yazı tipleri, marka, renk vb.)
Bu nedenle, aşağıdaki gibi CSS sınıf adları:
-
.blog-right-column
-
.latest_topics_list
-
.job-vacancy-ad
Aynı CSS stillerini uygulayan ancak belirli bir bağlama bağlı olmayan daha fazla yeniden kullanılabilir alternatiflerle değiştirilir:
-
.col-md-4
-
.list-group
-
.card
Bu yaklaşım genellikle Bootstrap, Foundation gibi bir Sass çerçevesinin veya giderek daha sık olarak projeye daha iyi uyacak şekilde şekillendirilebilen ısmarlama bir çerçevenin yardımıyla uygulanır.
Şimdi bir kalıplar çerçevesinden özenle seçilmiş CSS sınıflarını, UI bileşenlerini ve yardımcı program sınıflarını kullanıyoruz. Aşağıdaki örnek, Bootstrap kullanılarak oluşturulmuş, dikey olarak yığınlanan ve ardından md kesme noktasına ulaşıldığında 3 sütun düzenine geçen ortak bir ızgara sistemini göstermektedir.
<div class="container"> <div class="row"> <div class="col-12 col-md-4">Column 1</div> <div class="col-12 col-md-4">Column 2</div> <div class="col-12 col-md-4">Column 3</div> </div> </div>
Bu kalıbı oluşturmak için burada .col-12
ve .col-md-4
gibi programlı olarak oluşturulmuş sınıflar kullanılır. Peki ya .col-1
ila .col-11
, .col-lg-4
, .col-md-6
veya .col-sm-12
? Bunların hepsi, kullanımda olmamasına rağmen, derlenmiş CSS stil sayfasına dahil edilecek, tarayıcı tarafından indirilecek ve ayrıştırılacak sınıfların örnekleridir.
Bu makalede, kullanılmayan CSS'nin sayfa yükleme hızları üzerindeki etkisini keşfederek başlayacağız. Daha sonra, kendi Sass odaklı çözümümü takip ederek, onu stil sayfalarından çıkarmak için mevcut bazı çözümlere değineceğiz.
Kullanılmayan CSS Sınıflarının Etkisini Ölçme
Güçlü kanatlar Sheffield United'a bayılıyor olsam da, web sitelerinin CSS'si, gzip ile sıkıştırıldığında bile 105 kb'ye ulaşan 568 kb'lik tek bir küçültülmüş dosyada toplanmıştır. Bu çok gibi görünüyor.
Bu CSS'nin gerçekte ne kadarının ana sayfalarında kullanıldığını görelim mi? Hızlı bir Google araması, işe uygun birçok çevrimiçi araç ortaya çıkarır, ancak Chrome'da doğrudan Chrome'un DevTools'undan çalıştırılabilen kapsama aracını kullanmayı tercih ederim. Bir tur atalım.
Sonuçlar, 568 kb stil sayfasından yalnızca 30 kb CSS'nin ana sayfa tarafından kullanıldığını ve kalan 538 kb'nin web sitesinin geri kalanı için gereken stillerle ilgili olduğunu gösteriyor. Bu, CSS'nin %94,8'inin kullanılmadığı anlamına gelir.
CSS, bir web sayfasının, bir tarayıcının sayfa oluşturmaya başlamadan önce tamamlaması gereken tüm farklı adımları içeren kritik oluşturma yolunun bir parçasıdır. Bu, CSS'yi oluşturmayı engelleyen bir varlık yapar.
Bunu akılda tutarak, Sheffield United'ın web sitesini iyi bir 3G bağlantısı kullanarak yüklerken, CSS'nin indirilmesi ve sayfa oluşturma işleminin başlayabilmesi için tam 1,15 saniye gerekir. Bu bir problem.
Google da bunu fark etti. Çevrimiçi veya tarayıcınız aracılığıyla bir Lighthouse denetimi çalıştırırken, kullanılmayan CSS'yi kaldırarak yapılabilecek olası yükleme süresi ve dosya boyutu tasarrufları vurgulanır.
Mevcut Çözümler
Amaç, hangi CSS sınıflarının gerekli olmadığını belirlemek ve bunları stil sayfasından kaldırmaktır. Bu süreci otomatikleştirmeye çalışan mevcut çözümler mevcuttur. Genellikle bir Node.js derleme betiği aracılığıyla veya Gulp gibi görev çalıştırıcılar aracılığıyla kullanılabilirler. Bunlar şunları içerir:
- UNCSS
- PurifyCSS
- PurgeCSS
Bunlar genellikle benzer şekilde çalışır:
- Bulld'da, web sitesine başsız bir tarayıcı (Örn: kuklacı) veya DOM öykünmesi (Örn: jsdom) aracılığıyla erişilir.
- Sayfanın HTML öğelerine göre kullanılmayan CSS tanımlanır.
- Bu, stil sayfasından kaldırılır ve yalnızca gerekli olanı bırakılır.
Bu otomatik araçlar tamamen geçerli olsa ve birçoğunu bir dizi ticari projede başarılı bir şekilde kullanmış olsam da, yol boyunca paylaşmaya değer birkaç dezavantajla karşılaştım:
- Sınıf adları '@' veya '/' gibi özel karakterler içeriyorsa, bunlar bazı özel kodlar yazılmadan tanınmayabilir. Sınıf adlarını
u-width-6/12@lg
gibi duyarlı soneklerle yapılandırmayı içeren Harry Roberts'ın BEM-IT'ini kullanıyorum, bu yüzden bu sorunu daha önce ele almıştım. - Web sitesi otomatik dağıtım kullanıyorsa, özellikle çok sayıda sayfanız ve çok sayıda CSS'niz varsa, oluşturma sürecini yavaşlatabilir.
- Bu araçlarla ilgili bilgilerin ekip arasında paylaşılması gerekir, aksi takdirde üretim stil sayfalarında CSS gizemli bir şekilde bulunmadığında kafa karışıklığı ve hayal kırıklığı olabilir.
- Web sitenizde çalışan birçok 3. taraf komut dosyası varsa, bazen başsız bir tarayıcıda açıldığında bunlar iyi çalışmaz ve filtreleme işleminde hatalara neden olabilir. Bu nedenle, genellikle, kurulumunuza bağlı olarak zor olabilecek, başsız bir tarayıcı algılandığında herhangi bir 3. taraf komut dosyasını hariç tutmak için özel kod yazmanız gerekir.
- Genel olarak, bu tür araçlar karmaşıktır ve yapım sürecine birçok ekstra bağımlılık getirir. Tüm 3. taraf bağımlılıklarında olduğu gibi, bu başka birinin koduna güvenmek anlamına gelir.
Bu noktaları göz önünde bulundurarak kendime bir soru yönelttim:
Sadece Sass kullanarak, derlediğimiz Sass'ı daha iyi ele almak, böylece Sass'taki kaynak sınıfları kabaca silmeye başvurmadan, kullanılmayan herhangi bir CSS'nin hariç tutulması mümkün müdür?
Spoiler uyarısı: Cevap evet. İşte aklıma gelen şey.
Sass Odaklı Çözüm
Çözümün, Sass'ın derlenmesi gereken şeyi seçmenin hızlı ve kolay bir yolunu sağlaması gerekirken, geliştirme sürecine daha fazla karmaşıklık eklemeyecek veya geliştiricilerin programlı olarak oluşturulmuş CSS gibi şeylerden yararlanmasını engellemeyecek kadar basit olmalıdır. sınıflar.
Başlamak için, derleme komut dosyaları ve buradan klonlayabileceğiniz birkaç örnek stil içeren bir depo var.
İpucu: Takılırsanız, ana daldaki tamamlanmış sürümle her zaman çapraz başvuru yapabilirsiniz.
depoya cd
yazın, npm install
çalıştırın ve ardından herhangi bir npm run build
. Bu, dist dizininde bir 55kb css dosyası oluşturmalıdır.
Daha sonra web tarayıcınızda /dist/index.html
açarsanız, tıklandığında içeriğin bir kısmını ortaya çıkarmak için genişleyen oldukça standart bir bileşen görmelisiniz. Bunu gerçek ağ koşullarının uygulanacağı buradan da görüntüleyebilir, böylece kendi testlerinizi yapabilirsiniz.
Kısmi Düzeyde Filtreleme
Tipik bir SCSS kurulumunda, büyük olasılıkla tek bir bildirim dosyanız (örneğin: main.scss
) veya her sayfada bir (örneğin: index.scss
, products.scss
, contact.scss
) olacak ve çerçeve kısmidir. ithal edilmektedir. OOCSS ilkelerine göre, bu ithalatlar şöyle görünebilir:
örnek 1
/* Undecorated design patterns */ @import 'objects/box'; @import 'objects/container'; @import 'objects/layout'; /* UI components */ @import 'components/button'; @import 'components/expander'; @import 'components/typography'; /* Highly specific helper classes */ @import 'utilities/alignments'; @import 'utilities/widths';
Bu kısmi öğelerden herhangi biri kullanımda değilse, bu kullanılmayan CSS'yi filtrelemenin doğal yolu, içe aktarmayı devre dışı bırakmak olacaktır, bu da derlenmesini engelleyecektir.
Örneğin, yalnızca genişletici bileşeni kullanılıyorsa, bildirim genellikle aşağıdaki gibi görünür:
Örnek 2
/* Undecorated design patterns */ // @import 'objects/box'; // @import 'objects/container'; // @import 'objects/layout'; /* UI components */ // @import 'components/button'; @import 'components/expander'; // @import 'components/typography'; /* Highly specific helper classes */ // @import 'utilities/alignments'; // @import 'utilities/widths';
Ancak, OOCSS'ye göre, maksimum yeniden kullanılabilirliğe izin vermek için dekorasyonu yapıdan ayırıyoruz, bu nedenle genişleticinin doğru şekilde oluşturulması için diğer nesnelerden, bileşenden veya yardımcı program sınıflarından CSS gerektirebilir. Geliştirici, HTML'yi inceleyerek bu ilişkilerin farkında olmadıkça, bu bölümleri içe aktarmayı bilemeyebilir, bu nedenle gerekli sınıfların tümü derlenemez.
Depoda, dist/index.html
içindeki genişleticinin HTML'sine bakarsanız, durum böyle görünüyor. Kutu ve düzen nesnelerindeki stilleri, tipografi bileşenini ve genişlik ve hizalama yardımcı programlarını kullanır.
dist/index.html
<div class="c-expander"> <div class="o-box o-box--spacing-small c-expander__trigger c-expander__header" tabindex="0"> <div class="o-layout o-layout--fit u-flex-middle"> <div class="o-layout__item u-width-grow"> <h2 class="c-type-echo">Toggle Expander</h2> </div> <div class="o-layout__item u-width-shrink"> <div class="c-expander__header-icon"></div> </div> </div> </div> <div class="c-expander__content"> <div class="o-box o-box--spacing-small"> Lorum ipsum <p class="u-align-center"> <button class="c-expander__trigger c-button">Close</button> </p> </div> </div> </div>
Bu ilişkileri Sass'ın kendi içinde resmi hale getirerek gerçekleşmesini bekleyen bu sorunu çözelim, böylece bir bileşen içe aktarıldığında, bağımlılıklar da otomatik olarak içe aktarılacaktır. Bu şekilde, geliştirici artık başka neleri içe aktarmaları gerektiğini öğrenmek için HTML'yi denetlemek zorunda kalmaz.
Programlı İthalat Haritası
Bu bağımlılık sisteminin çalışması için, bildirim dosyasındaki @import
ifadelerinde yorum yapmak yerine, Sass mantığının kısmilerin derlenip derlenmeyeceğini dikte etmesi gerekecektir.
src/scss/settings
içinde, _imports.scss
adında yeni bir kısmi oluşturun, settings/_core.scss
içinde @import
yapın ve ardından aşağıdaki SCSS eşlemesini oluşturun:
src/scss/settings/_core.scss
@import 'breakpoints'; @import 'spacing'; @import 'imports';
src/scss/settings/_imports.scss
$imports: ( object: ( 'box', 'container', 'layout' ), component: ( 'button', 'expander', 'typography' ), utility: ( 'alignments', 'widths' ) );
Bu harita, örnek 1'deki içe aktarma bildirimiyle aynı role sahip olacaktır.
Örnek 4
$imports: ( object: ( //'box', //'container', //'layout' ), component: ( //'button', 'expander', //'typography' ), utility: ( //'alignments', //'widths' ) );
Standart bir @imports
kümesi gibi davranmalıdır, çünkü belirli kısımlar yorumlanırsa (yukarıdaki gibi), o zaman bu kod derlemede derlenmemelidir.
Ancak, bağımlılıkları otomatik olarak içe aktarmak istediğimiz için, doğru koşullar altında bu haritayı da göz ardı edebilmeliyiz.
Render Mixin
Biraz Sass mantığı eklemeye başlayalım. src/scss/tools
içinde _render.scss
oluşturun ve ardından onun @import
tools/_core.scss
öğesine ekleyin.
Dosyada render()
adında boş bir mixin oluşturun.
src/scss/tools/_render.scss
@mixin render() { }
Karışımda, aşağıdakileri yapan Sass yazmamız gerekiyor:
- render()
“Merhaba,$imports
, hava güzel değil mi? Söylesene, haritanda konteyner nesnesi var mı?" - $ithalat
false
- render()
“Bu çok yazık, o zaman derlenmeyecek gibi görünüyor. Düğme bileşenine ne dersiniz?” - $ithalat
true
- render()
"Güzel! O zaman derlenen düğme budur. Karına benden selam söyle.”
Sass'ta bu şu anlama gelir:
src/scss/tools/_render.scss
@mixin render($name, $layer) { @if(index(map-get($imports, $layer), $name)) { @content; } }
Temel olarak, kısminin $imports
değişkenine dahil edilip edilmediğini kontrol edin ve öyleyse, onu bir içerik bloğunu mixin'e geçirmemize izin veren Sass' @content
yönergesini kullanarak oluşturun.
Bunu şöyle kullanırdık:
Örnek 5
@include render('button', 'component') { .c-button { // styles et al } // any other class declarations }
Bu karışımı kullanmadan önce, üzerinde yapabileceğimiz küçük bir iyileştirme var. Katman adı (nesne, bileşen, yardımcı program, vb.) güvenle tahmin edebileceğimiz bir şeydir, bu nedenle işleri biraz düzene sokma fırsatımız olur.
Oluşturma karıştırma bildiriminden önce, $layer
adlı bir değişken oluşturun ve aynı adlı değişkeni mixins parametrelerinden kaldırın. Şöyle:
src/scss/tools/_render.scss
$layer: null !default; @mixin render($name) { @if(index(map-get($imports, $layer), $name)) { @content; } }
Şimdi, nesnelerin, bileşenlerin ve yardımcı programın @imports
bulunduğu _core.scss
kısmi bölümlerinde, bu değişkenleri aşağıdaki değerlere yeniden bildirin; içe aktarılan CSS sınıflarının türünü temsil eder.
src/scss/objects/_core.scss
$layer: 'object'; @import 'box'; @import 'container'; @import 'layout';
src/scss/components/_core.scss
$layer: 'component'; @import 'button'; @import 'expander'; @import 'typography';
src/scss/utilities/_core.scss
$layer: 'utility'; @import 'alignments'; @import 'widths';
Bu şekilde, render()
karışımını kullandığımızda tek yapmamız gereken kısmi adı bildirmek.
render()
karışımını aşağıdaki gibi her nesne, bileşen ve yardımcı program sınıfı bildiriminin etrafına sarın. Bu size kısmi başına bir render mixin kullanımı verecektir.
Örneğin:
src/scss/objects/_layout.scss
@include render('button') { .c-button { // styles et al } // any other class declarations }
src/scss/components/_button.scss
@include render('button') { .c-button { // styles et al } // any other class declarations }
Not: utilities/_widths.scss
için, render()
işlevini kısmi tüm etrafına sarmak derlemede hata verecektir, çünkü Sass'ta mixin bildirimlerini mixin çağrılarına yerleştiremezsiniz. Bunun yerine, render()
karışımını aşağıdaki gibi create-widths()
çağrılarının etrafına sarın:
@include render('widths') { // GENERATE STANDARD WIDTHS //--------------------------------------------------------------------- // Example: .u-width-1/3 @include create-widths($utility-widths-sets); // GENERATE RESPONSIVE WIDTHS //--------------------------------------------------------------------- // Create responsive variants using settings.breakpoints // Changes width when breakpoint is hit // Example: .u-width-1/3@md @each $bp-name, $bp-value in $mq-breakpoints { @include mq(#{$bp-name}) { @include create-widths($utility-widths-sets, \@, #{$bp-name}); } } // End render }
Bu yerindeyken, derleme sırasında yalnızca $imports
içinde başvurulan kısmi öğeler derlenecektir.
$imports
içinde hangi bileşenlerin yorumlandığını karıştırıp eşleştirin ve denemek için terminalde npm run build
.
Bağımlılıklar Haritası
Şimdi programlı olarak kısmi öğeleri içe aktarıyoruz, bağımlılık mantığını uygulamaya başlayabiliriz.
src/scss/settings
içinde, _dependencies.scss
adında yeni bir kısmi oluşturun, bunu settings/_core.scss
@import
edin, ancak bunun _imports.scss
sonra olduğundan emin olun. Ardından, aşağıdaki SCSS haritasını oluşturun:
src/scss/settings/_dependencies.scss
$dependencies: ( expander: ( object: ( 'box', 'layout' ), component: ( 'button', 'typography' ), utility: ( 'alignments', 'widths' ) ) );
Burada, dist/index.html'de görüldüğü gibi, doğru şekilde oluşturulabilmesi için diğer kısmi bileşenlerden stiller gerektirdiğinden, genişletici bileşeni için bağımlılıkları beyan ederiz.
Bu listeyi kullanarak, $imports
değişkeninin durumu ne olursa olsun, bu bağımlılıkların her zaman bağımlı bileşenleriyle birlikte derleneceği anlamına gelen bir mantık yazabiliriz.
$dependencies
altında, dependency-setup()
adlı bir karışım oluşturun. Burada, aşağıdaki işlemleri yapacağız:
1. Bağımlılıklar haritasında dolaşın.
@mixin dependency-setup() { @each $componentKey, $componentValue in $dependencies { } }
2. Bileşen $imports
içinde bulunabilirse, bağımlılıklar listesinde dolaşın.
@mixin dependency-setup() { $components: map-get($imports, component); @each $componentKey, $componentValue in $dependencies { @if(index($components, $componentKey)) { @each $layerKey, $layerValue in $componentValue { } } } }
3. Bağımlılık $imports
içinde değilse, onu ekleyin.
@mixin dependency-setup() { $components: map-get($imports, component); @each $componentKey, $componentValue in $dependencies { @if(index($components, $componentKey)) { @each $layerKey, $layerValue in $componentValue { @each $partKey, $partValue in $layerValue { @if not index(map-get($imports, $layerKey), $partKey) { $imports: map-merge($imports, ( $layerKey: append(map-get($imports, $layerKey), '#{$partKey}') )) !global; } } } } } }
!global
bayrağının dahil edilmesi, Sass'a, karışımın yerel kapsamı yerine global kapsamda $imports
değişkenini aramasını söyler.
4. O zaman sadece mixin'i çağırma meselesi.
@mixin dependency-setup() { ... } @include dependency-setup();
Dolayısıyla, şimdi sahip olduğumuz gelişmiş bir kısmi içe aktarma sistemidir, burada bir bileşen içe aktarılırsa, geliştiricinin çeşitli bağımlılık bölümlerinin her birini manuel olarak içe aktarmak zorunda kalmaz.
$imports
değişkenini, yalnızca genişletici bileşeni içe aktarılacak şekilde yapılandırın ve ardından npm run build
. Derlenmiş CSS'de genişletici sınıflarını tüm bağımlılıklarıyla birlikte görmelisiniz.
Ancak bu, kullanılmayan CSS'yi filtrelemek açısından tabloya gerçekten yeni bir şey getirmez, çünkü aynı miktarda Sass, programlı olsun ya da olmasın hala içe aktarılıyor. Bunu geliştirelim.
Geliştirilmiş Bağımlılık İçe Aktarma
Bir bileşen, bir bağımlılıktan yalnızca tek bir sınıf gerektirebilir, bu nedenle devam etmek ve bu bağımlılığın tüm sınıflarını içe aktarmak, kaçınmaya çalıştığımız aynı gereksiz şişkinliğe yol açar.
Bileşenlerin yalnızca ihtiyaç duydukları bağımlılık sınıflarıyla derlendiğinden emin olmak için, sınıf bazında daha ayrıntılı filtrelemeye izin vermek için sistemi iyileştirebiliriz.
Çoğu tasarım deseninde, dekore edilmiş olsun veya olmasın, desenin doğru görüntülenmesi için stil sayfasında bulunması gereken minimum sayıda sınıf vardır.
BEM gibi yerleşik bir adlandırma kuralı kullanan sınıf adları için, genellikle "Blok" ve "Öğe" adlı sınıflar minimum olarak gereklidir ve "Değiştiriciler" genellikle isteğe bağlıdır.
Not: Yardımcı sınıflar, dar odakları nedeniyle doğada izole olduklarından genellikle BEM yolunu izlemez.
Örneğin, nesne yönelimli CSS'nin muhtemelen en iyi bilinen örneği olan bu medya nesnesine bir göz atın:
<div class="o-media o-media--spacing-small"> <div class="o-media__image"> <img src="url" alt="Image"> </div> <div class="o-media__text"> Oh! </div> </div>
Bir bileşen bağımlılık olarak bu kümeye sahipse, desenin çalışması için gereken minimum CSS miktarı olduğundan, her zaman .o-media
, .o-media__image
ve .o-media__text
derlemek mantıklıdır. Bununla birlikte, .o-media--spacing-small
isteğe bağlı bir değiştirici olduğundan, kullanımı tüm medya nesnesi örneklerinde tutarlı olmayabileceğinden, yalnızca açıkça belirttiğimiz takdirde derlenmelidir.
Bu isteğe bağlı sınıfları içe aktarmamıza izin vermek için $dependencies
haritasının yapısını değiştirirken, değiştiricilerin gerekmemesi durumunda yalnızca blok ve öğeyi içe aktarmanın bir yolunu dahil edeceğiz.
Başlamak için dist/index.html içindeki genişletici HTML'yi kontrol edin ve kullanımda olan bağımlılık sınıflarını not edin. Bunları aşağıdaki gibi $dependencies
haritasına kaydedin:
src/scss/settings/_dependencies.scss
$dependencies: ( expander: ( object: ( box: ( 'o-box--spacing-small' ), layout: ( 'o-layout--fit' ) ), component: ( button: true, typography: ( 'c-type-echo', ) ), utility: ( alignments: ( 'u-flex-middle', 'u-align-center' ), widths: ( 'u-width-grow', 'u-width-shrink' ) ) ) );
Bir değer true olarak ayarlandığında, bunu “Yalnızca blok ve eleman düzeyindeki sınıfları derleyin, değiştirici yok!” şeklinde çevireceğiz.
Sonraki adım, bu sınıfları ve manuel olarak içe aktarmak istediğimiz diğer (bağımlılık olmayan) sınıfları depolamak için bir beyaz liste değişkeni oluşturmayı içerir. /src/scss/settings/imports.scss
, $imports
öğesinden sonra, $global-filter
adlı yeni bir Sass listesi oluşturun.
src/scss/settings/_imports.scss
$global-filter: ();
$global-filter
filter'ın arkasındaki temel önerme, ait oldukları kısmi $imports
yoluyla içe aktarıldığı sürece, burada depolanan tüm sınıfların derlemede derlenmesidir.
Bu sınıf adları, bir bileşen bağımlılığıysa program aracılığıyla eklenebilir veya aşağıdaki örnekte olduğu gibi değişken bildirildiğinde manuel olarak eklenebilir:
Genel filtre örneği
$global-filter: ( 'o-box--spacing-regular@md', 'u-align-center', 'u-width-6/12@lg' );
Ardından, @dependency-setup
karışımına biraz daha mantık eklememiz gerekiyor, böylece $dependencies
içinde referans verilen tüm sınıflar otomatik olarak $global-filter
beyaz listemize eklenir.
Bu bloğun altında:
src/scss/settings/_dependencies.scss
@if not index(map-get($imports, $layerKey), $partKey) { }
...aşağıdaki snippet'i ekleyin.
src/scss/settings/_dependencies.scss
@each $class in $partValue { $global-filter: append($global-filter, '#{$class}', 'comma') !global; }
Bu, herhangi bir bağımlılık sınıfı arasında dolaşır ve onları $global-filter
beyaz listesine ekler.
Bu noktada, terminalde $global-filter
içeriğini yazdırmak için dependency-setup()
karışımının altına bir @debug
ifadesi eklerseniz:
@debug $global-filter;
...yapıda şöyle bir şey görmelisiniz:
DEBUG: "o-box--spacing-small", "o-layout--fit", "c-box--rounded", "true", "true", "u-flex-middle", "u-align-center", "u-width-grow", "u-width-shrink"
Şimdi bir sınıf beyaz listemiz var, bunu tüm farklı nesne, bileşen ve yardımcı program bölümlerine uygulamamız gerekiyor.
src/scss/tools
içinde _filter.scss
adında yeni bir kısmi oluşturun ve araçlar katmanının _core.scss
dosyasına bir @import
ekleyin.
Bu yeni bölümde, filter()
adında bir karışım oluşturacağız. Bunu mantığı uygulamak için kullanacağız; bu, sınıfların yalnızca $global-filter
değişkenine dahil edildiğinde derleneceği anlamına gelir.
Basitten başlayarak, tek bir parametreyi kabul eden bir karışım oluşturun - filtrenin kontrol ettiği $class
. Ardından, $class
$global-filter
beyaz listesine dahil edilmişse, derlenmesine izin verin.
src/scss/tools/_filter.scss
@mixin filter($class) { @if(index($global-filter, $class)) { @content; } }
Kısmi olarak, karışımı aşağıdaki gibi isteğe bağlı bir sınıfın etrafına saracağız:
@include filter('o-myobject--modifier') { .o-myobject--modifier { color: yellow; } }
Bu, .o-myobject--modifier
sınıfının yalnızca, $global-filter
-filter'a dahil edilmesi durumunda derleneceği anlamına gelir; bu, doğrudan veya $dependencies
içinde ayarlananlar aracılığıyla dolaylı olarak ayarlanabilir.
Depoyu gözden geçirin ve filter()
karışımını nesne ve bileşen katmanları arasında tüm isteğe bağlı değiştirici sınıflara uygulayın. Tipografi bileşenini veya yardımcı programlar katmanını işlerken, her sınıf bir sonrakinden bağımsız olduğundan, hepsini isteğe bağlı yapmak mantıklı olur, böylece sınıfları ihtiyacımız olduğu gibi etkinleştirebiliriz.
İşte birkaç örnek:
src/scss/objects/_layout.scss
@include filter('o-layout__item--fit-height') { .o-layout__item--fit-height { align-self: stretch; } }
src/scss/utilities/_alignments.scss
// Changes alignment when breakpoint is hit // Example: .u-align-left@md @each $bp-name, $bp-value in $mq-breakpoints { @include mq(#{$bp-name}) { @include filter('u-align-left@#{$bp-name}') { .u-align-left\@#{$bp-name} { text-align: left !important; } } @include filter('u-align-center@#{$bp-name}') { .u-align-center\@#{$bp-name} { text-align: center !important; } } @include filter('u-align-right@#{$bp-name}') { .u-align-right\@#{$bp-name} { text-align: right !important; } } } }
Not: filter()
karışımına duyarlı son ek sınıf adlarını eklerken, '@' sembolünden '\' ile kaçmanız gerekmez.
Bu işlem sırasında, kısmilere filter()
karışımını uygularken, birkaç şeyi fark etmiş olabilirsiniz (veya görmemiş olabilirsiniz).
Gruplandırılmış Sınıflar
Kod tabanındaki bazı sınıflar birlikte gruplanır ve aynı stilleri paylaşır, örneğin:
src/scss/objects/_box.scss
.o-box--spacing-disable-left, .o-box--spacing-horizontal { padding-left: 0; }
Filtre yalnızca tek bir sınıfı kabul ettiğinden, bir stil bildirim bloğunun birden fazla sınıf için olma olasılığını hesaba katmaz.
Bunu hesaba katmak için, filter()
karışımını genişleteceğiz, böylece tek bir sınıfa ek olarak, birçok sınıf içeren bir Sass argüman listesini kabul edebilir. Şöyle:
src/scss/objects/_box.scss
@include filter('o-box--spacing-disable-left', 'o-box--spacing-horizontal') { .o-box--spacing-disable-left, .o-box--spacing-horizontal { padding-left: 0; } }
Bu nedenle, filter()
karışımına, bu sınıflardan herhangi biri $global-filter
içindeyse, sınıfları derlemenize izin verildiğini söylemeliyiz.
Bu, her bir öğenin $global-filter
değişkeninde olup olmadığını kontrol etmek için bir argüman listesi geçirilirse bir döngü ile yanıt vererek, mixin'in $class
argümanını kontrol etmek için ek bir mantık içerecektir.
src/scss/tools/_filter.scss
@mixin filter($class...) { @if(type-of($class) == 'arglist') { @each $item in $class { @if(index($global-filter, $item)) { @content; } } } @else if(index($global-filter, $class)) { @content; } }
O zaman, filter()
karışımını doğru bir şekilde uygulamak için aşağıdaki bölümlere geri dönmek yeterlidir:
-
objects/_box.scss
-
objects/_layout.scss
-
utilities/_alignments.scss
Bu noktada, $imports
geri dönün ve yalnızca genişletici bileşeni etkinleştirin. Derlenmiş stil sayfasında, genel ve öğe katmanlarındaki stillerin yanı sıra yalnızca aşağıdakileri görmelisiniz:
- Genişletici bileşene ait olan, ancak değiştiricisine ait olmayan blok ve eleman sınıfları.
- Genişleticinin bağımlılıklarına ait blok ve eleman sınıfları.
-
$dependencies
değişkeninde açıkça bildirilen, genişleticinin bağımlılıklarına ait herhangi bir değiştirici sınıf.
Teorik olarak, derlenmiş stil sayfasına genişletici bileşenler değiştirici gibi daha fazla sınıf eklemek istediğinize karar verdiyseniz, bu yalnızca onu bildirim noktasında $global-filter
değişkenine eklemek veya başka bir noktada eklemek meselesidir. kod tabanında (Değiştiricinin kendisinin bildirildiği noktadan önce olduğu sürece).
Her Şeyi Etkinleştirmek
Şimdi, nesneleri, bileşenleri ve yardımcı programları bu kısmi öğelerdeki bireysel sınıflara aktarmanıza izin veren oldukça eksiksiz bir sistemimiz var.
Geliştirme sırasında, herhangi bir nedenle, her şeyi tek seferde etkinleştirmek isteyebilirsiniz. Buna izin vermek için, $enable-all-classes
adında yeni bir değişken oluşturacağız ve ardından bazı ek mantıklar ekleyeceğiz, böylece bu true olarak ayarlanırsa, $imports
ve $global-filter
durumlarından bağımsız olarak her şey derlenir. $global-filter
değişkenleri.
İlk olarak, ana bildirim dosyamızda değişkeni bildirin:
src/scss/main.scss
$enable-all-classes: false; @import 'settings/core'; @import 'tools/core'; @import 'generic/core'; @import 'elements/core'; @import 'objects/core'; @import 'components/core'; @import 'utilities/core';
Ardından, $enable-all-classes
değişkeni true olarak ayarlandığında bazı geçersiz kılma mantığı eklemek için filter()
ve render()
karışımlarımızda birkaç küçük düzenleme yapmamız gerekiyor.
İlk olarak, filter()
mixin. Herhangi bir mevcut kontrolden önce, $enable-all-classes
öğesinin true olarak ayarlanıp ayarlanmadığını görmek için bir @if
ifadesi ekleyeceğiz ve öyleyse, soru sorulmadan @content
oluşturacağız.
src/scss/tools/_filter.scss
@mixin filter($class...) { @if($enable-all-classes) { @content; } @else if(type-of($class) == 'arglist') { @each $item in $class { @if(index($global-filter, $item)) { @content; } } } @else if(index($global-filter, $class)) { @content; } }
render()
karışımında, $enable-all-classes
değişkeninin doğru olup olmadığını görmek için bir kontrol yapmamız gerekiyor ve eğer öyleyse, diğer kontrolleri atlıyoruz.
src/scss/tools/_render.scss
$layer: null !default; @mixin render($name) { @if($enable-all-classes or index(map-get($imports, $layer), $name)) { @content; } }
Şimdi, $enable-all-classes
değişkenini true olarak ayarlar ve yeniden oluşturursanız, isteğe bağlı her sınıf derlenir ve bu süreçte size epey zaman kazandırır.
karşılaştırmalar
Bu tekniğin bize ne tür kazanımlar sağladığını görmek için bazı karşılaştırmalar yapalım ve dosya boyutu farklılıklarının ne olduğunu görelim.
Karşılaştırmanın adil olduğundan emin olmak için, kutu ve kap nesnelerini $imports
içine eklemeli ve ardından kutunun o-box--spacing-regular
değiştiricisini $global-filter
öğesine şöyle eklemeliyiz:
src/scss/settings/_imports.scss
$imports: ( object: ( 'box', 'container' // 'layout' ), component: ( // 'button', 'expander' // 'typography' ), utility: ( // 'alignments', // 'widths' ) ); $global-filter: ( 'o-box--spacing-regular' );
Bu, genişleticinin ana öğelerine ilişkin stillerin, filtreleme yapılmamış olsaydı olacakları gibi derlenmesini sağlar.
Orijinal ve Filtrelenmiş Stil Sayfaları
Derlenmiş tüm sınıflarla orijinal stil sayfasını, yalnızca genişletici bileşeni tarafından gerekli olan CSS'nin derlendiği filtrelenmiş stil sayfasıyla karşılaştıralım.
Standart | ||
---|---|---|
stil sayfası | Boyut (kb) | Boyut (gzip) |
orijinal | 54.6kb | 6.98 kb |
Filtrelenmiş | 15,34 kb (%72 daha küçük) | 4,91 kb (%29 daha küçük) |
- Orijinal: https://webdevluke.github.io/handlingunusedcss/dist/index2.html
- Filtrelendi: https://webdevluke.github.io/handlingunusedcss/dist/index.html
Orijinal ve filtrelenmiş stil sayfaları arasında pek bir fark olmadığından, gzip yüzdesi tasarrufunun bu çabaya değmeyeceği anlamına geldiğini düşünebilirsiniz.
Gzip sıkıştırmasının daha büyük ve daha fazla tekrar eden dosyalarda daha iyi çalıştığını vurgulamakta fayda var. Filtrelenmiş stil sayfası tek kavram kanıtı olduğundan ve yalnızca genişletici bileşen için CSS içerdiğinden, gerçek hayattaki bir projede olduğu gibi sıkıştırılacak çok şey yoktur.
Her stil sayfasını bir web sitesinin CSS paket boyutundan daha tipik olan boyutlara 10 kat büyütecek olursak, gzip dosya boyutlarındaki fark çok daha etkileyici olur.
10x Boyut | ||
---|---|---|
stil sayfası | Boyut (kb) | Boyut (gzip) |
Orijinal (10x) | 892.07kb | 75.70kb |
Filtrelenmiş (10x) | 209,45 kb (%77 daha küçük) | 19,47 kb (%74 daha küçük) |
Filtrelenmiş Stil Sayfası ve UNCSS
Burada, filtrelenmiş stil sayfası ile UNCSS aracı aracılığıyla çalıştırılmış bir stil sayfası arasında bir karşılaştırma bulunmaktadır.
Filtrelenmiş vs UNCSS | ||
---|---|---|
stil sayfası | Boyut (kb) | Boyut (gzip) |
Filtrelenmiş | 15.34kb | 4.91kb |
UNCSS | 12.89kb (%16 daha küçük) | 4,25 kb (%13 daha küçük) |
UNCSS aracı, jenerik ve element dizinlerinde CSS'yi filtrelediği için burada marjinal olarak kazanır.
Çok çeşitli HTML öğelerinin kullanıldığı gerçek bir web sitesinde, 2 yöntem arasındaki farkın ihmal edilebilir olması mümkündür.
Toplama
Böylece, yalnızca Sass kullanarak, derlemede hangi CSS sınıflarının derlendiği üzerinde nasıl daha fazla kontrol elde edebileceğinizi gördük. Bu, son stil sayfasında kullanılmayan CSS miktarını azaltır ve kritik işleme yolunu hızlandırır.
Makalenin başında, UNCSS gibi mevcut çözümlerin bazı dezavantajlarını listeledim. Bu Sass odaklı çözümü aynı şekilde eleştirmek adil olur, bu nedenle hangi yaklaşımın sizin için daha iyi olduğuna karar vermeden önce tüm gerçekler masada:
Artıları
- Ek bağımlılık gerekmez, bu nedenle başka birinin koduna güvenmeniz gerekmez.
- Kodunuzu denetlemek için başsız tarayıcılar çalıştırmanız gerekmediğinden, Node.js tabanlı alternatiflerden daha az derleme süresi gerekir. Bir yapı kuyruğu görme olasılığınız daha düşük olabileceğinden, bu özellikle sürekli entegrasyon için kullanışlıdır.
- Otomatik araçlarla karşılaştırıldığında benzer dosya boyutuyla sonuçlanır.
- Kutunun dışında, bu CSS sınıflarının kodunuzda nasıl kullanıldığına bakılmaksızın, hangi kodun filtreleneceği üzerinde tam kontrole sahipsiniz. Node.js tabanlı alternatiflerle, dinamik olarak enjekte edilen HTML'ye ait CSS sınıflarının filtrelenmemesi için genellikle ayrı bir beyaz liste tutmanız gerekir.
Eksileri
- Sass odaklı çözüm,
$imports
ve$global-filter
değişkenlerinin üstünde tutmanız gerektiği anlamında kesinlikle daha pratik. İlk kurulumun ötesinde, incelediğimiz Node.js alternatifleri büyük ölçüde otomatikleştirilmiştir. -
$global-filter
filter'a CSS sınıfları ekler ve daha sonra bunları HTML'nizden kaldırırsanız, değişkeni güncellemeyi hatırlamanız gerekir, aksi takdirde ihtiyacınız olmayan CSS'yi derlersiniz. Büyük projeler üzerinde aynı anda birden fazla geliştirici tarafından çalışıldığından, düzgün bir şekilde planlamadığınız sürece bunu yönetmek kolay olmayabilir. - Bağımlılıkları bir araya getirmek ve
render()
karışımını çok sayıda sınıfa uygulamak için oldukça fazla zaman harcamanız gerekeceğinden, bu sistemi mevcut herhangi bir CSS kod tabanına bağlamanızı tavsiye etmem. Uğraşacak mevcut kodunuz olmadığında, yeni yapılarla uygulanması çok daha kolay bir sistem.
Umarım bunu bir araya getirmeyi ilginç bulduğum kadar okumak için ilginç bulmuşsunuzdur. Bu yaklaşımı geliştirmek için herhangi bir öneriniz, fikriniz varsa veya tamamen gözden kaçırdığım bazı ölümcül kusurlara işaret etmek istiyorsanız, aşağıdaki yorumları gönderdiğinizden emin olun.