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üçültmektir. Bunu webpack ile nasıl yapacağınız burada açıklanmaktadır.
Üretim modunu kullanma (yalnızca web paketi 4)
Webpack 4, yeni mode
bayrağını kullanıma sundu. Web paketini, uygulamayı belirli bir ortam için derlediğinize dair ipucu vermek amacıyla bu işareti 'development'
veya 'production'
olarak ayarlayabilirsiniz:
// webpack.config.js
module.exports = {
mode: 'production',
};
Uygulamanızı üretim için derlerken production
modunu etkinleştirdiğinizden emin olun.
Bu; küçültme, kitaplıklardaki yalnızca geliştirme amaçlı kodların kaldırılması gibi vb. optimizasyonlar web paketinin uygulamasını sağlar.
Daha fazla bilgi
Küçültmeyi etkinleştir
Fazladan boşlukları kaldırarak, değişken adlarını kısaltarak ve benzer şekillerde kodu sıkıştırarak küçültme yapabilirsiniz. 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. Bunlar aynı anda kullanılmalıdır.
Paket düzeyinde küçültme
Paket düzeyinde küçültme, derlemeden sonra tüm paketi sıkıştırır. İşleyiş şekli:
Şuna benzer bir kod yazarsınız:
// comments.js import './comments.css'; export function render(data, target) { console.log('Rendered!'); }
Webpack, bunu yaklaşık olarak aşağıdaki gibi 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!'); }
Bir küçültücü, onu yaklaşık olarak şu ş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)
Web paketi 4'te paket düzeyinde küçültme işlemi,hem üretim modunda hem de bağımsız olarak otomatik olarak etkinleştirilir. Gelişmiş olarak UglifyJS küçültücüyü kullanır. (Kısaltmayı devre dışı bırakmanız gerekirse geliştirme modunu kullanmanız veya false
öğesini optimization.minimize
seçeneğine iletmeniz yeterlidir.)
webpack 3'te doğrudan UglifyJS eklentisini kullanmanız gerekir. Eklenti, web paketi 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çenekleri sayesinde küçültücünün küçültemeyeceği
öğeleri sıkıştırabilirsiniz. Örneğin, css-loader
ile bir CSS dosyasını 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}",""]);
Bu kod bir dize olduğu için küçültücü tarafından sıkıştırılamaz. 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 Derleyici
NODE_ENV=production
değerini belirtin
Kullanıcı arabirimi boyutunu azaltmanın bir başka yolu da, kodunuzdaki NODE_ENV
ortam değişkenini production
değerine ayarlamaktır.
Kitaplıklar, geliştirmede mi yoksa üretim modunda mı çalışmaları gerektiğini belirlemek için NODE_ENV
değişkenini okur. Bazı kitaplıklar bu değişkene göre farklı davranır. Örneğin, NODE_ENV
production
olarak ayarlanmadığında Vue.js ek kontroller ve baskı uyarıları yapar:
// vue/dist/vue.runtime.esm.js
// …
if (process.env.NODE_ENV !== 'production') {
warn('props must be strings when using array syntax.');
}
// …
Tepki vermek 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 üretimde genellikle 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. Tüm process.env.NODE_ENV
tekrarlarını belirtilen değerle değiştirirler. Yukarıdaki yapılandırma ile:
Web paketi, tüm
process.env.NODE_ENV
oluşumlarını"production"
ile değiştirecek:// 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, bu 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?
DefinePlugin
,EnvironmentPlugin
ile ilgili web paketi dokümanları
ES modüllerini kullanma
Kullanıcı arabirimi boyutunu azaltmanın bir sonraki yolu da ES modüllerini kullanmaktır.
ES modüllerini kullandığınızda webpack, ağaç sallama işlemini yapabilir. Ağaç sallama, bir paketleyicinin tüm bağımlılık ağacını geçip hangi bağımlılıkların kullanıldığını kontrol etmesi ve kullanılmayanları kaldırmasıdır. Böylece, ES modülü söz dizimini kullanırsanız webpack kullanılmayan kodu ortadan kaldırabilir:
Birden çok dışa aktarma işlemi olan 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
yönergesinin 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 işlev, ES modülleriyle yazılmış kitaplıklarda da çalışır.
Yine de tam olarak webpack'in yerleşik küçültücüyü (UglifyJsPlugin
) kullanmanız gerekmez.
Ölü kod kaldırma özelliğini destekleyen her türlü küçültücü (ör. Babel Minify eklentisi veya Google Closure Compiler eklentisi) sorunu çözecektir.
Daha fazla bilgi
Ağaç sallama hakkında web paketi belgeleri
Resimleri optimize edin
Resimler, sayfa boyutunun yarıdan fazlasını oluşturur. JavaScript kadar kritik olmasa da (ör. oluşturmayı engellemezler), yine de bant genişliğinin büyük bir kısmını tüketirler. Bunları web paketinde optimize etmek için url-loader
, svg-url-loader
ve image-webpack-loader
kullanın.
url-loader
, küçük statik dosyaları uygulamaya satır içine alır. Yapılandırma olmadan, geçirilen bir dosyayı alır, derlenen paketin yanına yerleştirir ve bu dosyanın URL'sini döndürür. Bununla birlikte, limit
seçeneğini belirtirsek bu sınırdan küçük dosyaları Base64 veri URL'si olarak kodlar ve bu URL'yi döndürür. Bu işlem, resmi JavaScript kodunda satır içine alır 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ı ile kodlamasıdır. Bu, SVG resimleri için kullanışlıdır. SVG dosyaları düz metin olduğundan, bu kodlama boyut açısından daha etkilidir.
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 kullanacağız.
Bu yükleyici, resimleri uygulamaya yerleştirmediğinden url-loader
ve svg-url-loader
ile birlikte çalışması gerekir. Her iki kurala da (biri JPG/PNG/GIF resimleri ve diğeri SVG resimleri için) kopyalanıp yapıştırılmaması için bu yükleyiciyi 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ını kullanmaya başlayabilirsiniz. Ancak, yükleyiciyi daha fazla yapılandırmak isterseniz eklenti seçeneklerine bakın. Hangi seçenekleri belirteceğinizi seçmek için Addy Osmani'nin mükemmel resim optimizasyonu 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 edin
Ortalama JavaScript boyutunun yarıdan fazlası bağımlılıklardan gelir ve bu boyutun bir kısmı gereksiz olabilir.
Örneğin, Lodash (4.17.4 sürümünden itibaren) pakete 72 KB küçültülmüş kod ekler. Ancak yöntemlerinden yalnızca 20 tanesini kullanırsanız, yaklaşık 65 KB'lık küçültülmüş kodun herhangi bir etkisi olmaz.
Diğer bir örnek ise Moment.js. 2.19.1 sürümü, 223 KB boyutunda küçültülmüş kod kapsıyor. Bu çok büyük bir değer. Bir sayfadaki JavaScript'in ortalama boyutu Ekim 2017'de 452 KB'tı. 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.
Tüm bu bağımlılıklar kolayca optimize edilebilir. Optimizasyon yaklaşımlarını bir GitHub deposunda topladık. İnceleyin.
ES modülleri için modül birleştirmeyi (diğer bir deyişle kapsam kaldırma) etkinleştir
Bir paket oluştururken, webpack her bir 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 durum her modül için bir boyut ve performans yükü ekliyordu.
Web paketi 2'de sunulan ES modülleri, CommonJS ve AMD modüllerinin aksine, her biri bir işlevle sarmalanmadan paketlenebilir. Webpack 3 ise modül birleştirme sayesinde böyle bir paketleme işlemi gerçekleştirilmesini sağladı. Modül birleştirmenin işi şu şekildedir:
// 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ştirmede, require
yalnızca gerekli işlevle değiştirilir ve modül 1 kaldırılır. Pakette daha az modül ve daha az modül ek yükü olur.
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
kullanın:
// webpack.config.js (for webpack 3)
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
};
Daha fazla bilgi
- ModuleConcatenation Plugin için web paketi belgeleri
- "Kapsam kaldırma hakkında kısa giriş"
- Bu eklentinin ne yaptığına ilişkin ayrıntılı açıklama
Hem webpack hem de webpack olmayan kodunuz varsa externals
kullanın
Bazı kodların webpack ile derlendiği, bazı kodların ise olmadığı büyük bir projeniz olabilir. Oynatıcı widget'ının web paketi ile oluşturulabileceği ancak etrafındaki sayfanın aşağıdaki özelliklere sahip olmadığı bir video barındırma sitesi gibi:
![Video barındırma sitesinin ekran görüntüsü](https://web.dev/static/articles/decrease-frontend-size/image/a-screenshot-a-video-hos-5b11b4fdff7f2.png?authuser=5&hl=tr)
Her iki kod parçasının da ortak bağımlılıkları varsa kodlarını birden fazla 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
konumunda bağımlılıklar varsa
Web paketi olmayan kodunuz window
içinde değişken olarak bulunan bağımlılıklara dayanıyorsa bağımlılık adlarını değişken adlarına takma ad olarak verir:
// webpack.config.js
module.exports = {
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
};
Bu yapılandırmada webpack, react
ve react-dom
paketlerini paketlemez. Bunların yerine, şunun gibi
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ın AMD paketleri olarak yüklenip yüklenmediği
Web paketi olmayan kodunuz window
ürününde bağımlılıkları açığa çıkarmıyorsa işler daha karmaşıktır.
Ancak web paketi olmayan kod bu bağımlılıkları AMD paketleri olarak tüketiyorsa aynı kodu iki kez yüklemekten kaçınabilirsiniz.
Bunu yapmak için web paketi kodunu bir AMD paketi ve kitaplık URL'leri için takma ad modülleri olarak derleyin:
// 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 sarmalayarak şu URL'lere bağlı olmasını sağlar:
// 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
externals
üzerindeki web paketi dokümanları
Özet
- Webpack 4 kullanıyorsanız üretim modunu etkinleştirin
- Paket düzeyinde küçültücü ve yükleyici seçenekleriyle kodunuzu küçültün
NODE_ENV
yerineproduction
koyarak yalnızca geliştirme amaçlı kodu kaldırın- Ağaç sallamayı etkinleştirmek için ES modüllerini kullanın
- Resimleri sıkıştırın
- Bağımlılığa özgü optimizasyonlar uygulama
- Modül birleştirmeyi etkinleştir
- Sizin için mantıklıysa
externals
kullanın.