Performansı Artırmak için Sass'ta Kullanılmayan CSS'yi Kullanma

Yayınlanan: 2022-03-10
Kısa özet ↬ Kullanılmayan CSS'nin performans üzerindeki etkisini biliyor musunuz? Spoyler: Çok fazla! Bu makalede, kullanılmayan CSS ile başa çıkmak, başsız tarayıcıları içeren karmaşık Node.js bağımlılıklarına ve DOM öykünmesine duyulan ihtiyacı ortadan kaldırmak için Sass odaklı bir çözüm keşfedeceğiz.

Modern ö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.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Ş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, Sheffield United'ın web sitesi, yerel Futbol takımım (bu, kolonilerde sizin için Futboldur). (Büyük önizleme)

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.

Geliştirici Araçları'ndaki kapsama aracına erişmenin en hızlı yolu, komut menüsünü açmak için Control+Shift+P veya Command+Shift+P (Mac) klavye kısayolunu kullanmaktır. İçerisine coverage yazın ve 'Kapsamını Göster' seçeneğini seçin. (Büyük önizleme)

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.

Chrome'daki herhangi bir varlık için bunun gibi zamanlamaları, Geliştirici Araçları'nda Ağ -> Varlığınıza tıklayın -> Zamanlama sekmesi aracılığıyla görebilirsiniz. (Büyük önizleme)

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.

Chrome'da (ve Chromium Edge), geliştirici araçlarında Denetim sekmesine tıklayarak Google Lighthouse denetimlerini sağlayabilirsiniz. (Büyük önizleme)

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:

  1. Bulld'da, web sitesine başsız bir tarayıcı (Örn: kuklacı) veya DOM öykünmesi (Örn: jsdom) aracılığıyla erişilir.
  2. Sayfanın HTML öğelerine göre kullanılmayan CSS tanımlanır.
  3. 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.

Kullanılmayan CSS'yi işlemek için Sass odaklı çözümü geliştirirken bu genişletici UI bileşenini test konumuz olarak kullanacağız. (Büyük önizleme)

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.