Web'de oluşturma

Web geliştiricilerinin vermesi gereken temel kararlardan biri, uygulamalarında mantığı ve oluşturmayı nereye uygulayacaklarıdır. Web sitesi oluşturmanın birçok yolu olduğundan bu işlem zor olabilir.

Bu alanla ilgili bilgilerimiz, Chrome'da son birkaç yıldır büyük sitelerle yaptığımız görüşmelerden elde edilmiştir. Genel olarak, geliştiricilerin tam yeniden hidrasyon yaklaşımı yerine sunucu tarafı oluşturma veya statik oluşturma yöntemlerini değerlendirmelerini öneririz.

Bu kararı verirken aramızdan seçim yaptığımız mimarileri daha iyi anlamak için her yaklaşımı sağlam bir şekilde anlamamız ve bu yaklaşımlardan bahsederken tutarlı bir terminoloji kullanmamız gerekir. Oluşturma yaklaşımları arasındaki farklar, web'de oluşturmanın sayfa performansı açısından avantajlarını ve dezavantajlarını göstermeye yardımcı olur.

Öncelikle kullanacağımız bazı terimleri tanımlayalım.

Oluşturma

Sunucu tarafı oluşturma (SSR)
İstemciye JavaScript yerine HTML göndermek için sunucuda bir uygulama oluşturma.
İstemci tarafı oluşturma (CSR)
DOM'u değiştirmek için JavaScript'i kullanarak bir uygulamayı tarayıcıda oluşturma.
Yeniden sulandırma
JavaScript görünümlerini istemcide"başlatmak", böylece sunucu tarafından oluşturulan HTML'nin DOM ağacını ve verilerini yeniden kullanabilir.
Önceden oluşturma
İlk durumunu statik HTML olarak yakalamak için derleme sırasında istemci tarafı bir uygulamayı çalıştırma.

Performans

İlk Bayta Erişim Süresi (TTFB)
Bir bağlantının tıklanmasından yeni sayfada içeriğin ilk baytının yüklenmesi arasında geçen süre.
İlk Zengin İçerikli Boyama (FCP)
İstenen içeriğin (makalenin gövdesi vb.) görünür hale geldiği zaman.
Interaction to Next Paint (INP)
Bir sayfanın kullanıcı girişlerine tutarlı bir şekilde hızlı yanıt verip vermediğini değerlendiren temsili bir metriktir.
Toplam Engelleme Süresi (TBT)
Sayfa yükleme sırasında ana ileti dizisinin ne kadar süreyle engellendiğini hesaplayan INP için proxy metriği.

Sunucu tarafı oluşturma

Sunucu tarafı oluşturma, gezinmeye yanıt olarak sunucudaki bir sayfanın tam HTML'sini oluşturur. Tarayıcı yanıt almadan önce oluşturma aracı bunları işlediği için bu, istemcide veri getirme ve şablon oluşturma için ek gidiş dönüşlerin yapılmasını önler.

Sunucu tarafı oluşturma genellikle hızlı bir FCP oluşturur. Sayfa mantığını ve oluşturma işlemini sunucuda çalıştırmak, istemciye çok fazla JavaScript göndermekten kaçınmanızı sağlar. Bu, sayfanın TBT'sini azaltmaya yardımcı olur. Ayrıca, sayfa yükleme sırasında ana iş parçacığı sık sık engellenmediğinden INP'nin de düşmesine neden olabilir. Ana iş parçacığı daha seyrek engellendiğinde, kullanıcı etkileşimlerinin daha erken çalışma olasılığı artar. Bu durum, sunucu tarafı oluşturma işleminde aslında kullanıcının tarayıcısından metin ve bağlantılar gönderdiğiniz için mantıklıdır. Bu yaklaşım, çeşitli cihaz ve ağ koşullarında iyi sonuç verebilir ve akış şeklinde belge ayrıştırma gibi ilginç tarayıcı optimizasyonları sunar.

FCP ve TTI'yi etkileyen sunucu tarafı oluşturma ve JS yürütme işlemlerini gösteren şema.
Sunucu tarafı oluşturma ile FCP ve TTI.

