Gulp: Bir Web Geliştiricisinin Site Hızını En Üst Düzeye Çıkaran Gizli Silahı
Yayınlanan: 2022-03-11Birçoğumuz üretimde kullanılan, halka çeşitli hizmetler sunan web tabanlı projelerle uğraşmak zorundayız. Bu tür projelerle uğraşırken kodumuzu hızlı bir şekilde oluşturup dağıtmak önemlidir. Bir şeyi hızlı yapmak, özellikle bir süreç tekrar ediyorsa, genellikle hatalara yol açar, bu nedenle böyle bir süreci mümkün olduğunca otomatikleştirmek iyi bir uygulamadır.
Bu yazıda, böyle bir otomasyona ulaşmamızı sağlayacak şeyin bir parçası olabilecek bir araca bakacağız. Bu araç, Gulp.js adlı bir npm paketidir. Bu gönderide kullanılan temel Gulp.js terminolojisine aşina olmak için lütfen Toptal geliştirici arkadaşlarımızdan biri olan Antonios Minas tarafından daha önce blogda yayınlanan "Gulp ile JavaScript Otomasyonuna Giriş" bölümüne bakın. Bu gönderi boyunca paketleri kurmak için yaygın olarak kullanıldığından, npm ortamına temel düzeyde aşinalık sağlayacağız.
Ön Uç Varlıklarına Hizmet Verme
Devam etmeden önce, Gulp.js'nin bizim için çözebileceği soruna genel bir bakış için birkaç adım geriye gidelim. Birçok web tabanlı proje, web sayfasına çeşitli işlevler sağlamak için istemciye sunulan ön uç JavaScript dosyalarına sahiptir. Genellikle müşteriye sunulan bir dizi CSS stil sayfası da vardır. Bazen bir web sitesinin veya web uygulamasının kaynak koduna baktığımızda şöyle bir kod görebiliriz:
<link href="css/main.css" rel="stylesheet"> <link href="css/custom.css" rel="stylesheet"> <script src="js/jquery.min.js"></script> <script src="js/site.js"></script> <script src="js/module1.js"></script> <script src="js/module2.js"></script>Bu kodla ilgili birkaç sorun var. İki ayrı CSS stil sayfasına ve dört ayrı JavaScript dosyasına referansları vardır. Bu, sunucunun sunucuya toplam altı istek yapması gerektiği ve sayfa hazır olmadan önce her isteğin ayrı olarak bir kaynak yüklemesi gerektiği anlamına gelir. Bu, HTTP/2 ile daha az sorun yaratır, çünkü HTTP/2 paralellik ve başlık sıkıştırması sunar, ancak yine de bir sorundur. Bu sayfayı yüklemek için gereken toplam trafik hacmini artırır ve dosyaların yüklenmesi daha uzun sürdüğü için kullanıcı deneyiminin kalitesini düşürür. HTTP 1.1 durumunda, ağa da takılır ve mevcut olan istek kanallarının sayısını azaltır. CSS ve JavaScript dosyalarını her biri için tek bir pakette birleştirmek çok daha iyi olurdu. Bu şekilde, yalnızca toplam iki istek olacaktır. Bu dosyaların genellikle orijinallerinden çok daha küçük olan küçültülmüş versiyonlarını sunmak da güzel olurdu. Varlıklardan herhangi biri önbelleğe alınırsa web uygulamamız da bozulabilir ve müşteri eski bir sürüm alır.
Bu sorunlardan bazılarını çözmeye yönelik ilkel bir yaklaşım, her varlık türünü bir metin düzenleyici kullanarak manuel olarak bir pakette birleştirmek ve ardından sonucu http://jscompress.com/ gibi bir küçültme hizmeti aracılığıyla çalıştırmaktır. Bu, geliştirme sürecinde sürekli olarak yapmak çok sıkıcı olduğunu kanıtlıyor. Küçük ama şüpheli bir gelişme, GitHub'da bulunan paketlerden birini kullanarak kendi küçültücü sunucumuzu barındırmak olacaktır. Ardından, aşağıdakine biraz benzeyen şeyler yapabiliriz:
<script src="min/f=js/site.js,js/module1.js"></script>Bu, müşterimize küçültülmüş dosyalar sunar, ancak önbelleğe alma sorununu çözmez. Ayrıca, sunucumuz her istekte esasen tüm kaynak dosyaları tekrar tekrar birleştirmek ve küçültmek zorunda kalacağından, sunucuda ek yüke neden olur.
Gulp.js ile otomatikleştirme
Elbette bu iki yaklaşımdan herhangi birinden daha iyisini yapabiliriz. Gerçekten istediğimiz, paketlemeyi otomatikleştirmek ve onu projemizin inşa aşamasına dahil etmektir. Halihazırda küçültülmüş ve hizmete hazır, önceden oluşturulmuş varlık paketleri elde etmek istiyoruz. Ayrıca, müşteriyi her istekte paketlenmiş varlıklarımızın en güncel sürümlerini almaya zorlamak istiyoruz, ancak yine de mümkünse önbelleğe alma özelliğini kullanmak istiyoruz. Neyse ki bizim için Gulp.js bununla başa çıkabilir. Makalenin geri kalanında, dosyaları birleştirmek ve küçültmek için Gulp.js'nin gücünden yararlanacak bir çözüm oluşturacağız. Güncellemeler olduğunda önbelleği bozmak için bir eklenti de kullanacağız.
Örneğimizde aşağıdaki dizini ve dosya yapısını oluşturacağız:
public/ |- build/ |- js/ |- bundle-{hash}.js |- css/ |- stylesheet-{hash}.css assets/ |- js/ |- vendor/ |- jquery.js |- site.js |- module1.js |- module2.js |- css/ |- main.css |- custom.css gulpfile.js package.json gulpfile.js dosyası, Gulp'un bizim için gerçekleştireceği görevleri tanımlayacağımız yerdir. package.json , npm tarafından uygulamamızın paketini tanımlamak ve kuracağımız bağımlılıkları izlemek için kullanılır. Genel dizin, web'e bakacak şekilde yapılandırılması gereken şeydir. Varlıklar dizini, kaynak dosyalarımızı depolayacağımız yerdir. Gulp'u projede kullanmak için npm üzerinden kurmamız ve proje için geliştirici bağımlılığı olarak kaydetmemiz gerekecek. Ayrıca, birden fazla dosyayı tek bir dosyada birleştirmemize izin verecek olan Gulp için concat eklentisiyle başlamak isteyeceğiz.
Bu iki öğeyi yüklemek için aşağıdaki komutu çalıştıracağız:
npm install --save-dev gulp gulp-concatArdından gulpfile.js'nin içeriğini yazmaya başlayacağız.
var gulp = require('gulp'); var concat = require('gulp-concat'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(gulp.dest('public/build/js')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(gulp.dest('public/build/css')); }); gulp.task('default', ['pack-js', 'pack-css']);Burada gulp kütüphanesini ve concat eklentisini yüklüyoruz. Daha sonra üç görev tanımlıyoruz.
İlk görev ( pack-js ), birden çok JavaScript kaynak dosyasını tek bir pakette sıkıştırmak için bir prosedür tanımlar. Belirtilen sırayla globbed, okunacak ve birleştirilmiş kaynak dosyaları listeleriz. bundle.js adlı son bir dosya almak için bunu concat eklentisine aktarıyoruz. Son olarak, gulp'e dosyayı public/build/js dizinine yazmasını söyleriz.
İkinci görev ( pack-css ) yukarıdakiyle aynı şeyi yapar, ancak CSS stil sayfaları için. Gulp'a birleştirilmiş çıktıyı public/build/css içinde stylesheet.css olarak saklamasını söyler.
Üçüncü görev ( default ), herhangi bir argüman olmadan çağırdığımızda Gulp'un çalıştırdığı görevdir. İkinci parametrede ise varsayılan görev çalıştırıldığında yürütülecek diğer görevlerin listesini geçiyoruz.
Normalde kullandığımız herhangi bir kaynak kod düzenleyicisini kullanarak bu kodu gulpfile.js'ye yapıştıralım ve ardından dosyayı uygulama köküne kaydedelim.
Ardından komut satırını açıp çalıştıracağız:
gulp Bu komutu çalıştırdıktan sonra dosyalarımıza bakarsak, iki yeni dosya bulacağız: public/build/js/bundle.js ve public/build/css/stylesheet.css . Bunlar, orijinal sorunun bir kısmını çözen kaynak dosyalarımızın birleştirilmiş halidir. Ancak, küçültülmediler ve henüz önbellek bozma yok. Otomatik küçültme ekleyelim.
Yerleşik Varlıkları Optimize Etme
İki yeni eklentiye ihtiyacımız olacak. Bunları eklemek için aşağıdaki komutu çalıştıracağız:
npm install --save-dev gulp-clean-css gulp-minifyİlk eklenti CSS'yi küçültmek, ikincisi JavaScript'i küçültmek içindir. İlki clean-css paketini, ikincisi ise UglifyJS2 paketini kullanır. İlk önce gulpfile.js dosyamıza bu iki paketi yükleyeceğiz:
var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css');Çıktıyı diske yazmadan hemen önce bunları görevlerimizde kullanmamız gerekecek:
.pipe(minify()) .pipe(cleanCss())gulpfile.js şimdi şöyle görünmelidir:
var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify()) .pipe(gulp.dest('public/build/js')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(gulp.dest('public/build/css')); }); gulp.task('default', ['pack-js', 'pack-css']); Tekrar yudum çalıştıralım. stylesheet.css dosyasının küçültülmüş biçimde kaydedildiğini ve bundle.js dosyasının hala olduğu gibi kaydedildiğini göreceğiz. Artık küçültülmüş paket-min.js'ye de sahip olduğumuzu fark edeceğiz. Yalnızca küçültülmüş dosyayı ve bunun bundle.js olarak kaydedilmesini istiyoruz, bu nedenle kodumuzu ek parametrelerle değiştireceğiz:

