Webpack Nedir ve Nasıl Kullanılır?
Frontend dünyası son yıllarda daha karmaşık ve daha takip edilemez bir hale geldi. Neredeyse her gün yeni kavramlar, kütüphaneler ve yöntemler hayatımıza girmeye başladı. Bu yazıda bu karmaşıklığı bir nebze ortadan kaldıran, production için daha performanslı ve daha optimize bir çıktı alabilmemizi sağlayan webpack'den bahsetmeye çalışacağım.
Webpack'e geçmeden önce bu yazıda neler öğreneceğiz nelerden bahsedeceğiz liste halinde görelim.
Neler Öğreneceğiz?
- Neden webpack kullanmalıyız?
- Hangi sorunları çözüyor?
- Webpack nedir?
- Webpack konseptleri nelerdir?
- Webpack kurulumu nasıl yapılır?
- Projeye nasıl dahil edilir?
- Webpack plugin'leri nasıl kullanılır?
Webpack Hangi Sorunları Çözüyor?
Eskiden frontend geliştirmek için 3 - 5 kütüphane, tek bir javascript ve css dosyası yeterli oluyordu. Hem javascript'in aşırı popülerleşmesi hem de bir web projesinden beklentilerin artması ile birlikte onlarca kütüphane ve geliştirme yapmak için birden fazla javascript ve css dosyasına ihtiyaç duymaya başladık. Bu ihtiyaçlar da birden fazla problemi beraberinde getirdi.
Web sayfasında onlarca dosya çağırmak tarayıcıların aynı anda paralel olarak indirebileceği dosya sayısının belirli bir limitinin olmasından dolayı performans sorunlarına sebep olmaya başladı. Bu sorunu çözmek için kullandığımız dosyaları küçültmemiz ve yapabiliyorsak tek bir dosya haline getirmemiz gerekiyordu. Zamanla bu işlemleri ve daha fazlasını yapabilen gulp, grunt gibi javascript task runner'lar geliştirildi.
Javascript task runner'lar sıkıştırma, bundle oluşturma, optimizasyon, css preprocessor'ların compile edilmesi, sprite oluşturma gibi bir çok işlemi bizim yerimize yapan araçlardır. Tabi bu araçlar da günümüz frontend yapısına ayak uydurmaya yeterli olamadı. Çünkü bu araçlar build işlemlerini dosyalar arasındaki ilişkiyi gözetmeksizin yapabiliyordu.
Sonunda kullandığımız kütüphaneler ile yaptığımız geliştirmeler arasındaki ilişkilerin de gözetilerek bir build işlemi yapabildiğimiz webpack ortaya çıktı. Zamanla webpack hem yetenekli hem çok yönlü hem de bir çok soruna çözüm bulan ve çok popüler bir araç haline geldi.
Webpack Nedir?
Webpack Node.js tabanlı çok akıllı bir modül paketleyicisidir (module bundler). Birden fazla dosya tipini işleyebilir bu dosyalar üzerinde farklı işlemler (transpile, concat, minify vs.) yapabilir ve belirtilen biçimde statik bundle'lar (paketler) oluşturabilir. Ayrıca modüllerin tüm bağımlılıklarını takip eder, bir bundle oluşturulacağı zaman ise kullanılan bağımlılıkları bildiği için optimize edilmiş ve duplicate (kopya) bağımlılıklardan arındırılmış bir çıktı oluşturur.
Webpack Konseptleri Nelerdir?
Webpack kendi içinde 5 adet konsepte sahiptir. Webpack kurulumuna geçmeden önce bu kavramları açıklamaya çalışayım.
- Entry: Webpack'in dependency graph'ını (bağımlılık grafiği) oluşturubilmesi için bir başlangıç noktasına ihtiyacı vardır. Webpack bu başlangıç dosyasından başlar ve tüm modülleri gezerek bağımlılıkları yönetmeye başlar. Webpack'de
./src/index.js
default başlangıç dosyasıdır. Bu dosya dışında farklı dosya yolu verebiliriz. Ayrıca entry olarak birden fazla başlangıç dosyası da girmek mümkün.
module.exports = {
entry: {
detail: './src/detail.js',
search: './src/search.js',
vendor: './src/vendor.js'
}
}
- Output: Webpack'in tüm işlemlerden sonra oluşturduğu bundle dosyasını hangi klasöre yazacağını belirtiğimiz kısımdır. Bu klasör genellikle
dist
veyabuild
klasörü olur. Fakat siz istediğiniz bir klasörü seçebilirsiniz. Aşağıdaki örnekte webpack işlemlerini bitirdikten sonradist
klasörü altındadetail.js
,search.js
vevendor.js
dosyaları oluşturur.
module.exports = {
entry: {
detail: './src/detail.js',
search: './src/search.js',
vendor: './src/vendor.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
}
Loaders: Webpack sadece javascript ve json dosyalarını işleyebilir. Webpack'e diğer dosya tiplerini de işleyebilme yeteneği kazandırmak için loader'ları kullanırız.
Plugins: Plugin'ler webpack ekosisteminde çok önemli yere sahiptirler. Loader'ların yetmediği bazı işlemleri ve görevleri plugin'ler yardımıyla yaparız. Plugin'ler asset management, bundle minimization, optimization gibi birçok görevi yerine getirebilir.
Mode: Genellikle bir uygulama geliştirken development ve production olmak üzere iki tip kaynak koduna sahip oluruz. Webpack tarafında da bunu ayarlamak mümkün. Production modu kodun optimize edilmiş halini, development modu geliştirme yaparken kullandığımız optimize edilmemiş halini işaret eder. Bu ayarı iki şekilde yapabiliriz.
module.exports = {
mode: 'development'
}
webpack --mode=development
Webpack Kurulumu Nasıl Yapılır?
Webpack hakkında birçok bilgiye sahip olduk. Gelin şimdi uzun uzun bahsettiğimiz webpack'i projemize kuralım.
# Global olarak kurulum
npm install -g webpack webpack-cli
# Proje bazlı kurulum
npm install --save-dev webpack webpack-cli
💡 Webpack versiyonları ile baş ağrısı çekmek istemiyorsanız proje bazlı bir webpack kurulumu yapmak daha faydalı olacaktır. Yazının bu kısmından itibaren proje bazlı kurulum üzerinden anlatmaya çalışacağım.
Webpack Kullanarak Uygulama Geliştirelim
Evet şimdi kolları sıvayıp webpack'i kullanmaya sıra geldi. Öncelikle projemiz için bir klasör oluşturalım. Bu işlemleri adım adım uygulayarak takip etmenizi tavsiye ediyorum. webpack-example
adında bir klasör oluşturalım ve npm init -y
komutunu terminalde çalıştıralım. Bu komut ile birlikte proje dizinimizde dependency'lerimizi tuttuğumuz package.json
dosyası oluşturulmuş oldu.
Projemize webpack kurmak için aşağıdaki komutu terminalde çalıştıralım.
npm install --save-dev webpack webpack-cli
package.json
dosyamız görseldeki gibi güncellenmiş olacaktır.
Webpack konfigürasyonlarını tanımlamak için webpack.config.js
dosyasını oluşturmamız gerekiyor. Proje dizininde bu dosyayı oluşturalım.
Webpack konfigürasyonunu entry olarak src/
klasörü output olarak dist/
klasörü olacak şekilde ayarlayalım.
module.exports = {
entry: {
index: './src/index.js'
},
output: {
filename: '[name].[chunkhash].js',
// [chunkhash]: her build işleminde benzersiz bir çıktı üretmek için kullanılır.
path: __dirname + '/dist'
}
}
src
klasörü altında index.js
dosyası oluşturup test için iki sayıyı toplayan bir fonksiyon oluşturup bu fonksiyonu çağıralım.
const sum = (a, b) => a + b
console.log(sum(4, 5))
Son olarak package.json
dosyasında scripts
property'sini güncelleyelim.
"scripts": {
"dev": "webpack --mode=development",
"build": "webpack --mode=production"
},
npm run dev
komutunu terminalde çalıştırıp sihri görelim 😄. Adımları doğru uyguladıysanız proje dizininde dist
klasörü altında index.[hash].js
dosyamızın oluştuğunu görebilirsiniz. Bu dosyayı incelediğimizde optimize edilmemiş ve yorum satırları bulunan bir içeriğe sahip olduğunu görebiliriz. Çünkü webpack'i development modunda çalıştırdık.
Terminalde npm run build
komutunu çalıştırıp production modunda bir çıktı üretelim. Bu sefer dist
klasöründe tamamen optimize ve minify edilmiş bir içeriğe sahip dosya oluşturuldu.
Fakat bir sorun var gibi... Her build işleminden sonra dist
klasörü altında yeni index.js
dosyaları oluşuyor.
Build işlemlerinden sonra temiz bir dist
klasörü elde için clean-webpack-plugin
paketini aşağıdaki komutla yükleyelim.
npm install --save-dev clean-webpack-plugin
Plugin'ler hakkında ufak bir bilgi elde etmiştik. Config dosyamızı güncelleyip indirdiğimiz plugin'i kullanalım.
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ...
plugins: [new CleanWebpackPlugin()]
}
Webpack İle HTML Dosyalarını Yönetmek
Şu ana kadar build işlemlerini webpack ile yaptık. Fakat yaptığımız işlemleri hala bir web tarayıcısında görebilmiş değiliz. Bunun için webpack'in html dosyalarını işlemesini sağlatmalıyız. Dolayısıyla projemize aşağıdaki komutu çalıştırarak html-webpack-plugin
plugin'ini yükleyelim.
npm install --save-dev html-webpack-plugin
src
klasörü altına aşağıdaki gibi bir index.html
dosyası oluşturalım.
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
Hello From Webpack
</body>
</html>
Şimdi de build sırasında html dosyalarının da işleme alınması için config dosyamızı güncelleyelim.
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
plugins: [
// ...
new HtmlWebpackPlugin({
template: './src/index.html',
inject: true
// inject: true => Otomatik olarak build dosyasını script tag'ı olarak eklemeyi sağlar.
})
]
}
npm run dev
komutunu çalıştıralım. dist
klasöründe artık index.html
dosyamız da var. Bu dosyayı tarayıcıda açıp console
'u açarsanız yaptığımız örnekteki toplama işleminin sonucunu görebilirsiniz.
webpack-dev-server
Geliştirme yaparken local server oluşturmak ve herhangi bir değişlik yaptımızda tarayıcının otomatik yenilenmesini sağlayan webpack-dev-server
paketini kuralım ve zamandan tasarruf edelim 😋.
npm install --save-dev webpack-dev-server
Kurulumdan sonra package.json
dosyasını da güncellememiz gerekiyor. Dosyayı açıp aşağıdaki gibi güncelleyelim.
"scripts": {
// ...
"start": "webpack-dev-server --mode=development"
// ...
},
npm run start
komutunu çalıştırdıktan sonra terminalde muhtemelen aşağıdaki gibi bir görüntü oluşacak.
Tarayıcınızdan http://localhost:8080
adresine gidip kodunuzda herhangi bir değişiklik yaparsanız sayfanın otomatik olarak yenilendiğini görebilirsiniz 🎉.
ES6 Kodunu Transpile Etmek
Geliştiriciler olarak modern javascript kullanmayı severiz. Fakat eski tarayıcılar bunu anlayamaz ve yazdığımız kod maalesef çalışmaz. Bu yüzden production'daki kodumuzun her zaman eski tarayılara uyumlu olması gerekir. Webpack tarafında bu transpile işlemini yapabilmek için loader'lara ihtiyacımız vardır. Gerekli paketleri aşağıdaki komutu çalıştırarak projemize yükleyelim.
npm install --save-dev babel-loader @babel/core @babel/preset-env
Gerekli paketleri yükledikten sonra config dosyamıza loader tanımlayalım.
module.exports = {
// ...
module: {
rules: [
{
test: [/.js$/], // test => Hangi dosya tiplerinin işlemden geçeceğini belirttiğimiz property
exclude: /(node_modules)/, // exclude => Hangi klasörlerin işlemden geçmeyeceğini belirttiğimiz property
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
// ...
}
Artık özgürce yeni javascript özelliklerini kullanabiliriz. Kodumuz build olduğunda tarayıcıların anlayabileceği formata otomatik olarak çevrilecektir 🎉.
CSS Dosyaları İle Çalışmak
Webpack'de css dosyaları ile çalışabilmek için css-loader ve style-loader olmak üzere iki adet loader'a ihtiyacımız vardır. css-loader uygulamadaki tüm stilleri toplar ve string haline dönüştürür. style-loader ise bu string çıktısını alır ve html sayfamızda <style>
tagleri arasına yazar. Hemen bu paketleri yükleyelim.
npm install --save-dev css-loader style-loader
Yükleme işlemi bittikten sonra config dosyamızı güncelleyelim.
module.exports = {
// ...
module: {
rules: [
// ...
{
test: [/.css$/],
use: ['style-loader', 'css-loader']
}
// ...
]
}
// ...
}
Şimdi src
klasörü altında styles
adında bir klasör ve index.css
dosyası oluşturalım ve çalıştığına emin olmak için css dosyamıza aşağıdaki kodu ekleyelim. Son olarak da index.css
dosyasını index.js
dosyasında çağıralım.
body {
font-size: 40px;
color: blue;
}
import './styles/index.css'
// ...
npm run start
komutunu çalıştırarak tarayıcıda sonucu görelim.
Sass Dosyalarını CSS Dosyalarına Çevirmek
Webpack ile sass dosyalarını css dosyalarına çevirmek için node-sass ve sass-loader paketlerini yüklememiz gerekir. sass-loader node-sass yardımıyla sass dosyalarını css dosyalarına çevirir. Paketleri indirip config dosyamızı güncelleyelim.
npm install --save-dev node-sass sass-loader
module.exports = {
// ...
module: {
rules: [
// ...
{
test: [/.css$|.scss$|.sass$/],
use: ['style-loader', 'css-loader', 'sass-loader']
}
// ...
]
}
// ...
}
Buradaki loader'ların sıralaması önemlidir. Çünkü önce sass dosyaları css dosyalarına çevirlmelidir. Ardından stiller stringlere dönüştürülüp html dosyasına yazılmalıdır. Bu sıralamayı değiştirirseniz webpack tarafında hata alırsınız.
src/styles
klasörü altına index.sass
dosyası ekleyip aşağıdaki gibi güncelleyelim ve index.js
dosyasında çağıralım.
$body-bg-color: #ddd
body
background-color: $body-bg-color
// ...
import './styles/index.sass'
// ...
Tarayıcıyı açtığınızda sass dosyamızdaki kodun css koduna çevrildiğini görebilirsiniz.
Stilleri Css Dosyasına Yazmak
Çoğu zaman stillerimizi inline olarak eklemek yerine css dosyaları halinde tutmak isteriz. Webpack'de stilleri css dosyasına yazmak için mini-css-extract-plugin
paketini yüklememiz gerekir. Aşağıdaki komutu çalıştırıp paketimizi yükleyelim ve config dosyamızı güncelleyelim.
npm install --save-dev mini-css-extract-plugin
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// ...
module: {
rules: [
// ...
{
test: [/.css$|.scss$|.sass$/],
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
}
// ...
]
},
plugins: [
// ...
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
}
Config dosyamızda stillerimizi inline olarak kullanmak yerine css dosyasına yazmak istediğimiz style-loader'ı kaldırdık yerine MiniCssExtractPlugin.loader
ekledik. Tarayıcıya girip kontrol ettiğimizde style.css
dosyamızın başarılı bir şekilde eklendiğini görebiliriz.
Webpack Alias İle Dosyalara Kolay Ulaşım
Üzerinde çalıştığımız proje büyüdükçe ve iç içe klasör sayısı arttığında belirli seviyelerdeki dosyalara ulaşmak çok zahmetli hale gelebiliyor. Bir örnekle sorunu anlamaya çalışalım.
// ...
import getErrorType from '../../../../utils/getErrorType'
import smileIcon from '../../../../../images/icons/smileIcon.png'
Bu kullanım oldukça çirkin duruyor. Fakat webpack bunu da düşünmüş ve klasörlerimize alias yani bir çeşit takma ad koyabilmemizi ve daha kısa yoldan ulaşabilmemizi sağlayabiliyor. Bunun için config dosyamızda ufak bir değişiklik yapmamız yeterli olacaktır.
// ...
const path = require('path')
module.exports = {
// ...
resolve: {
alias: {
'@components': path.resolve(__dirname, 'src/components/'),
'@containers': path.resolve(__dirname, 'src/containers/'),
'@context': path.resolve(__dirname, 'src/context/'),
'@images': path.resolve(__dirname, 'src/images/'),
'@utils': path.resolve(__dirname, 'src/utils/')
}
}
}
Config dosyamızdaki güncellemeden sonra çirkin duran kodumuzu aşağıdaki gibi güncelleyebiliriz.
// ...
import getErrorType from '@utils/getErrorType'
import smileIcon from '@images/icons/smileIcon.png'
Kapanış
Oldukça uzun soluklu bir yazı olduğunun farkındayım fakat webpack gibi bir konuyu üstün körü anlatamazdım. Bu yazı ile ilgili soru, öneri veya eklemek istediklerinizi aşağıdaki yorum kısmından yazabilirsiniz. Ayrıca config dosyamızın son halini de aşağıda paylaşıyorum.
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: {
index: './src/index.js'
},
output: {
filename: '[name].[chunkhash].js',
path: __dirname + '/dist'
},
module: {
rules: [
{
test: [/.js$/],
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: [/.css$|.scss$|.sass$/],
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
inject: true
}),
new MiniCssExtractPlugin({
filename: 'style.css'
})
],
resolve: {
alias: {
'@styles': path.resolve(__dirname, 'src/styles/')
}
}
}
Yorumlar
Soru, cevap ve destekleriniz için aşağıdan yorum bırakmayı unutmayın.