Sunucu tarafı oluşturma sayesinde, kullanıcıların sitenizi kullanabilmek için CPU'ya bağlı JavaScript'in çalışmasını bekleme olasılığı daha düşüktür. Üçüncü taraf JS'den kaçınamıyor olsanız bile kendi birinci taraf JavaScript maliyetlerinizi azaltmak için sunucu tarafı oluşturma özelliğini kullanmak, diğer maliyetler için size daha fazla bütçe sağlayabilir. Ancak bu yaklaşımın bir dezavantajı vardır: Sunucudaki sayfaların oluşturulması zaman alır ve bu da sayfanızın TTFB'sini artırabilir.

Sunucu tarafı oluşturmanın uygulamanız için yeterli olup olmadığı büyük ölçüde ne tür bir deneyim oluşturduğunuza bağlıdır. Sunucu tarafı oluşturma ve istemci tarafı oluşturmanın doğru uygulamaları konusunda uzun süredir devam eden bir tartışma vardır. Ancak bazı sayfalar için sunucu tarafı oluşturmayı, bazı sayfalar için ise kullanmamayı seçebilirsiniz. Bazı siteler karma oluşturma tekniklerini başarıyla benimsemiştir. Örneğin, Netflix, nispeten statik açılış sayfalarını sunucu tarafından oluştururken etkileşime dayalı sayfalar için JS'yi prefetching. Böylece, istemci tarafından oluşturulan bu ağır sayfaların daha hızlı yüklenme şansı artar.

Birçok modern çerçeve, kitaplık ve mimari, aynı uygulamayı hem istemcide hem de sunucuda oluşturmanıza olanak tanır. Bu teknikleri sunucu tarafı oluşturma için kullanabilirsiniz. Ancak hem sunucuda hem istemcide oluşturma işleminin gerçekleştiği mimariler, çok farklı performans özelliklerine ve değiş tokuşlara sahip kendi çözüm sınıflarıdır. React kullanıcıları, sunucu tarafı oluşturma için sunucu DOM API'lerini veya bunlara dayalı çözümleri (ör. Next.js) kullanabilir. Vue kullanıcıları, Vue'un sunucu tarafı oluşturma kılavuzunu veya Nuxt'u kullanabilir. Angular'da Evrensel vardır. Bununla birlikte, en popüler çözümlerin çoğunda bir tür sulandırma yöntemi kullanılır. Bu nedenle, aracınızın kullandığı yaklaşımları öğrenin.

Statik oluşturma

Statik oluşturma, derleme zamanında gerçekleşir. Bu yaklaşım, sayfalarınızdaki istemci tarafı JS miktarını sınırladığınız sürece hızlı bir FCP ve daha düşük bir TBT ve INP sunar. Sunucu tarafında oluşturmanın aksine, sayfanın HTML'sinin sunucuda dinamik olarak oluşturulması gerekmediğinden tutarlı bir şekilde hızlı bir TTFB elde edilir. Genel olarak statik oluşturma, her URL için önceden ayrı bir HTML dosyası oluşturmak anlamına gelir. Önceden oluşturulmuş HTML yanıtlarıyla, uçta önbelleğe alma özelliğinden yararlanmak için statik oluşturma işlemlerini birden fazla CDN'ye dağıtabilirsiniz.

FCP ve TTI'yi etkileyen statik oluşturma ve isteğe bağlı JS yürütme işlemlerini gösteren şema.
Statik oluşturma ile FCP ve TTI.

Statik oluşturma çözümleri her şekil ve boyutta olabilir. Gatsby gibi araçlar, geliştiricilerin uygulamalarının bir derleme adımı olarak oluşturulmadığını, dinamik olarak oluşturulduğunu hissetmesini sağlamak için tasarlanmıştır. 11ty, Jekyll ve Metalsmith gibi statik site oluşturma araçları, statik yapılarını benimseyerek daha şablon odaklı bir yaklaşım sunar.

Statik oluşturmanın dezavantajlarından biri, mümkün olan her URL için ayrı HTML dosyaları oluşturması gerektiğidir. Bu URL'lerin ne olacağını önceden tahmin edemediğinizde veya çok sayıda benzersiz sayfası olan sitelerde bu işlem zor hatta imkansız olabilir.

React kullanıcıları Gatsby, Next.js statik dışa aktarma veya Navi'yi biliyor olabilir. Bu araçların tümü, bileşenlerden sayfa oluşturmayı kolaylaştırır. Ancak statik oluşturma ve ön oluşturma farklı şekilde çalışır: Statik olarak oluşturulan sayfalar, çok fazla istemci tarafı JavaScript çalıştırılmasına gerek kalmadan etkileşimlidir. Öte yandan ön oluşturma, sayfaları gerçekten etkileşimli hale getirmek için istemcide başlatılması gereken tek sayfalık bir uygulamanın FCP'sini iyileştirir.

