Web Paketi Bağımlılıklarını Yönetme Kılavuzu
Yayınlanan: 2022-03-11Modülerleştirme kavramı, çoğu modern programlama dilinin doğal bir parçasıdır. Ancak JavaScript, ECMAScript ES6'nın en son sürümü gelene kadar modülerleştirmeye yönelik herhangi bir resmi yaklaşımdan yoksundu.
Günümüzün en popüler JavaScript çerçevelerinden biri olan Node.js'de modül paketleyiciler, NPM modüllerinin web tarayıcılarına yüklenmesine izin verir ve bileşen odaklı kitaplıklar (React gibi) JavaScript kodunun modülerleştirilmesini teşvik eder ve kolaylaştırır.
Webpack, JavaScript kodunun yanı sıra stil sayfaları, resimler ve yazı tipleri gibi tüm statik varlıkları paketlenmiş bir dosyada işleyen mevcut modül paketleyicilerinden biridir. İşleme, derleme, birleştirme, küçültme ve sıkıştırma gibi kod bağımlılıklarını yönetmek ve optimize etmek için gerekli tüm görevleri içerebilir.
Ancak, Webpack'i ve bağımlılıklarını yapılandırmak stresli olabilir ve özellikle yeni başlayanlar için her zaman basit bir süreç değildir.
Bu blog gönderisi, Webpack'in farklı senaryolar için nasıl yapılandırılacağına dair örneklerle birlikte yönergeler sağlar ve Webpack kullanılarak proje bağımlılıklarının paketlenmesiyle ilgili en yaygın tuzaklara işaret eder.
Bu blog gönderisinin ilk bölümü, bir projedeki bağımlılıkların tanımının nasıl basitleştirileceğini açıklar. Daha sonra, çoklu ve tek sayfalı uygulamaların kod bölme konfigürasyonunu tartışıyor ve gösteriyoruz. Son olarak, projemize üçüncü taraf kitaplıkları dahil etmek istiyorsak Webpack'i nasıl yapılandıracağımızı tartışıyoruz.
Takma Adları ve Göreli Yolları Yapılandırma
Göreceli yollar doğrudan bağımlılıklarla ilgili değildir, ancak bunları bağımlılıkları tanımlarken kullanırız. Bir proje dosyası yapısı karmaşıksa, ilgili modül yollarını çözmek zor olabilir. Web paketi yapılandırmasının en temel faydalarından biri, bir projedeki göreli yolların tanımını basitleştirmeye yardımcı olmasıdır.
Diyelim ki aşağıdaki proje yapısına sahibiz:
- Project - node_modules - bower_modules - src - script - components - Modal.js - Navigation.js - containers - Home.js - Admin.js
İhtiyacımız olan dosyalara göreli yollarla bağımlılıklara başvurabiliriz ve bileşenleri kaynak kodumuzdaki kapsayıcılara aktarmak istiyorsak, aşağıdaki gibi görünür:
Home.js
Import Modal from '../components/Modal'; Import Navigation from '../components/Navigation';
Modal.js
import {datepicker} from '../../../../bower_modules/datepicker/dist/js/datepicker';
Bir betiği veya modülü içe aktarmak istediğimiz her zaman, geçerli dizinin konumunu bilmemiz ve içe aktarmak istediğimiz şeye göreli yolu bulmamız gerekir. Yuvalanmış bir dosya yapısına sahip büyük bir projemiz varsa veya karmaşık bir proje yapısının bazı bölümlerini yeniden düzenlemek istiyorsak, bu sorunun karmaşıklıkta nasıl tırmanabileceğini hayal edebiliriz.
Bu sorunu resolve.alias
seçeneği ile kolayca halledebiliriz. Takma adlar olarak adlandırılanları - konumuyla birlikte bir dizinin veya modülün adını bildirebiliriz ve projenin kaynak kodundaki göreli yollara güvenmiyoruz.
webpack.config.js
resolve: { alias: { 'node_modules': path.join(__dirname, 'node_modules'), 'bower_modules': path.join(__dirname, 'bower_modules'), } }
Modal.js
dosyasında, artık veri seçiciyi çok daha basit bir şekilde içe aktarabiliriz:
import {datepicker} from 'bower_modules/datepicker/dist/js/datepicker';
Kod Bölme
Son pakete bir komut dosyası eklememiz veya son paketi bölmemiz gereken veya talep üzerine ayrı paketler yüklemek istediğimiz senaryolarımız olabilir. Bu senaryolar için projemizi ve Web paketi yapılandırmamızı ayarlamak kolay olmayabilir.
Web paketi yapılandırmasında, Entry
seçeneği Web paketine son paket için başlangıç noktasının nerede olduğunu söyler. Bir giriş noktasının üç farklı veri türü olabilir: Dize, Dizi veya Nesne.
Tek bir başlangıç noktamız varsa, bu formatlardan herhangi birini kullanabilir ve aynı sonucu alabiliriz.
Birden fazla dosya eklemek istiyorsak ve bunlar birbirine bağlı değilse, bir Dizi biçimi kullanabiliriz. Örneğin, analytics.js
bundle.js
sonuna ekleyebiliriz:
webpack.config.js
module.exports = { // creates a bundle out of index.js and then append analytics.js entry: ['./src/script/index.jsx', './src/script/analytics.js'], output: { path: './build', filename: bundle.js ' } };
Birden Fazla Giriş Noktasını Yönetme
admin.html
index.html
birden çok HTML dosyası içeren çok sayfalı bir uygulamamız olduğunu varsayalım. Giriş noktasını bir Nesne türü olarak kullanarak birden çok demet oluşturabiliriz. Aşağıdaki yapılandırma iki JavaScript paketi oluşturur:
webpack.config.js
module.exports = { entry: { index: './src/script/index.jsx', admin: './src/script/admin.jsx' }, output: { path: './build', filename: '[name].js' // template based on keys in entry above (index.js & admin.js) } };
index.html
<script src=”build/index.js”></script>
admin.html
<script src=”build/admin.js”></script>
Her iki JavaScript paketi de ortak kitaplıkları ve bileşenleri paylaşabilir. Bunun için, birden çok giriş parçasında oluşan modülleri bulan ve birden çok sayfa arasında önbelleğe alınabilen paylaşılan bir paket oluşturan CommonsChunkPlugin
kullanabiliriz.
webpack.config.js
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js'); module.exports = { entry: { index: './src/script/index.jsx', admin: './src/script/admin.jsx' }, output: { path: './build', filename: '[name].js' // template based on keys in entry above (index.js & admin.js) }, plugins: [commonsPlugin] };
Şimdi, paketlenmiş komut dosyalarından önce <script src="build/common.js"></script>
eklemeyi unutmamalıyız.
Tembel Yüklemeyi Etkinleştirme
Web paketi, statik varlıkları daha küçük parçalara bölebilir ve bu yaklaşım standart birleştirmeden daha esnektir. Büyük bir tek sayfa uygulamamız (SPA) varsa, tek bir pakette basit birleştirme iyi bir yaklaşım değildir çünkü büyük bir paketin yüklenmesi yavaş olabilir ve kullanıcılar genellikle her görünümde tüm bağımlılıklara ihtiyaç duymaz.
Bir uygulamayı birden çok pakete nasıl böleceğinizi, ortak bağımlılıkları nasıl birleştireceğinizi ve tarayıcı önbelleğe alma davranışından nasıl yararlanacağınızı daha önce açıkladık. Bu yaklaşım, çok sayfalı uygulamalar için çok iyi çalışır, ancak tek sayfalı uygulamalar için geçerli değildir.
SPA için yalnızca mevcut görünümü oluşturmak için gerekli olan statik varlıkları sağlamalıyız. SPA mimarisindeki istemci tarafı yönlendirici, kod bölmeyi işlemek için mükemmel bir yerdir. Kullanıcı bir rotaya girdiğinde, ortaya çıkan görünüm için yalnızca gerekli olan bağımlılıkları yükleyebiliriz. Alternatif olarak, kullanıcı bir sayfayı aşağı kaydırırken bağımlılıkları yükleyebiliriz.
Bunun için require.ensure
statik olarak algılayabildiği request.ensure veya System.import
fonksiyonlarını kullanabiliriz. Web paketi, bu bölünme noktasına dayalı olarak ayrı bir paket oluşturabilir ve talep üzerine çağırabilir.
Bu örnekte iki React kapsayıcımız var; bir yönetici görünümü ve bir pano görünümü.
admin.jsx
import React, {Component} from 'react'; export default class Admin extends Component { render() { return <div > Admin < /div>; } }
dashboard.jsx
import React, {Component} from 'react'; export default class Dashboard extends Component { render() { return <div > Dashboard < /div>; } }
Kullanıcı /dashboard
veya /admin
URL'sini girerse, yalnızca ilgili gerekli JavaScript paketi yüklenir. Aşağıda, istemci tarafı yönlendiricili ve yönlendiricisiz örnekleri görebiliriz.
index.jsx
if (window.location.pathname === '/dashboard') { require.ensure([], function() { require('./containers/dashboard').default; }); } else if (window.location.pathname === '/admin') { require.ensure([], function() { require('./containers/admin').default; }); }
index.jsx
ReactDOM.render( <Router> <Route path="/" component={props => <div>{props.children}</div>}> <IndexRoute component={Home} /> <Route path="dashboard" getComponent={(nextState, cb) => { require.ensure([], function (require) { cb(null, require('./containers/dashboard').default) }, "dashboard")}} /> <Route path="admin" getComponent={(nextState, cb) => { require.ensure([], function (require) { cb(null, require('./containers/admin').default) }, "admin")}} /> </Route> </Router> , document.getElementById('content') );
Stilleri Ayrı Paketlere Ayıklama
Web paketinde, style-loader
ve css-loader
gibi yükleyiciler, stil sayfalarını önceden işler ve bunları çıktı JavaScript paketine gömer, ancak bazı durumlarda, stilsiz içeriğin Flash'ına (FOUC) neden olabilirler.

Tüm stillerin son JavaScript paketine gömülmek yerine ayrı CSS paketlerinde oluşturulmasına izin veren ExtractTextWebpackPlugin ile ExtractTextWebpackPlugin
kaçınabiliriz.
webpack.config.js
var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { module: { loaders: [{ test: /\.css/, loader: ExtractTextPlugin.extract('style', 'css')' }], }, plugins: [ // output extracted CSS to a file new ExtractTextPlugin('[name].[chunkhash].css') ] }
Üçüncü Taraf Kitaplıkları ve Eklentileri Kullanma
Çoğu zaman, aynı bileşenleri sıfırdan geliştirmek için zaman harcamak istemediğimiz için üçüncü taraf kitaplıkları, çeşitli eklentiler veya ek komut dosyaları kullanmamız gerekir. Aktif olarak bakımı yapılmayan, JavaScript modüllerini anlamayan ve önceden tanımlanmış adlar altında küresel olarak bağımlılıkların varlığını varsayan birçok eski kitaplık ve eklenti mevcuttur.
Aşağıda, son paketi oluşturabilmek için Webpack'in nasıl düzgün bir şekilde yapılandırılacağına dair bir açıklama ile birlikte jQuery eklentileriyle ilgili bazı örnekler verilmiştir.
ProvidePlugin
Çoğu üçüncü taraf eklentisi, belirli küresel bağımlılıkların varlığına güvenir. jQuery durumunda, eklentiler tanımlanan $
veya jQuery
değişkenine dayanır ve jQuery eklentilerini $('div.content').pluginFunc()
çağırarak kullanabiliriz.
Webpack eklentisi ProvidePlugin
, global $
tanımlayıcısıyla her karşılaştığında var $ = require("jquery")
öğesinin başına eklemek için kullanabiliriz.
webpack.config.js
webpack.ProvidePlugin({ '$': 'jquery', })
Web paketi kodu işlerken, $
varlığını arar ve require
işlevi tarafından belirtilen modülü içe aktarmadan genel bağımlılıklara bir başvuru sağlar.
İthalat-yükleyici
Bazı jQuery eklentileri, global ad alanında $
olduğunu varsayar veya this
window
nesnesi olmasına güvenir. Bu amaçla, global değişkenleri modüllere enjekte eden imports-loader
loader'ı kullanabiliriz.
example.js
$('div.content').pluginFunc();
Ardından, imports-loader
yapılandırarak $
değişkenini modüle enjekte edebiliriz:
require("imports?$=jquery!./example.js");
Bu basitçe var $ = require("jquery");
öğesinin başına gelir. example.js
.
İkinci kullanım durumunda:
webpack.config.js
module: { loaders: [{ test: /jquery-plugin/, loader: 'imports?jQuery=jquery,$=jquery,this=>window' }] }
=>
sembolünü kullanarak (ES6 Ok işlevleriyle karıştırılmamalıdır), isteğe bağlı değişkenler ayarlayabiliriz. Son değer, global değişkeni this
öğesini window
nesnesine işaret edecek şekilde yeniden tanımlar. Dosyanın tüm içeriğini (function () { ... }).call(window);
ve this
işlevi window
ile argüman olarak çağırmak.
Ayrıca CommonJS veya AMD modül biçimini kullanan kitaplıklara da ihtiyaç duyabiliriz:
// CommonJS var $ = require("jquery"); // jquery is available // AMD define(['jquery'], function($) { // jquery is available });
Bazı kitaplıklar ve modüller farklı modül biçimlerini destekleyebilir.
Sonraki örnekte, AMD ve CommonJS modül biçimini kullanan ve jQuery bağımlılığı olan bir jQuery eklentimiz var:
jquery-plugin.js
(function(factory) { if (typeof define === 'function' && define.amd) { // AMD format is used define(['jquery'], factory); } else if (typeof exports === 'object') { // CommonJS format is used module.exports = factory(require('jquery')); } else { // Neither AMD nor CommonJS used. Use global variables. } });
webpack.config.js
module: { loaders: [{ test: /jquery-plugin/, loader: "imports?define=>false,exports=>false" }] }
Belirli bir kitaplık için kullanmak istediğimiz modül biçimini seçebiliriz. define
öğesini false
olarak bildirirsek, Webpack modülü AMD modül biçiminde ayrıştırmaz ve değişken exports
equal false
olarak bildirirsek, Webpack modülü CommonJS modül biçiminde ayrıştırmaz.
Açık yükleyici
Bir modülü global bağlama maruz bırakmamız gerekirse, expose-loader
kullanabiliriz. Bu, örneğin, Web paketi yapılandırmasının parçası olmayan ve global ad alanındaki simgeye dayanan harici komut dosyalarımız varsa veya tarayıcı konsolundaki bir simgeye erişmesi gereken tarayıcı eklentileri kullanıyorsak yararlı olabilir.
webpack.config.js
module: { loaders: [ test: require.resolve('jquery'), loader: 'expose-loader?jQuery!expose-loader?$' ] }
jQuery kitaplığı artık web sayfasındaki diğer komut dosyaları için global ad alanında mevcuttur.
window.$ window.jQuery
Dış Bağımlılıkları Yapılandırma
Harici olarak barındırılan komut dosyalarından modülleri dahil etmek istiyorsak, bunları yapılandırmada tanımlamamız gerekir. Aksi takdirde, Webpack son paketi oluşturamaz.
Webpack konfigürasyonundaki externals
seçeneğini kullanarak harici scriptleri konfigüre edebiliriz. Örneğin, bir CDN'den ayrı bir <script>
etiketi aracılığıyla bir kitaplık kullanabiliriz, ancak yine de bunu projemizde bir modül bağımlılığı olarak açıkça beyan ederiz.
webpack.config.js
externals: { react: 'React', 'react-dom': 'ReactDOM' }
Bir Kitaplığın Çoklu Örneklerini Destekleme
Üçüncü taraf kitaplıkları ve bağımlılıkları yönetmek için ön uç geliştirmede NPM paket yöneticisini kullanmak harikadır. Ancak bazen aynı kitaplığın farklı sürümleri olan birden çok örneğine sahip olabiliriz ve bunlar tek bir ortamda birlikte iyi oynamazlar.
Bu, örneğin, NPM'den React'i yükleyebileceğimiz React kitaplığı ile olabilir ve daha sonra bazı ek paket veya eklentilerle farklı bir React sürümü kullanılabilir hale gelebilir. Proje yapımız aşağıdaki gibi görünebilir:
project | |-- node_modules | |-- react |-- react-plugin | |--node_modules | |--react
Reaksiyon react-plugin
gelen bileşenler, projedeki diğer bileşenlerden farklı bir React örneğine sahiptir. Şimdi iki ayrı React kopyamız var ve bunlar farklı versiyonlar olabilir. Uygulamamızda bu senaryo global değişken DOM'umuzu bozabilir ve web konsol günlüğünde hata mesajlarını görebiliriz. Bu sorunun çözümü, tüm proje boyunca aynı React sürümüne sahip olmaktır. Webpack takma adlarıyla çözebiliriz.
webpack.config.js
module.exports = { resolve: { alias: { 'react': path.join(__dirname, './node_modules/react'), 'react/addons': path.join(__dirname, '/node_modules/react/addons'), } } }
react-plugin
React gerektirmeye çalıştığında, projenin node_modules
içindeki sürümü kullanır. React'in hangi sürümünü kullandığımızı öğrenmek istiyorsak kaynak koduna console.log(React.version)
ekleyebiliriz.
Web Paketi Yapılandırmasına Değil Geliştirmeye Odaklanın
Bu gönderi, Webpack'in gücünün ve kullanımının yüzeyini çiziyor.
JavaScript paketlemesini optimize etmenize ve düzenlemenize yardımcı olacak başka birçok Web paketi yükleyicisi ve eklentisi vardır.
Yeni başlayan biri olsanız bile, bu kılavuz size Webpack'i kullanmaya başlamanız için sağlam bir temel sağlar ve bu da paket yapılandırmasından daha az geliştirmeye odaklanmanızı sağlar.