Webpack adından da anlaşılabileceği için üzere paketleme işini üstlenir.
Proje içerisinde kullandığımız JavaScript, HTML, CSS, Image, Font gibi dosyaları istediğimiz formata dönüştürmemizi sağlar.
Asıl görevi, projemizin ihtiyaç duyduğu tüm modüllerin bağımlılıklarını arka planda çözerek, biz tek bir bundle (demet) vermesidir.
Webpack, bir proje çalıştırıldığı anda, projenin ihtiyaç duyabileceği her modül tipini alan bir bağımlılık grafiğini oluşturur. Bu doğrultuda webpack bir çıktı paketi oluşturur.
Yukarıdaki resimde bir App.js var ve buna bağlı olan diğer bağımlılıklar mevcut.
Webpack bizim için bu bağımlılık grafiğini oluşturur ve paketler. Aslında projemize dahil etmediğimiz bir dosya bağımlılık oluşturmadığı için bu bağımlılık grafiğine dahil edilmeyecek ve kullanılmayacaktır.
JavaScript dosyalarının artması beraberinde web sitelerinin yavaş açılmasına neden olabilir.
Webpack bizim için JavaScript dosyalarını birleştirir. Bu birleştirme işlemlerini yaparken yukarıda bahsettiğimiz bağımlılık grafiğini göz önünde bulundurarak bundle işlemini gerçekleştirir.
Webpack için geliştirilen loaders ve plugins kullanılarak SASS derlemesi, TypeScript derlemesi vb. ön işleme işlemleri de yapılır.
React, Vue gibi yapılarda bu webpack hazır olarak gelmekte. Bir konfigürasyon yapmak gerekmemektedir.
Eğer bu konfigürasyon işini React tarafına bırakmak istemiyor bu konfigürasyon dosyasını biz düzenlemek istiyorsak eject komutunu kullanabiliriz.
Oluşturduğumuz bir React uygulamasında eject komutunu kullanarak webpack konfigürasyonunu kendimiz yapabiliriz.
npm run eject
Webpack özelliklerini temel olarak kullanmak istiyorsak konfigürasyon yapmadan ön tanımlı ayarlar ile webpack kullanılabilir.
npm init -y
komutu ile package.json dosyasını oluşturalım.
-y komutunu kullanarak npm init içerisindeki bütün sorulara evet cevabını vermiş oluyoruz.
Webpack kurulumu ve webpack komutunun komut yorumlayıcısının çalışması için öncelikle webpack ve webpack-cli modüllerini indiriyoruz.
npm i -D webpack webpack-cli
Paketleri indirdikten sonra package.json dosyamızı açalım ve script bölümüne bir script yazalım.
Build türlerimizi package.json dosyası içerisinde bulunan scripts altında belirtmiş olduk.
Bundan sonra npm run build-dev komutunu kullanıdığımızda webpack bizim için development için bir bundle işlemi yapacak.
Webpack default olarak “src” adı verilen dizindeki JavaScript dosyalarını alır ve “dist” adı verilen bir klasöre tek bir JavaScript dosyası olarak bundle işlemini tamamlar.
“src” dizini altına index.js dosyası oluşturalım ve içerisine loglamak için bir console.log ifadesi yazalım.
npm run build-dev
Komutunu kullanarak development ortamı için bir bundle almış olduk. Bu işlemden sonra dist atında bir klasör oluştuğunu ve içerisinde main.js olduğunu gözlemeyebiliriz.
main.js içerisi alınan bundle türüne göre değişiklik gösterir. Development bundle işlemleri daha çok yer kaplar fakat daha hızlı bundle alınır.
Production bundle işlemlerinde ise dosya boyutu düşük fakat bundle işlemi görece daha uzun sürmektedir.
Yukarıda bahsettiğimiz adımları webpack bizim için tamamlamıştı. Fakat biz daha detaylı bir konfigürasyon hazırlamak istiyor olabiliriz. Bu durumda bir konfigürasyon dosyası oluşturmamız gerekmekte.
“webpack.config.js” adında yeni bir dosya oluşturuyoruz. Bu aşamadan sonra artık ayarları kendimizin yapacağımızı belirtmekteyiz.
Konfig dosyamızı doldurmadan önce bazı konseptlere hakim olmakta fayda var.
Entry: Webpack bağımlılık grafiği oluşturabilmesi için bir başlangıç noktasına ihtiyaç duyar. Webpack bu başlangıç dosyasından başlar ve tüm modülleri gezerek bağımlılıkları yönetmeye başlar.
module.export = { entry: './src/index.js' }
şeklinde giriş dizini belirtilebilir.
Ayrıca istenirse birden fazla giriş dizini belirtilebilir:
entry: { detaylar: "./src/detaylar.js", vendor: "./src/vendor", }
Output: Webpack işlemlerini tamamladıktan sonra bundle dosyasını hangi klasöre yazacağını belirttiğimiz kısımdır. Bu klasör adı genellikle “dist” veya “build” olur.
const path = require('path'); module.exports = { entry: { detaylar: "./src/detaylar.js", vendor: "./src/vendor", }, output: { filename: '[name].js', path: path.resolve(__dirname, "dist"), } }
path istenilirse, path: __dirname + ‘/dist’ şeklinde de oluşturulabilir.
Loaders: Webpack ilk etapta sadece JavaScript ve JSON dosyalarını anlayabilir ve işleyebilir. Webpack’e diğer dosya tiplerini işleme yeteneğini kazandırmak için loader’ları kullanırız.
Örnek olması adına projeye bir sass dosyası ekleyelim.
Kaynak kodlara yazının altından paylaşıyor olacağım.
npm i -D sass style-loader css-loader sass-loader
Gerekli loaderları indirdiğimize göre webpack.config.js dosyasında gerekli konfigürasyonları ekleyebiliriz.
module içerisinde bir regex ifadesi yazıyoruz. Böylece bu tarz dosyaları yakalayıp aşağıdaki loaderları kullanması gerektiğini söylüyoruz.
module: { rules: [ { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'], }, ] }
Gerçekten scss dosyasının etki ettiğini gözlemek için “dist” klasörü altında bir index.html dosyası oluşturalım.
Bundle işlemi yaptığımız javaScript dosyasının içerisine scss dosyasını import ediyoruz.
Vscode içerisinde bulunan live server eklentisi ile html dosyasının nasıl göründüğüne bakıyoruz.
Görüldüğü üzere scss dosyamız çalışmış. Bundle işlemi yaptığımız dosyamızın içerisi ise küçültülmüş bir biçimde bizleri karşılıyor.
Plugins: Loaderslar dışında bir de plugins adını verdiğimiz ayrı bir yapımız var. Loader’ların yetmediği bazı işlemleri plugin’ler yardımıyla yaparız. Plugin’ler asset management, bundle minimization ve optimization gibi birçok görevi yerine getirir.
Daha önce yukarıda dist klasörü içerisine bir index.html dosyası eklemiştik ve bunu liveserver ile açmıştık. Artık pluginsler ile tanıştığımıza göre bu işi bizim için yapan bir plugin ile öğrendiklerimizi pekiştirilebiliriz.
npm i -D html-webpack-plugin
Bu plugin bizler için src dizinindeki index.html dosyasını alır ve istediğimiz moda göre bundle işlemini gerçekleştirip dist dosyasının içerisine yerleştirir.
Güncel webpack.config.js
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'main.js', }, module: { rules: [ { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'], }, ] }, plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })], }
Öncelikle TypeScript dosyaları yakalamak için typescript ve ts-loader indirmeliyiz.
npm install -D typescript ts-loader
webpack.config.js içerisinde yeni bir kural ekliyoruz. (rules objesinin içerisine)
{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, },
Ayrıca bir tsconfig.json dosyası oluşturmamız gerekiyor.
Örnek tsconfig.json dosyası aşağıdaki gibidir:
{ "compilerOptions": { "outDir": "./dist/", "noImplicitAny": true, "module": "es6", "target": "es5", "jsx": "react", "allowJs": true, "moduleResolution": "node" } }
Örnek bir TypeScript dosyası oluşturalım:
interface User { name: string; id: number; } class UserAccount { name: string; id: number; constructor(name: string, id: number) { this.name = name; this.id = id; } } const user: User = new UserAccount("Utku Kemal", 1001); console.log(user);
Daha sonra bu ts dosyamızı index.js dosyasında import edelim ki yukarıda bahsettiğimiz webpack bağımlılık grafiğine eklesin.
Bu şekilde ts dosyalarını da projelerimiz içerisinde kullanabiliyoruz.
Webpack içerisinde bulunan pluginler sayesinde çok özel amaçlara hizmet edebilir.
Örneğin javaScript kodlarımızı yazarken içeride test amacıyla yazdığımız “console.log” ifadelerini unutmuş olabiliriz. İstenilirse webpack ile bu sorunun üstesinden gelebiliriz.
Fakat burada ayrı bir sorun karşımıza çıkıyor. Geliştirme esnasında bu mesajlar bizler için kıymetli. Bu nedenle mesajlar sadece production modunda kaldırılmalı.
Öncelikle bütün console.log ifadeleri kaldıralım sonrasında sadece bu işlemi production modu için tekrar konfigüre edelim.
Bu işlem için öncelikle “uglifyjs-webpack-plugin” isimli plugini indirmemiz gerekli.
npm i -D uglifyjs-webpack-plugin
webpack.config.js içerisine bu şekilde ekliyoruz.
UglifyJsPlugin yukarıda const ile tanımlamamız gerekiyor. Bu nedenle en üst kısma şunu ekliyoruz:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
optimization: { minimizer: [ new UglifyJsPlugin({ uglifyOptions: { compress: { drop_console: true, } } }) ] }
Console.log ifadesini sadece development ortamında kaldırmak için:
optimization: { minimizer: [ new UglifyJsPlugin({ uglifyOptions: { compress: { pure_funcs: ['console.log'], }, mangle: { reserved: ['console.log'] } } }) ] }
Eğer bundle işleminden sonra bir cache problemi yaşıyorsanız,
output: { filename: '[name][contenthash].js', path: __dirname + '/dist', clean:true },
şeklinde her bundle işleminden sonra otomatik olarak bir hash değeri eklenebilir.
clean: Eğer bu komutu eklemezsek her bundle işleminden sonra burada yeni bir dosya oluşacaktır.
Bundle aldığımızda html dosyası şu şekilde gözükecektir:
Buraya kadar ne yaptığımızı kısaca özetleyelim.
“src” klasörü altında kodlarımızı yazdık. javaScript, typeScrpipt ve scss dosyalarımızı import ettik daha sonra bundle işlemi yapıp “LiveServer” aracılığı ile bundle dosyamızın nasıl göründüğünü inceledik.
LiveServer yerine Webpack Dev Server ile canlı olarak çalışmalarımızı gözlemlememiz mümkün.
Webpack Dev Server Eklenmesi
npm i -D webpack-dev-server
Bu işlemi yaptıktan sonra package.json dosyamızda biraz düzenleme yapmamız gerekiyor.
Mevcut package.json dosyamızın scripts kısmı:
Çalışmalarımızı anlık olarak görmek için package.json dosyamıza yeni bir scripts eklememiz gerekiyor.
"dev": "webpack serve --mode development"
npm run dev
bu komut ile dev server 3000 portunda çalışır ve çalışmalarımızı bize anlık olarak gösterir.
Bu şekilde default ayarlar ile kullanabiliriz. Veya daha fazla ayar istiyorsak,
devServer:{ static: { directory: path.resolve(__dirname, 'dist') }, port: 3001, open: true, hot: true, compress: true, },
şeklinde konfigürasyonu tamamlayabiliriz.
open: npm run dev komutu çalıştırıldığında otomatik olarak tarayıcıda çalışmamızı gösterir.
hot: Hot Reload anlamına gelir.
compress: Sıkıştırma işlemi yapar.
Babel’in Eklenmesi
Babel, ECMAScript 2015+ kodunu mevcut ve eski tarayıcılarda veya ortamlarda geriye dönük olarak uyumlu bir JavaScript sürümüne dönüştürmek için kullanılan bir araçtır.
Yeni özellikler standartlaştırıldığında, tarayıcılar yavaş yavaş yeni özellikleri benimsemeye başlar. Bu yüzden Babel kullanmak bizler için bir nevi zorunluluk.
npm i -D babel-loader @babel/core @babel/preset-env
Daha sonra babelin gördüğü her javaScript dosyası için çalışması için package.json dosyasında yeni bir kural eklememiz gerekiyor.
{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } },
Resim Ekleme İşlemi
src dizini altına img adında yeni bir klasör oluşturuyoruz. Buraya bir svg dosyası ekleyelim.
Eklediğimiz bu svg dosyasını index.js içerisine import edelim.
package.json içerisine yeni bir kural ekleyelim.
{ test: /\.(png|svg|jpeg|gif)$/i, type: 'asset/resource', }
Bu işlemlerden sonra tekrar build aldığımızda dist dizini altında svg dosyamız farklı bir isimle oluşacak. Bunu önlemek için output objesi içerisinde şu işlemi yapmamız gerekiyor:
assetModuleFilename: '[name][ext]'
İhtiyaca göre loader ve pluginleri kullanarak çeşitli şeyler yapmak mümkün.
Konfigürasyon işlemlerini tamamladıktan sonra örnek bir proje olması adına “şaka şelalesi” adını verdiğim basit bir uygulama ekledim.
Şaka uygulamasının kodları bu kullanıcıdan alınmıştır.
Makaledeki kaynak kodlar için tıklayınız.
https://github.com/Utkukemalcifci/webpack-config-calismasi
BONUS
Webpack’e rakip olan Vite’den de kısaca bahsetmek isterim.
Hatırlarsanız yukarıda webpack için bir bağımlılık grafiğinden bahsetmiştim. Vite bu bağımlılık grafiklerini sadece ilgili sayfalar için render edildiğinde oluşturuyor. Örneğin kullanıcı Login sayfasına giderse onun altında bulunan bağımlılıkları bizim için paketliyor.