Belirli bir çözümün statik oluşturma mı yoksa ön oluşturma mı olduğundan emin değilseniz JavaScript'i devre dışı bırakıp test etmek istediğiniz sayfayı yüklemeyi deneyin. Statik olarak oluşturulmuş sayfalarda, etkileşimli özelliklerin çoğu JavaScript olmadan da mevcuttur. Önceden oluşturulmuş sayfalarda JavaScript'in devre dışı bırakıldığı bağlantılar gibi bazı temel özellikler bulunabilir ancak sayfanın çoğu etkin değildir.

Yararlı bir başka test de Chrome Geliştirici Araçları'nda ağ tıkanıklığını kullanarak bir sayfa etkileşimli hale gelmeden önce ne kadar JavaScript indirildiğini görmektir. Önceden oluşturma işleminin etkileşimli hale gelmesi genellikle daha fazla JavaScript gerektirir ve bu JavaScript, statik oluşturmada kullanılan kademeli geliştirme yaklaşımından daha karmaşık olma eğilimindedir.

Sunucu tarafı oluşturma ve statik oluşturma karşılaştırması

Dinamik yapısı nedeniyle önemli düzeyde bilgi işlem ek maliyetleri olabileceğinden sunucu tarafı oluşturma her şey için en iyi çözüm değildir. Sunucu tarafı oluşturma çözümlerinin çoğu, erken temizleme yapmaz, TTFB'yi geciktirmezler veya gönderilen verileri ikiye katlamaz (ör. istemcide JavaScript tarafından kullanılan satır içi durumlar). React'te renderToString(), senkronize ve tek iş parçacıklı olduğu için yavaş olabilir. Yeni React sunucu DOM API'leri, bir HTML yanıtının ilk bölümünü tarayıcıya daha erken gönderebilen akış özelliğini destekler. Bu sırada yanıtın geri kalanı sunucuda oluşturulmaya devam eder.

Sunucu tarafı oluşturmayı "doğru" şekilde kullanmak, bileşen önbelleğe alma için bir çözüm bulmak veya oluşturmak, hafıza tüketimini yönetmek, hafıza önbelleğe alma tekniklerini kullanmak ve diğer endişeleri ele almayı içerebilir. Genellikle aynı uygulamayı istemcide bir kez, sunucuda bir kez olmak üzere iki kez işliyor veya yeniden inşa ediyorsunuz. İçeriği daha erken gösteren sunucu tarafı oluşturma, daha az iş yapmanızı sağlamaz. Sunucu tarafından oluşturulan bir HTML yanıtı istemciye ulaştıktan sonra istemcide çok fazla işiniz varsa bu durum web siteniz için daha yüksek TBT ve INP'ye neden olabilir.

Sunucu tarafı oluşturma, her URL için isteğe bağlı olarak HTML oluşturur ancak statik olarak oluşturulmuş içeriği sunmaktan daha yavaş olabilir. Ek çalışmalar yapabilecekseniz sunucu tarafı oluşturma ve HTML önbelleğe alma, sunucu oluşturma süresini önemli ölçüde azaltabilir. Sunucu tarafı oluşturmanın avantajı, statik oluşturmayla mümkün olandan daha fazla "canlı" veri çekme ve daha kapsamlı bir istek grubuna yanıt verme olanağıdır. Kişiselleştirme gerektiren sayfalar, statik oluşturma ile iyi çalışmayan istek türüne dair somut bir örnektir.

Sunucu tarafı oluşturma, PWA geliştirirken ilginç kararlar da sunabilir: Tam sayfa hizmet çalışanı önbelleğe alma özelliğini kullanmak mı yoksa yalnızca içerik parçalarını sunucu tarafında oluşturmak mı daha iyidir?

İstemci tarafı oluşturma

İstemci tarafı oluşturma, sayfaları doğrudan tarayıcıda JavaScript ile oluşturma anlamına gelir. Tüm mantık, veri getirme, şablon oluşturma ve yönlendirme işlemleri sunucu yerine istemcide gerçekleştirilir. Bunun sonucunda, sunucudan kullanıcının cihazına daha fazla veri aktarılır. Bu da kendi avantaj ve dezavantajlarıyla birlikte gelir.

