Merhaba arkadaşlar, bugün sizlerle uzun süredir canımı sıkan ve sonunda kendimce temiz bir çözüm bulduğum bir konuyu paylaşmak istiyorum. Projelerimizde, tasarımcılar bize genellikle devasa boyutlarda, yüksek çözünürlüklü PSD'ler veya PNG'ler gönderiyor. Geliştirme aşamasında bunları olduğu gibi kullanmak işimizi kolaylaştırıyor ama production'a geçtiğimizde sayfa hızları berbata dönüyordu. Her seferinde manuel optimize etmek de zaman kaybıydı. İşte bu dertten kurtulmak için kurduğum otomatik pipeline'ı anlatacağım.
Sorun Tam Olarak Neydi?
Geliştirme ortamında `assets/images/original/` gibi bir klasöre attığımız 5-10 MB'lık görseller, doğrudan build işlemine giriyor ve nihai `dist` veya `public` klasörüne aynen kopyalanıyordu. Bu da, bir blog yazısındaki tek bir görselin bile sayfa açılışını saniyelerce geciktirmesine neden oluyordu. Lighthouse skorlarımız sürekli "Resimleri optimize edin" uyarısı veriyordu. "Bir script yazayım da otomatik yapsın" deyip her seferinde erteliyordum, ta ki bir projede Lighthouse Performance skoru 32 görünceye kadar. O an karar verdim, bu işi kalıcı çözecektim.
Çözüm: Gulp + Sharp İkilisi
Birkaç farklı araç denedikten sonra, Gulp ile Sharp kütüphanesini birlikte kullanmanın hem hızlı hem de esnek olduğuna karar verdim. Gulp, dosya işlemleri için mükemmel bir task runner, Sharp ise Node.js için yazılmış, inanılmaz hızlı bir resim işleme kütüphanesi. Webpack loader'ları da vardı ama daha fazla kontrol istediğim için bu yolu seçtim.
İlk adım, projeye bu paketleri yüklemek oldu.
Gulpfile.js'in Sihirli Yapılandırması
Ana mantık şu: `src/images/original/` klasöründeki tüm görselleri (`jpg, jpeg, png, webp`) al, optimize et, boyutlandır ve `dist/images/` klasörüne farklı formatlarda yaz. Aynı dosyayı tekrar işlememek için `gulp-newer` eklentisini kullandım.
İşte benim `gulpfile.js` dosyamın özü:
Elde Ettiğimiz Sonuç ve Picture Etiketi
Bu pipeline çalıştığında, `src/images/original/banner.jpg` (5MB) dosyasından şunlar üretiliyor:
- `dist/images/banner.jpg` (~150KB, optimize edilmiş)
- `dist/images/webp/banner.webp` (~80KB)
- `dist/images/responsive/banner-320w.jpg`, `banner-640w.jpg`, ... (farklı boyutlar)
Artık HTML'de modern `picture` etiketini rahatlıkla kullanabiliyoruz:
Veya responsive resimler için `srcset`:
Neden Bu Yöntemi Seviyorum?
Çünkü tamamen otomatik. Geliştirici olarak ben sadece orijinal, yüksek kaliteli dosyayı `original` klasörüne atıyorum. `npm run build` dediğimde veya `gulp watch` ile canlı takip ettiğimde, tüm optimizasyonlar ve format dönüşümleri arka planda hallediliyor. Ayrıca, gulp-newer sayesinde sadece değişen dosyalar işlendiği için build süreleri de kısaldı.
Sonuç olarak, Lighthouse skorlarımız yerden göğe fırladı. Artık "Resimleri optimize edin" uyarısı görmüyoruz ve kullanıcı deneyimi ciddi anlamda iyileşti.
Sizler de benzer bir pipeline kullanıyor musunuz? Farklı olarak hangi araçları tercih ediyorsunuz? (Örn: ImageMagick, Squoosh.app, Webpack loaders) Ya da Next.js gibi framework'lerin built-in image optimization özelliklerini mi kullanıyorsunuz? Yorumlarda deneyimlerinizi paylaşın, tartışalım!
Geliştirme ortamında `assets/images/original/` gibi bir klasöre attığımız 5-10 MB'lık görseller, doğrudan build işlemine giriyor ve nihai `dist` veya `public` klasörüne aynen kopyalanıyordu. Bu da, bir blog yazısındaki tek bir görselin bile sayfa açılışını saniyelerce geciktirmesine neden oluyordu. Lighthouse skorlarımız sürekli "Resimleri optimize edin" uyarısı veriyordu. "Bir script yazayım da otomatik yapsın" deyip her seferinde erteliyordum, ta ki bir projede Lighthouse Performance skoru 32 görünceye kadar. O an karar verdim, bu işi kalıcı çözecektim.
Birkaç farklı araç denedikten sonra, Gulp ile Sharp kütüphanesini birlikte kullanmanın hem hızlı hem de esnek olduğuna karar verdim. Gulp, dosya işlemleri için mükemmel bir task runner, Sharp ise Node.js için yazılmış, inanılmaz hızlı bir resim işleme kütüphanesi. Webpack loader'ları da vardı ama daha fazla kontrol istediğim için bu yolu seçtim.
İlk adım, projeye bu paketleri yüklemek oldu.
JavaScript:
npm install --save-dev gulp gulp-sharp gulp-newer gulp-rename
Ana mantık şu: `src/images/original/` klasöründeki tüm görselleri (`jpg, jpeg, png, webp`) al, optimize et, boyutlandır ve `dist/images/` klasörüne farklı formatlarda yaz. Aynı dosyayı tekrar işlememek için `gulp-newer` eklentisini kullandım.
İşte benim `gulpfile.js` dosyamın özü:
JavaScript:
const gulp = require('gulp');
const sharp = require('gulp-sharp');
const newer = require('gulp-newer');
const rename = require('gulp-rename');
// Ana resim optimizasyon task'ı
function optimizeImages() {
return gulp.src('src/images/original//.{jpg,jpeg,png,webp}')
.pipe(newer('dist/images')) // Sadece değişenleri işle
.pipe(sharp({
failOnError: false,
webp: { quality: 80 }, // WebP formatında çıktı al
avif: { quality: 70 }, // AVIF formatında çıktı al (isteğe bağlı)
}))
.pipe(rename({ extname: '.webp' })) // Uzantıyı .webp yap
.pipe(gulp.dest('dist/images/webp'))
.pipe(sharp({
jpeg: { quality: 75, progressive: true }, // Progressive JPEG
png: { compressionLevel: 8, palette: true } // PNG optimizasyonu
}))
.pipe(gulp.dest('dist/images')); // Orijinal formatında (jpg/png) kaydet
}
// Farklı breakpoint'ler için resim boyutlandırma
function generateResponsiveImages() {
const sizes = [640, 768, 1024, 1280];
return gulp.src('src/images/original//.{jpg,jpeg,png}')
.pipe(newer('dist/images/responsive'))
.pipe(sharp())
.pipe(sharp.resize({ width: 320 }))
.pipe(rename({ suffix: '-xs' }))
.pipe(gulp.dest('dist/images/responsive'))
.pipe(sharp())
.pipe(
sharp.resize(
sizes.map(width => ({ width }))
)
)
.pipe(
rename(filePath => {
sizes.forEach(size => {
if (filePath.width === size) {
filePath.suffix = `-${size}w`;
}
});
})
)
.pipe(gulp.dest('dist/images/responsive'));
}
// İzleme (watch) task'ı
function watchImages() {
gulp.watch('src/images/original//', gulp.parallel(optimizeImages, generateResponsiveImages));
}
// Default task
exports.default = gulp.parallel(optimizeImages, generateResponsiveImages);
exports.watch = watchImages;
Bu pipeline çalıştığında, `src/images/original/banner.jpg` (5MB) dosyasından şunlar üretiliyor:
- `dist/images/banner.jpg` (~150KB, optimize edilmiş)
- `dist/images/webp/banner.webp` (~80KB)
- `dist/images/responsive/banner-320w.jpg`, `banner-640w.jpg`, ... (farklı boyutlar)
Artık HTML'de modern `picture` etiketini rahatlıkla kullanabiliyoruz:
HTML:
<picture>
<source srcset="/images/webp/banner.webp" type="image/webp">
<source srcset="/images/banner.jpg" type="image/jpeg">
<img src="/images/banner.jpg" alt="Açıklayıcı metin" loading="lazy">
</picture>
Veya responsive resimler için `srcset`:
HTML:
<img src="/images/responsive/banner-640w.jpg"
srcset="/images/responsive/banner-320w.jpg 320w,
/images/responsive/banner-640w.jpg 640w,
/images/responsive/banner-1024w.jpg 1024w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="Açıklayıcı metin">
Çünkü tamamen otomatik. Geliştirici olarak ben sadece orijinal, yüksek kaliteli dosyayı `original` klasörüne atıyorum. `npm run build` dediğimde veya `gulp watch` ile canlı takip ettiğimde, tüm optimizasyonlar ve format dönüşümleri arka planda hallediliyor. Ayrıca, gulp-newer sayesinde sadece değişen dosyalar işlendiği için build süreleri de kısaldı.
Sonuç olarak, Lighthouse skorlarımız yerden göğe fırladı. Artık "Resimleri optimize edin" uyarısı görmüyoruz ve kullanıcı deneyimi ciddi anlamda iyileşti.
Sizler de benzer bir pipeline kullanıyor musunuz? Farklı olarak hangi araçları tercih ediyorsunuz? (Örn: ImageMagick, Squoosh.app, Webpack loaders) Ya da Next.js gibi framework'lerin built-in image optimization özelliklerini mi kullanıyorsunuz? Yorumlarda deneyimlerinizi paylaşın, tartışalım!