.pipe(minify({ ext:{ min:'.js' }, noSource: true }))gulp-minify eklenti belgelerine göre (https://www.npmjs.com/package/gulp-minify), bu, küçültülmüş sürüm için istenen adı belirler ve eklentiye orijinal kaynağı içeren sürümü oluşturmamasını söyler. Derleme dizininin içeriğini siler ve komut satırından tekrar gulp çalıştırırsak, elimizde sadece iki küçültülmüş dosya kalır. Oluşturma sürecimizin küçültme aşamasını uygulamayı yeni bitirdik.
Önbellek Bozma
Ardından, önbellek bozma eklemek isteyeceğiz ve bunun için bir eklenti yüklememiz gerekecek:
npm install --save-dev gulp-revVe bunu gulp dosyamızda talep edin:
var rev = require('gulp-rev');Eklentiyi kullanmak biraz zor. Önce küçültülmüş çıktıyı eklentiden geçirmeliyiz. Daha sonra sonuçları diske yazdıktan sonra eklentiyi tekrar çağırmalıyız. Eklenti, dosyaları benzersiz bir karma ile etiketlenecek şekilde yeniden adlandırır ve ayrıca bir bildirim dosyası oluşturur. Manifest dosyası, HTML kodumuzda başvurmamız gereken en son dosya adlarını belirlemek için uygulamamız tarafından kullanılabilecek bir haritadır. Gulp dosyasını değiştirdikten sonra, şöyle görünmelidir:
var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); var rev = require('gulp-rev'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify({ ext:{ min:'.js' }, noSource: true })) .pipe(rev()) .pipe(gulp.dest('public/build/js')) .pipe(rev.manifest()) .pipe(gulp.dest('public/build')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(rev()) .pipe(gulp.dest('public/build/css')) .pipe(rev.manifest()) .pipe(gulp.dest('public/build')); }); gulp.task('default', ['pack-js', 'pack-css']); Derleme dizinimizin içeriğini silip tekrar gulp çalıştıralım. Artık dosya adlarının her birine hashtag'leri eklenmiş iki dosyamız ve public/build dosyasına kaydedilmiş bir manifest.json'umuz olduğunu göreceğiz. Manifest dosyasını açarsak, bunun yalnızca küçültülmüş ve etiketlenmiş dosyalarımızdan birine referansı olduğunu göreceğiz. Olan şu ki, her görev ayrı bir bildirim dosyası yazar ve bunlardan biri diğerinin üzerine yazar. Görevleri, mevcut bildirim dosyasını aramalarını ve varsa yeni verileri onunla birleştirmelerini söyleyen ek parametrelerle değiştirmemiz gerekecek. Bunun sözdizimi biraz karmaşık, bu yüzden kodun nasıl görünmesi gerektiğine bakalım ve sonra üzerinden geçelim:
var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); var rev = require('gulp-rev'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify({ ext:{ min:'.js' }, noSource: true })) .pipe(rev()) .pipe(gulp.dest('public/build/js')) .pipe(rev.manifest('public/build/rev-manifest.json', { merge: true })) .pipe(gulp.dest('')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(rev()) .pipe(gulp.dest('public/build/css')) .pipe(rev.manifest('public/build/rev-manifest.json', { merge: true })) .pipe(gulp.dest('')); }); gulp.task('default', ['pack-js', 'pack-css']); Çıktıyı önce rev.manifest() 'e aktarıyoruz. Bu, daha önce sahip olduğumuz dosyalar yerine etiketli dosyalar oluşturur. rev-manifest.json dosyamızın istenen yolunu sağlıyoruz ve rev.manifest() varsa, mevcut dosyayla birleşmesini söylüyoruz. Ardından, gulp'e manifest'i o noktada public/build olacak olan geçerli dizine yazmasını söylüyoruz. Yol sorunu, GitHub'da daha ayrıntılı olarak tartışılan bir hatadan kaynaklanmaktadır.
Artık otomatik küçültme, etiketli dosyalar ve bir bildirim dosyamız var. Tüm bunlar, dosyaları kullanıcıya daha hızlı teslim etmemize ve değişikliklerimizi yaptığımızda önbelleklerini bozmamıza izin verecek. Ancak geriye kalan sadece iki sorun var.
İlk sorun, kaynak dosyalarımızda herhangi bir değişiklik yaparsak, yeni etiketlenmiş dosyalar alacağız, ancak eskileri de orada kalacak. Eski küçültülmüş dosyaları otomatik olarak silmenin bir yoluna ihtiyacımız var. Dosyaları silmemize izin verecek bir eklenti kullanarak bu sorunu çözelim:
npm install --save-dev delKodumuzda buna ihtiyaç duyacağız ve her bir kaynak dosya türü için bir tane olmak üzere iki yeni görev tanımlayacağız:
var del = require('del'); gulp.task('clean-js', function () { return del([ 'public/build/js/*.js' ]); }); gulp.task('clean-css', function () { return del([ 'public/build/css/*.css' ]); });Ardından, yeni görevin iki ana görevimizden önce çalışmayı bitirdiğinden emin olacağız:
gulp.task('pack-js', ['clean-js'], function () { gulp.task('pack-css', ['clean-css'], function () { Bu değişiklikten sonra tekrar gulp çalıştırırsak, en son küçültülmüş dosyalara sahip olacağız.
İkinci sorun, her değişiklik yaptığımızda yutkunmaya devam etmek istemememizdir. Bunu çözmek için bir izleyici görevi tanımlamamız gerekecek:
gulp.task('watch', function() { gulp.watch('assets/js/**/*.js', ['pack-js']); gulp.watch('assets/css/**/*.css', ['pack-css']); });Varsayılan görevimizin tanımını da değiştireceğiz:
gulp.task('default', ['watch']); Şimdi komut satırından yudum çalıştırırsak, artık çağrı üzerine hiçbir şey oluşturmadığını görürüz. Bunun nedeni, artık herhangi bir değişiklik için kaynak dosyalarımızı izleyecek ve yalnızca bir değişiklik algıladığında oluşturacak olan izleyici görevini çağırmasıdır. Kaynak dosyalarımızdan herhangi birini değiştirmeyi denersek ve ardından konsolumuza tekrar bakarsak, pack-js ve pack-css görevlerinin bağımlılıklarıyla birlikte otomatik olarak çalıştığını göreceğiz.
Şimdi tek yapmamız gereken manifest.json dosyasını uygulamamıza yüklemek ve bundan etiketli dosya isimlerini almak. Bunu nasıl yapacağımız, özel arka uç dilimize ve teknoloji yığınımıza bağlıdır ve uygulanması oldukça önemsiz olacaktır, bu nedenle ayrıntılı olarak üzerinde durmayacağız. Ancak genel fikir, bildirimi bir diziye veya nesneye yükleyebilmemiz ve ardından aşağıdakine benzer bir şekilde şablonlarımızdan sürümlü varlıkları çağırmamıza izin verecek bir yardımcı işlev tanımlayabilmemizdir:
gulp('bundle.js')Bunu yaptığımızda, bir daha dosya isimlerimizdeki değişen etiketler hakkında endişelenmemize gerek kalmayacak ve yüksek kaliteli kod yazmaya odaklanabileceğiz.
Birkaç örnek varlıkla birlikte bu makalenin son kaynak kodu bu GitHub deposunda bulunabilir.
Çözüm
Bu yazıda, inşa sürecimiz için Gulp tabanlı otomasyonun nasıl uygulanacağını inceledik. Umarım bu size yardımcı olur ve kendi uygulamalarınızda daha karmaşık oluşturma süreçleri geliştirmenize olanak tanır.
Lütfen Gulp'un bu amaç için kullanılabilecek araçlardan sadece biri olduğunu ve Grunt, Browserify ve Webpack gibi daha birçok araç olduğunu unutmayın. Amaçları ve çözebilecekleri problemlerin kapsamı bakımından farklılık gösterirler. Bazıları, isteğe bağlı olarak yüklenebilen bağımlılıklarla JavaScript modüllerini paketlemek gibi Gulp'un çözemediği sorunları çözebilir. Bu, "kod bölme" olarak adlandırılır ve her sayfada programımızın tüm bölümleriyle büyük bir dosya sunma fikrine göre bir gelişmedir. Bu araçlar oldukça karmaşıktır ancak gelecekte kapsanabilir. Bir sonraki gönderide, uygulamamızın dağıtımını nasıl otomatikleştireceğimizi gözden geçireceğiz.