İstemci tarafı oluşturma işlemini mobil cihazlar için hızlı bir şekilde yapmak ve sürdürmek zor olabilir. Sıkışık bir JavaScript bütçesi tutmak ve mümkün olduğunca az yinelenen istek gerçekleştirerek değer sunmak için biraz çalışmayla, istemci tarafı oluşturmanın performansını neredeyse tamamen sunucu tarafı oluşturma ile aynı hale getirebilirsiniz. <link rel=preload> kullanarak kritik komut dosyalarını ve verileri sağlayarak ayrıştırıcının sizin için daha hızlı çalışmasını sağlayabilirsiniz. Ayrıca, ilk ve sonraki gezinmelerin anında gerçekleşmesini sağlamak için PRPL gibi kalıpları kullanmayı da düşünmenizi öneririz.

FCP ve TTI&#39;yi etkileyen istemci tarafı oluşturmayı gösteren şema.
İstemci tarafı oluşturma ile FCP ve TTI.

İstemci tarafı oluşturmanın başlıca dezavantajı, uygulama büyüdükçe gereken JavaScript miktarının da artma eğiliminde olmasıdır. Bu durum, sayfanın INP'sini etkileyebilir. Bu durum, özellikle işlem gücü için rekabet eden ve genellikle bir sayfanın içeriğinin oluşturulabilmesi için önce işlenmesi gereken yeni JavaScript kitaplıklarının, polyfill'lerin ve üçüncü taraf kodlarının eklenmesiyle daha da zorlaşır.

İstemci tarafı oluşturma kullanan ve büyük JavaScript paketlerine dayanan deneyimler, sayfa yükleme sırasında TBT ve INP'yi azaltmak için agresif kod bölme ve yalnızca kullanıcının ihtiyaç duyduğunda sunmak için JavaScript'i yavaş yükleme yöntemlerini kullanmalıdır. Etkileşimin az olduğu veya hiç olmadığı deneyimler için sunucu tarafı oluşturma, bu sorunlara daha ölçeklenebilir bir çözüm sunabilir.

Tek sayfalık uygulamalar oluşturan kullanıcılar, kullanıcı arayüzünün çoğu sayfa tarafından paylaşılan temel bölümlerini tanımlayarak uygulama kabuğu önbelleğe alma tekniğini uygulayabilir. Bu, hizmet işçileriyle birlikte kullanıldığında sayfanın uygulama kabuğu HTML'sini ve bağımlılıkları CacheStorage'den çok hızlı bir şekilde yükleyebilmesi nedeniyle tekrar ziyaretlerde algılanan performansı önemli ölçüde artırabilir.

Yeniden sulandırma, sunucu tarafı ve istemci tarafı oluşturmayı birleştirir

Yeniden sulandırma, her ikisini de yaparak istemci tarafı ve sunucu tarafı oluşturma arasındaki dengeleri yumuşatmaya çalışan bir yaklaşımdır. Sayfanın tamamının yüklenmesi veya yeniden yüklenmesi gibi gezinme istekleri, uygulamayı HTML'ye dönüştüren bir sunucu tarafından işlenir. Ardından, oluşturma için kullanılan JavaScript ve veriler oluşturulan belgeye yerleştirilir. Bu işlem dikkatli bir şekilde yapıldığında, sunucu tarafı oluşturma gibi hızlı bir FCP elde edilir ve ardından istemcide tekrar oluşturma işlemi yapılarak "devam edilir". Bu etkili bir çözümdür ancak önemli performans dezavantajları olabilir.

Yeniden sulandırmalı sunucu tarafı oluşturmanın başlıca dezavantajı, FCP'yi iyileştirse bile TBT ve INP üzerinde önemli ölçüde olumsuz bir etkisi olabilmesidir. Sunucu tarafında oluşturulan sayfalar yüklenmiş ve etkileşimli görünebilir ancak bileşenlerin istemci tarafı komut dosyaları çalıştırılıp etkinlik işleyiciler eklenene kadar girişlere yanıt veremez. Bu işlem mobil cihazlarda birkaç dakika sürebilir ve kullanıcının kafasını karıştırıp canını sıkabilir.

