Uygulamanızı mümkün olduğunca küçük hale getirmek için webpack'i kullanma
Bir uygulamayı optimize ederken yapılacak ilk şeylerden biri uygulamayı mümkün olduğunca küçük hale getirmektir. Bunu webpack ile nasıl yapacağınız aşağıda açıklanmıştır.
Üretim modunu kullanma (yalnızca webpack 4)
Webpack 4, yeni mode
işaretini kullanıma sundu. Webpack'e uygulamayı belirli bir ortam için oluşturduğunuzu belirtmek üzere bu işaretçiyi 'development'
veya 'production'
olarak ayarlayabilirsiniz:
// webpack.config.js
module.exports = {
mode: 'production',
};
Uygulamanızı üretim için oluştururken production
modunu etkinleştirdiğinizden emin olun.
Bu, webpack'in kod sıkıştırma, kitaplıklardaki yalnızca geliştirme amaçlı kodun kaldırılması gibi optimizasyonlar ve daha fazlasını uygulamasını sağlar.
Daha fazla bilgi
Kod sıkıştırmayı etkinleştirme
Küçültme işlemi, fazladan boşlukları kaldırarak, değişken adlarını kısaltmak ve diğer yöntemlerle kodu sıkıştırmak anlamına gelir. Aşağıdaki gibi:
// Original code
function map(array, iteratee) {
let index = -1;
const length = array == null ? 0 : array.length;
const result = new Array(length);
while (++index < length) {
result[index] = iteratee(array[index], index, array);
}
return result;
}
↓
// Minified code
function map(n,r){let t=-1;for(const a=null==n?0:n.length,l=Array(a);++t<a;)l[t]=r(n[t],t,n);return l}
Webpack, kodu küçültmek için iki yöntemi destekler: paket düzeyinde küçültme ve yükleyiciye özel seçenekler. Bu iki araç aynı anda kullanılmalıdır.
Paket düzeyinde küçültme
Paket düzeyinde küçültme, derleme işleminden sonra paketin tamamını sıkıştırır. İşleyiş şekli:
Kodunuzu şu şekilde yazarsınız:
// comments.js import './comments.css'; export function render(data, target) { console.log('Rendered!'); }
Webpack, dosyayı yaklaşık olarak aşağıdaki şekilde derler:
// bundle.js (part of) "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony export (immutable) */ __webpack_exports__["render"] = render; /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__comments_css__ = __webpack_require__(1); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__comments_css_js___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__comments_css__); function render(data, target) { console.log('Rendered!'); }
Küçültücü, şablonu yaklaşık olarak aşağıdaki bölümlere sıkıştırır:
// minified bundle.js (part of) "use strict";function t(e,n){console.log("Rendered!")} Object.defineProperty(n,"__esModule",{value:!0}),n.render=t;var o=r(1);r.n(o)
Webpack 4'te paket düzeyinde küçültme hem üretim modunda hem de üretim modu olmadan otomatik olarak etkinleştirilir. Bu araç, arka planda UglifyJS küçültücüyü kullanır. (Küçültmeyi devre dışı bırakmanız gerekirse geliştirme modunu kullanmanız veya false
'yi optimization.minimize
seçeneğine aktarmanız yeterlidir.)
Web paketi 3'te,doğrudan UglifyJS eklentisini kullanmanız gerekir. Eklenti, webpack ile birlikte gelir. Eklentiyi etkinleştirmek için yapılandırmanın plugins
bölümüne ekleyin:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.UglifyJsPlugin(),
],
};
Yükleyiciye özel seçenekler
Kodu küçültmenin ikinci yolu, yükleyiciye özel seçeneklerdir (yükleyici nedir?). Yükleyici seçenekleriyle, küçültücünün küçültme yapamadığı öğeleri sıkıştırabilirsiniz. Örneğin, bir CSS dosyasını css-loader
ile içe aktardığınızda, dosya bir dizede derlenir:
/* comments.css */
.comment {
color: black;
}
// minified bundle.js (part of)
exports=module.exports=__webpack_require__(1)(),
exports.push([module.i,".comment {\r\n color: black;\r\n}",""]);
Kod sıkıştırıcı, dize olduğu için bu kodu sıkıştıramaz. Dosya içeriğini küçültmek için yükleyiciyi bunu yapacak şekilde yapılandırmamız gerekir:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { minimize: true } },
],
},
],
},
};
Daha fazla bilgi
- UglifyJsplugin belgeleri
- Diğer popüler küçültücüler: Babel Minify, Google Closure Compiler
NODE_ENV=production
değerini belirtin
Kullanıcı arayüzü boyutunu küçültmenin bir diğer yolu da kodunuzdaki NODE_ENV
ortam değişkenini production
değerine ayarlamaktır.
Kitaplıklar, hangi modda (geliştirme veya üretim) çalışmaları gerektiğini algılamak için NODE_ENV
değişkenini okur. Bazı kitaplıklar bu değişkene bağlı olarak farklı davranır. Örneğin, NODE_ENV
değeri production
olarak ayarlanmadığında Vue.js ek kontroller yapar ve uyarılar yazdırır:
// vue/dist/vue.runtime.esm.js
// …
if (process.env.NODE_ENV !== 'production') {
warn('props must be strings when using array syntax.');
}
// …
React de benzer şekilde çalışır. Uyarıları içeren bir geliştirme derlemesi yükler:
// react/index.js
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
// react/cjs/react.development.js
// …
warning$3(
componentClass.getDefaultProps.isReactClassApproved,
'getDefaultProps is only used on classic React.createClass ' +
'definitions. Use a static property named `defaultProps` instead.'
);
// …
Bu tür kontroller ve uyarılar genellikle üretimde gereksizdir ancak kodda kalır ve kitaplık boyutunu artırır. Web paketi 4'te,optimization.nodeEnv: 'production'
seçeneğini ekleyerek bunları kaldırın:
// webpack.config.js (for webpack 4)
module.exports = {
optimization: {
nodeEnv: 'production',
minimize: true,
},
};
Webpack 3'te bunun yerine DefinePlugin
kullanın:
// webpack.config.js (for webpack 3)
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.optimize.UglifyJsPlugin()
]
};
Hem optimization.nodeEnv
seçeneği hem de DefinePlugin
aynı şekilde çalışır. Bu seçenekler, process.env.NODE_ENV
değerinin tüm kullanımlarını belirtilen değerle değiştirir. Yukarıdaki yapılandırmayla:
Webpack,
process.env.NODE_ENV
değerinin tüm örneklerini"production"
ile değiştirir:// vue/dist/vue.runtime.esm.js if (typeof val === 'string') { name = camelize(val); res[name] = { type: null }; } else if (process.env.NODE_ENV !== 'production') { warn('props must be strings when using array syntax.'); }
↓
// vue/dist/vue.runtime.esm.js if (typeof val === 'string') { name = camelize(val); res[name] = { type: null }; } else if ("production" !== 'production') { warn('props must be strings when using array syntax.'); }
Ardından küçültücü, bu tür tüm
if
dallarını kaldırır. Çünkü"production" !== 'production'
her zaman yanlıştır ve eklenti, şu dalların içindeki kodun hiçbir zaman yürütülmeyeceğini anlar:// vue/dist/vue.runtime.esm.js if (typeof val === 'string') { name = camelize(val); res[name] = { type: null }; } else if ("production" !== 'production') { warn('props must be strings when using array syntax.'); }
↓
// vue/dist/vue.runtime.esm.js (without minification) if (typeof val === 'string') { name = camelize(val); res[name] = { type: null }; }
Daha fazla bilgi
- "Ortam değişkenleri" nedir?
- Aşağıdakiler hakkında Webpack dokümanları:
DefinePlugin
,EnvironmentPlugin
ES modüllerini kullanma
Ön uç boyutunu küçültmenin bir diğer yolu da ES modülleri kullanmaktır.
ES modüllerini kullandığınızda webpack, ağaç sallama işlemini yapabilir. Ağda kaldırma, bir paketleyicinin tüm bağımlılık ağacını taraması, hangi bağımlılıkların kullanıldığını kontrol etmesi ve kullanılmayanları kaldırmasıdır. Bu nedenle, ES modülü söz dizimini kullanırsanız webpack, kullanılmayan kodu ortadan kaldırabilir:
Birden fazla dışa aktarma içeren bir dosya yazarsınız ancak uygulama bunlardan yalnızca birini kullanır:
// comments.js export const render = () => { return 'Rendered!'; }; export const commentRestEndpoint = '/rest/comments'; // index.js import { render } from './comments.js'; render();
Webpack,
commentRestEndpoint
öğesinin kullanılmadığını ve pakette ayrı bir dışa aktarma noktası oluşturmadığını anlar:// bundle.js (part that corresponds to comments.js) (function(module, __webpack_exports__, __webpack_require__) { "use strict"; const render = () => { return 'Rendered!'; }; /* harmony export (immutable) */ __webpack_exports__["a"] = render; const commentRestEndpoint = '/rest/comments'; /* unused harmony export commentRestEndpoint */ })
Küçültücü, kullanılmayan değişkeni kaldırır:
// bundle.js (part that corresponds to comments.js) (function(n,e){"use strict";var r=function(){return"Rendered!"};e.b=r})
Bu işlem, ES modülleriyle yazılan kitaplıklarda bile çalışır.
Ancak tam olarak webpack'in yerleşik minify aracını (UglifyJsPlugin
) kullanmanız gerekmez.
Geçersiz kodların kaldırılmasını destekleyen herhangi bir küçültücü (ör. Babel Minify eklentisi veya Google Closure Compiler eklentisi) bu sorunu çözecektir.
Daha fazla bilgi
Ağaç sallama hakkında web paketi dokümanları
Resimleri optimize edin
Görseller, sayfa boyutunun yarısından fazlasını oluşturur. JavaScript kadar kritik olmasalar (ör. oluşturmayı engellemezler), yine de bant genişliğinin büyük bir kısmını tüketirler. webpack'ta bunları optimize etmek için url-loader
, svg-url-loader
ve image-webpack-loader
özelliklerini kullanın.
url-loader
, küçük statik dosyaları uygulamaya satır içi olarak ekler. Yapılandırma olmadan, iletilen bir dosyayı alır, derlenmiş paketin yanına yerleştirir ve bu dosyanın URL'sini döndürür. Ancak limit
seçeneğini belirtirsek bu sınırdan küçük dosyalar Base64 veri URL'si olarak kodlanır ve bu URL döndürülür. Bu işlem, resmi JavaScript kodunda satır içi yapar ve bir HTTP isteği kaydeder:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(jpe?g|png|gif)$/,
loader: 'url-loader',
options: {
// Inline files smaller than 10 kB (10240 bytes)
limit: 10 * 1024,
},
},
],
}
};
// index.js
import imageUrl from './image.png';
// → If image.png is smaller than 10 kB, `imageUrl` will include
// the encoded image: 'data:image/png;base64,iVBORw0KGg…'
// → If image.png is larger than 10 kB, the loader will create a new file,
// and `imageUrl` will include its url: `/2fcd56a1920be.png`
svg-url-loader
, url-loader
gibi çalışır. Tek fark, dosyaları Base64 kodu yerine URL kodlaması ile kodlar. Bu, SVG resimleri için kullanışlıdır. SVG dosyaları düz metin olduğu için bu kodlama daha az yer kaplar.
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
loader: "svg-url-loader",
options: {
limit: 10 * 1024,
noquotes: true
}
}
]
}
};
image-webpack-loader
, üzerinden geçen resimleri sıkıştırır. JPG, PNG, GIF ve SVG resimleri desteklediğinden tüm bu türler için onu kullanacağız.
Bu yükleyici, resimleri uygulamaya yerleştirmediğinden url-loader
ve svg-url-loader
ile birlikte çalışmalıdır. Bu yükleyiciyi her iki kurala da (biri JPG/PNG/GIF resimleri, diğeri SVG resimleri için) kopyalayıp yapıştırmak zorunda kalmamak için enforce: 'pre'
ile ayrı bir kural olarak ekleyeceğiz:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'image-webpack-loader',
// This will apply the loader before the other ones
enforce: 'pre'
}
]
}
};
Yükleyicinin varsayılan ayarları kullanıma hazırdır. Ancak daha fazla yapılandırmak isterseniz eklenti seçeneklerine göz atın. Hangi seçenekleri belirteceğinizi belirlemek için Addy Osmani'nin resim optimizasyonu ile ilgili mükemmel rehberine göz atın.
Daha fazla bilgi
- "base64 kodlaması ne için kullanılır?"
- Addy Osmani'nin resim optimizasyonu rehberi
Bağımlılıkları optimize etme
Ortalama JavaScript boyutunun yarısından fazlası bağımlılıklardan kaynaklanır ve bu boyutun bir kısmı gereksiz olabilir.
Örneğin, Lodash (v4.17.4 itibarıyla) pakete 72 KB sıkıştırılmış kod ekler. Ancak, örneğin yalnızca 20 yöntemini kullanırsanız yaklaşık 65 KB küçültülmüş kod herhangi bir işe yaramaz.
Moment.js de bu tür bir kitaplıktır. 2.19.1 sürümü 223 KB sıkıştırılmış kod kullanıyor. Bu çok büyük bir boyuttur. Bir sayfadaki JavaScript'in ortalama boyutu Ekim 2017'de 452 KB idi. Ancak bu boyutun 170 KB'ı yerelleştirme dosyalarıdır. Moment.js'yi birden fazla dilde kullanmıyorsanız bu dosyalar, paketi bir amaç olmadan şişirir.
Bu bağımlılıkların tümü kolayca optimize edilebilir. Optimizasyon yaklaşımlarını bir GitHub deposunda topladık. Hemen göz atın!
ES modülleri için modül birleştirmeyi etkinleştir (kapsam kaldırma olarak da bilinir)
Bir paket oluştururken webpack, her modülü bir işleve sarmalar:
// index.js
import {render} from './comments.js';
render();
// comments.js
export function render(data, target) {
console.log('Rendered!');
}
↓
// bundle.js (part of)
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
var __WEBPACK_IMPORTED_MODULE_0__comments_js__ = __webpack_require__(1);
Object(__WEBPACK_IMPORTED_MODULE_0__comments_js__["a" /* render */])();
}),
/* 1 */
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_exports__["a"] = render;
function render(data, target) {
console.log('Rendered!');
}
})
Geçmişte, CommonJS/AMD modüllerini birbirinden ayırmak için bu gerekliydi. Ancak bu, her modül için bir boyut ve performans ek yükü getiriyordu.
Webpack 2, CommonJS ve AMD modüllerinin aksine her birini bir işlevle sarmalamadan paket hâline getirilebilen ES modülleri için destek sunar. Webpack 3 ise modül birleştirme özelliğiyle bu tür paketleme işlemlerini mümkün kıldı. Modül birleştirme işleminin işlevi şudur:
// index.js
import {render} from './comments.js';
render();
// comments.js
export function render(data, target) {
console.log('Rendered!');
}
↓
// Unlike the previous snippet, this bundle has only one module
// which includes the code from both files
// bundle.js (part of; compiled with ModuleConcatenationPlugin)
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
// CONCATENATED MODULE: ./comments.js
function render(data, target) {
console.log('Rendered!');
}
// CONCATENATED MODULE: ./index.js
render();
})
Fark görüyor musunuz? Düz pakette, modül 0, modül 1'den render
gerektiriyordu. Modül birleştirme kullanıldığında require
, gerekli işlevle değiştirilir ve modül 1 kaldırılır. Pakette daha az modül ve daha az modül yükü vardır.
Bu davranışı etkinleştirmek için web paketi 4'te optimization.concatenateModules
seçeneğini etkinleştirin:
// webpack.config.js (for webpack 4)
module.exports = {
optimization: {
concatenateModules: true
}
};
Web paketi 3'te,ModuleConcatenationPlugin
kullanın:
// webpack.config.js (for webpack 3)
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
};
Daha fazla bilgi
- ModuleConcatenationEklein için web paketi belgeleri
- "Kapsam kaldırmaya kısa giriş"
- Bu eklentinin ne yaptığına ilişkin ayrıntılı açıklama
Hem webpack hem de webpack olmayan kodunuz varsa externals
değerini kullanın
Bazı kodların webpack ile derlendiği, bazılarının ise derlendiği büyük bir projeniz olabilir. Oynatıcı widget'ının webpack ile oluşturulmuş olabileceği bir video barındırma sitesi gibi, etrafındaki sayfa da aşağıdakiler olmayabilir:
Her iki kod parçası da ortak bağımlılıklara sahipse kodlarını birkaç kez indirmemek için bunları paylaşabilirsiniz. Bu işlem web paketinin externals
seçeneğiyle yapılır. Modülleri değişkenlerle veya diğer harici içe aktarma işlemleriyle değiştirir.
window
bölgesinde bağımlılıklar kullanılabiliyorsa
Webpack dışındaki kodunuz window
içinde değişken olarak kullanılabilen bağımlılıklara dayanıyorsa bağımlılık adlarını değişken adlarıyla takma adlandırın:
// webpack.config.js
module.exports = {
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
};
Bu yapılandırmayla webpack, react
ve react-dom
paketlerini bir araya getirmez. Bunların yerine şuna benzer bir metin yer alır:
// bundle.js (part of)
(function(module, exports) {
// A module that exports `window.React`. Without `externals`,
// this module would include the whole React bundle
module.exports = React;
}),
(function(module, exports) {
// A module that exports `window.ReactDOM`. Without `externals`,
// this module would include the whole ReactDOM bundle
module.exports = ReactDOM;
})
Bağımlılıklar AMD paketleri olarak yükleniyorsa
Web paketi olmayan kodunuz window
içine bağımlılıkları göstermiyorsa işler daha karmaşıktır.
Bununla birlikte, web paketi olmayan kod bu bağımlılıkları AMD paketleri olarak tüketirse aynı kodu iki kez yüklemekten kaçınabilirsiniz.
Bunu yapmak için web paketi kodunu AMD paketi ve takma ad modüllerinden kitaplık URL'lerinde derleyin:
// webpack.config.js
module.exports = {
output: {
libraryTarget: 'amd'
},
externals: {
'react': {
amd: '/libraries/react.min.js'
},
'react-dom': {
amd: '/libraries/react-dom.min.js'
}
}
};
Web paketi, paketi define()
içine sarmalar ve şu URL'lere bağımlı hale getirir:
// bundle.js (beginning)
define(["/libraries/react.min.js", "/libraries/react-dom.min.js"], function () { … });
Web paketi olmayan kod, bağımlılıklarını yüklemek için aynı URL'leri kullanıyorsa bu dosyalar yalnızca bir kez yüklenir. Ek istekler, yükleyici önbelleğini kullanır.
Daha fazla bilgi
- Webpack belgeleri
externals
Özet
- Web paketi 4 kullanıyorsanız üretim modunu etkinleştirme
- Paket düzeyinde kod sıkıştırıcı ve yükleyici seçenekleriyle kodunuzu küçültme
NODE_ENV
yerineproduction
yazarak yalnızca geliştirme amaçlı kodu kaldırın.- Ağaç sallamayı etkinleştirmek için ES modüllerini kullanma
- Resimleri sıkıştırın
- Bağımlılığa özel optimizasyonlar uygulama
- Modül birleştirmeyi etkinleştir
- Sizin için mantıklıysa
externals
özelliğini kullanın.