Uygulamanızı olabildiğince küçük hale getirmek için webpack'i kullanma
Bir uygulamayı optimize ederken ilk yapmanız gereken şey, uygulamayı mümkün olduğunca küçük yapmaktır. Bunu webpack ile nasıl yapacağınız aşağıda açıklanmıştır.
Üretim modunu kullanın (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ı üretime yönelik olarak 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
Kodu, fazla boşlukları kaldırarak, değişken adlarını kısaltarak vb. sıkıştırmanıza kod küçültme denir. 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üçültmenin iki yolunu destekler: paket düzeyinde küçültme ve yükleyiciye özgü seçenekler. Bu iki araç aynı anda kullanılmalıdır.
Paket düzeyinde sadeleştirme
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, bu dosyayı yaklaşık olarak aşağıdaki şekilde derleyecektir:
// 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!'); }
Bir sıkıştırıcı, bu dosyayı yaklaşık olarak aşağıdaki şekilde 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 işlem için UglifyJS minifier kullanılır. (Küçültmeyi devre dışı bırakmanız gerekirse geliştirme modunu kullanmanız veya false
seçeneğini optimization.minimize
seçeneğine iletmeniz yeterlidir.)
Webpack 3'te UglifyJS eklentisini doğrudan kullanmanız gerekir. Eklenti, webpack ile birlikte gelir. 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 özgü seçenekler
Kodu küçültmenin ikinci yolu, yükleyiciye özgü seçeneklerdir (yükleyici nedir?). Yükleyici seçenekleriyle, küçültücünün küçültme yapamadığı öğeleri sıkıştırabilirsiniz. Örneğin, css-loader
içeren bir CSS dosyasını içe aktardığınızda dosya bir dize halinde 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 aşağıdaki ş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 sıkıştırıcılar: 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
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 da 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. Webpack 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 kod küçültücü,
"production" !== 'production'
her zaman yanlış olduğu ve eklenti bu dalların içindeki kodun hiçbir zaman çalışmayacağını anladığı için bu tür tümif
dallarını kaldırır:// 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üllerini 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ıyorsanız webpack kullanılmayan kodu 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ı anlar ve pakette ayrı bir dışa aktarma noktası oluşturmaz:// 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, ES modülleri ile yazılmış kitaplıklarda bile çalışır.
Ancak tam olarak webpack'in yerleşik minify aracını (UglifyJsPlugin
) kullanmanız gerekmez.
Kullanılmayan kodun kaldırılmasını destekleyen herhangi bir kod sıkıştırıcı (ör. Babel Minify eklentisi veya Google Closure Compiler eklentisi) bu işlemi gerçekleştirebilir.
Daha fazla bilgi
Ağaç sallama hakkında Webpack dokümanları
Resimleri optimize edin
Görseller, sayfa boyutunun yarısından fazlasını oluşturuyor. Bunlar JavaScript kadar kritik olmasa da (ör. oluşturmayı engellemezler) 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 koduna satır içi olarak yerleştirir 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
ile aynı şekilde çalışır. Tek fark, dosyaları Base64 yerine URL kodlamasıyla kodlamasıdır. 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ği için bu algoritmayı tüm bu resim türleri için 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 dahil edeceğ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 kılavuzuna 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 gelir 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 yalnızca 20 yöntemini kullanıyorsanız yaklaşık 65 KB sıkıştırılmış kod hiç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ından oluşuyor. Moment.js'yi birden fazla dille kullanmıyorsanız bu dosyalar paketi gereksiz yere şişirir.
Bu bağımlılıkların tümü kolayca optimize edilebilir. Optimizasyon yaklaşımlarını bir GitHub kod deposunda topladık. Buraya göz atın.
ES modülleri için modül birleştirme özelliğini (diğer adıyla kapsam kaldırma) etkinleştirme
Webpack, paket oluştururken her modülü bir işleve sarar:
// 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!');
}
})
Eskiden bu, CommonJS/AMD modüllerini birbirinden ayırmak için gerekliydi. Ancak bu, her modül için boyut ve performans yükü ekledi.
Webpack 2, CommonJS ve AMD modüllerinden farklı olarak her biri bir işlevle sarmalanmadan gruplandırılabilen ES modülleri için destek sunmuştur. Webpack 3, modül birleştirme ile bu tür birleştirme 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? Basit pakette 0. modül, 1. modülden render
istiyordu. Modül birleştirme işleminde, require
yalnızca gerekli işlevle değiştirilir ve 1. modül kaldırılır. Pakette daha az modül ve daha az modül yükü vardır.
Bu davranışı etkinleştirmek için webpack 4'te optimization.concatenateModules
seçeneğini etkinleştirin:
// webpack.config.js (for webpack 4)
module.exports = {
optimization: {
concatenateModules: true
}
};
Webpack 3'te ModuleConcatenationPlugin
öğesini kullanın:
// webpack.config.js (for webpack 3)
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
};
Daha fazla bilgi
- ModuleConcatenationPlugin için Webpack dokümanları
- "Kapsam kaldırmaya kısa giriş"
- Bu eklentinin işlevinin ayrıntılı açıklaması
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 derlenmediği büyük bir projeniz olabilir. Örneğin, oynatıcı widget'ının webpack ile oluşturulabileceği ve çevreleyen sayfanın oluşturulmayabileceği bir video barındırma sitesi:
Her iki kod parçasının da ortak bağımlılıkları varsa kodlarını birden çok kez indirmekten kaçınmak için bunları paylaşabilirsiniz. Bu işlem, webpack'ın externals
seçeneği ile yapılır. Bu seçenek, modülleri değişkenlerle veya diğer harici içe aktarmalarla değiştirir.
Bağımlılıklar window
'te mevcutsa
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. Bunun yerine, şuna benzer bir ifadeyle değiştirilir:
// 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
Webpack dışındaki kodunuz window
'te bağımlılık göstermezse işler daha karmaşık hale gelir.
Ancak webpack olmayan kod bu bağımlılıkları AMD paketleri olarak tüketirse aynı kodu iki kez yüklemekten kaçınabilirsiniz.
Bunun için webpack kodunu AMD paketi olarak derleyin ve modülleri kitaplık URL'lerine takma adlandırın:
// webpack.config.js
module.exports = {
output: {
libraryTarget: 'amd'
},
externals: {
'react': {
amd: '/libraries/react.min.js'
},
'react-dom': {
amd: '/libraries/react-dom.min.js'
}
}
};
Webpack, paketi define()
içine sarar ve şu URL'lere bağımlı hale getirir:
// bundle.js (beginning)
define(["/libraries/react.min.js", "/libraries/react-dom.min.js"], function () { … });
Webpack dışındaki kod, bağımlılıkları 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
- Webpack 4 kullanıyorsanız üretim modunu etkinleştirin
- 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ırma
- Bağımlılığa özel optimizasyonlar uygulama
- Modül birleştirmeyi etkinleştirme
externals
'ü kullanın