Yeniden sulandırma sorunu: İki fiyatına bir uygulama

Sunucunun HTML'sini oluşturmak için kullandığı tüm verileri yeniden istemeden istemci tarafı JavaScript'in sunucunun kaldığı yerden doğru şekilde "devam edebilmesi" için çoğu sunucu tarafı oluşturma çözümü, kullanıcı arayüzünün veri bağımlılıkları tarafından sağlanan yanıtı dokümanda komut dosyası etiketleri olarak serileştirir. Bu işlem çok fazla HTML'yi kopyaladığı için yeniden sulandırma, gecikmeli etkileşimden daha fazla soruna neden olabilir.

Serileştirilmiş kullanıcı arayüzü, satır içi veriler ve bir bundle.js komut dosyası içeren HTML dokümanı
HTML dokümanında yinelenen kod.

Sunucu, gezinme isteğine yanıt olarak uygulamanın kullanıcı arayüzünün açıklamasını döndürür ancak bu kullanıcı arayüzünü oluşturmak için kullanılan kaynak verileri ve kullanıcı arayüzünün istemcide başlatılan tam kopyasını da döndürür. Kullanıcı arayüzü, bundle.js yüklenip çalıştırılana kadar etkileşimli olmaz.

Sunucu tarafı oluşturma ve yeniden sulandırma kullanılarak gerçek web sitelerinden toplanan performans metrikleri, bu seçeneğin nadiren en iyi seçenek olduğunu gösteriyor. Bunun en önemli nedeni, bir sayfa hazır görünmesine rağmen etkileşimli özelliklerinin hiçbirinin çalışmaması nedeniyle kullanıcı deneyimi üzerindeki etkisidir.

Müşteri oluşturmanın TTI&#39;yi olumsuz yönde etkilediğini gösteren diyagram.
İstemci tarafı oluşturmanın TTI üzerindeki etkileri.

Yine de yeniden hidrasyonla sunucu tarafı oluşturma için umut var. Kısa vadede, yalnızca yüksek oranda önbelleğe alınabilen içerikler için sunucu tarafı oluşturma işlemini kullanmak TBT'yi azaltabilir ve ön oluşturmaya benzer sonuçlar elde edilebilir. Artımlı, kademeli veya kısmi olarak yeniden sulandırma, bu tekniğin gelecekte daha uygulanabilir hale gelmesinin anahtarı olabilir.

Sunucu tarafı oluşturma işlemini akış şeklinde gerçekleştirme ve aşamalı olarak yeniden besleme

Sunucu tarafı oluşturma, son birkaç yılda birçok gelişme kaydetti.

Sunucu tarafı oluşturma akışıyla, HTML'yi tarayıcı tarafından alındıkça kademeli olarak oluşturulabilecek parçalar halinde gönderebilirsiniz. Bu sayede işaretleme, kullanıcılarınıza daha hızlı ulaşabilir ve FCP'nizi hızlandırabilir. React'te, renderToPipeableStream()'teki akışların eşzamanlı renderToString()'a kıyasla eşzamansız olması, geri basıncın iyi yönetildiği anlamına gelir.

Kademeli rehidrasyon da dikkate alınması gereken bir konudur ve React bunu uygulamıştır. Bu yaklaşımda, uygulamanın tamamını tek seferde başlatma gibi yaygın bir yaklaşım yerine, sunucu tarafından oluşturulan uygulamanın ayrı parçaları zaman içinde "başlatılır". Bu, sayfaları etkileşimli hale getirmek için gereken JavaScript miktarını azaltmaya yardımcı olabilir. Bunun nedeni, sayfanın düşük öncelikli bölümlerinin istemci tarafında yükseltilmesini erteleyerek ana ileti dizisini engellemesini önlemenize ve kullanıcı etkileşimlerinin kullanıcı tarafından başlatıldıktan sonra daha erken gerçekleşmesine olanak tanımasıdır.

Kademeli yeniden sulandırma, sunucu tarafı oluşturma yeniden sulandırmayla ilgili en yaygın sorunlardan birinden de kaçınmanıza yardımcı olabilir: Sunucu tarafından oluşturulan bir DOM ağacı, genellikle ilk senkronize istemci tarafı oluşturma işlemi için henüz hazır olmayan veriler (genellikle henüz çözülmemiş bir Promise) gerektiğinden yok edilir ve hemen yeniden oluşturulur.

Kısmi rehidrasyon

Kısmi rehidrasyonun uygulanmasının zor olduğu kanıtlanmıştır. Bu yaklaşım, sayfanın tek tek parçalarını (bileşenler, görünümler veya ağaçlar) analiz eden ve etkileşimi az veya hiç olmayan parçaları tanımlayan aşamalı rehidrasyon özelliğinin bir uzantısıdır. Çoğunlukla statik olan bu bölümlerin her biri için, ilgili JavaScript kodu daha sonra hareketsiz referanslara ve dekoratif özelliklere dönüştürülerek istemci tarafındaki ayak izini neredeyse sıfıra indirir.

Kısmi hidrasyon yaklaşımının kendine özgü sorunları ve ödünleri vardır. Bu, önbelleğe alma konusunda bazı ilginç zorluklar oluşturur ve istemci tarafı gezinme, uygulamanın hareketsiz bölümleri için sunucu tarafından oluşturulan HTML'nin tam sayfa yüklenmeden kullanılabileceğini varsayamayacağımız anlamına gelir.

Üç biçimli oluşturma

Hizmet çalışanları sizin için bir seçenekse üç biçimli oluşturmayı değerlendirin. İlk gezinmeler veya JavaScript içermeyen gezinmeler için akışlı sunucu tarafı oluşturmayı kullanmanıza ve ardından hizmet çalışanınızın yüklendikten sonra gezinmeler için HTML oluşturmasını sağlamanıza olanak tanıyan bir tekniktir. Bu sayede önbelleğe alınmış bileşenler ve şablonlar güncel tutulabilir ve aynı oturumda yeni görünümler oluşturmak için SPA tarzı gezinmeler etkinleştirilebilir. Bu yaklaşım, sunucu, istemci sayfası ve hizmet çalışanı arasında aynı şablon ve yönlendirme kodunu paylaşabildiğinizde en iyi sonucu verir.

Tarayıcı ve servis çalışanının sunucuyla iletişim kurduğunu gösteren üç biçimli oluşturma.
Trismorfik oluşturmanın işleyiş şeklini gösteren bir şema.

SEO ile ilgili dikkat edilmesi gereken noktalar

Ekipler web oluşturma stratejisi seçerken genellikle SEO'nun etkisini göz önünde bulundurur. Sunucu tarafı oluşturma, tarayıcıların yorumlayabileceği "tam görünümlü" bir deneyim sunmak için popüler bir seçimdir. Tarayıcılar JavaScript'i anlayabilir ancak genellikle oluşturma biçimlerinde sınırlamalar vardır. İstemci tarafı oluşturma işe yarayabilir ancak genellikle ek test ve ek maliyet gerektirir. Son zamanlarda, mimariniz istemci tarafı JavaScript'e büyük ölçüde bağlıysa dinamik oluşturma da dikkate alınması gereken bir seçenek haline geldi.

Şüphe duyduğunuzda mobil uyumluluk testi aracını kullanarak seçtiğiniz yaklaşımın beklediğiniz sonucu verip vermediğini test edebilirsiniz. Bu araç, herhangi bir sayfanın Google'ın tarayıcısına nasıl göründüğünü, JavaScript çalıştırıldıktan sonra bulduğu serileştirilmiş HTML içeriğini ve oluşturma sırasında karşılaşılan hataları görsel olarak önizler.

Mobil Uyumluluk Testi kullanıcı arayüzü.
Mobil Uyumluluk Testi kullanıcı arayüzü.

Sonuç

Oluşturma yaklaşımına karar verirken darboğazlarınızı ölçün ve anlayın. Statik oluşturma veya sunucu tarafı oluşturmanın bu hedefe ulaşmanıza yardımcı olup olmayacağını düşünün. Etkileşimli bir deneyim elde etmek için çoğunlukla HTML'yi minimum JavaScript ile gönderebilirsiniz. Sunucu-istemci spektrumunu gösteren kullanışlı bir infografik:

Bu makalede açıklanan seçenek yelpazesini gösteren infografik.
Oluşturma seçenekleri ve bu seçeneklerin avantajları ve dezavantajları

Kredi

Yorumları ve ilhamları için herkese teşekkür ederiz:

Jeffrey Posnick, Houssein Djirdeh, Shubhie Panicker, Chris Harrelson ve Sebastian Markbåge