Cara menggunakan webpack untuk membuat aplikasi sekecil mungkin
Salah satu hal pertama yang harus dilakukan saat mengoptimalkan aplikasi adalah membuatnya sekecil sebaik mungkin. Berikut cara melakukannya dengan webpack.
Menggunakan mode produksi (khusus webpack 4)
Webpack 4 memperkenalkan flag mode
baru. Anda dapat menyetel
flag ini ke 'development'
atau 'production'
untuk memberi petunjuk pada webpack yang sedang Anda buat
aplikasi untuk lingkungan tertentu:
// webpack.config.js
module.exports = {
mode: 'production',
};
Pastikan Anda mengaktifkan mode production
saat membangun aplikasi untuk produksi.
Tindakan ini akan membuat webpack menerapkan pengoptimalan seperti minifikasi, penghapusan kode khusus pengembangan
di library, dan lain-lain.
Bacaan lebih lanjut
Mengaktifkan minifikasi
Minifikasi adalah ketika Anda mengompresi kode dengan menghapus spasi ekstra, mempersingkat nama variabel dan dan seterusnya. Seperti ini:
// 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 mendukung dua cara untuk meminifikasi kode: minifikasi tingkat paket dan opsi khusus loader. Keduanya harus digunakan secara bersamaan.
Minifikasi tingkat paket
Minifikasi tingkat paket mengompresi seluruh paket setelah kompilasi. Berikut cara kerjanya:
Anda menulis kode seperti ini:
// comments.js import './comments.css'; export function render(data, target) { console.log('Rendered!'); }
Webpack mengompilasinya menjadi sekitar berikut ini:
// 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!'); }
minifier mengompresinya menjadi sekitar berikut:
// 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)
Di webpack 4, minifikasi tingkat paket diaktifkan secara otomatis – baik dalam produksi
dan tanpa satu pun. Arsitektur ini menggunakan minifier UglifyJS
di balik layar. (Jika Anda perlu menonaktifkan minifikasi, cukup gunakan mode pengembangan
atau teruskan false
ke opsi optimization.minimize
.)
Di webpack 3, Anda harus menggunakan plugin UglifyJS
secara langsung. Plugin ini dilengkapi dengan webpack; untuk mengaktifkannya, tambahkan ke plugins
pada konfigurasi:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.UglifyJsPlugin(),
],
};
Opsi khusus loader
Cara kedua untuk mengecilkan kode adalah opsi khusus loader (apa yang dimaksud
adalah). Dengan opsi loader, Anda bisa mengompresi hal-hal yang
minifier tidak bisa diminifikasi. Misalnya, saat Anda mengimpor
file CSS dengan
css-loader
, file akan dikompilasi menjadi string:
/* 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}",""]);
Minifier tidak dapat mengompresi kode ini karena berupa string. Untuk meminifikasi isi file, kita perlu konfigurasikan loader untuk melakukan ini:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { minimize: true } },
],
},
],
},
};
Bacaan lebih lanjut
- Dokumen UglifyJsPlugin
- Minifier populer lainnya: Babel Minifikasi, Penutupan Google Compiler
Tentukan NODE_ENV=production
Cara lain untuk mengurangi ukuran front-end adalah dengan menyetel NODE_ENV
variabel lingkungan
dalam kode Anda dengan nilai production
.
Library membaca variabel NODE_ENV
untuk mendeteksi dalam mode mana variabel tersebut harus berfungsi – dalam
pengembangan atau produksi. Beberapa library berperilaku berbeda berdasarkan variabel ini. Sebagai
Misalnya, jika NODE_ENV
tidak ditetapkan ke production
, Vue.js akan melakukan pemeriksaan dan pencetakan tambahan
peringatan:
// vue/dist/vue.runtime.esm.js
// …
if (process.env.NODE_ENV !== 'production') {
warn('props must be strings when using array syntax.');
}
// …
React berfungsi dengan cara yang sama – aplikasi ini memuat build pengembangan yang menyertakan peringatan:
// 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.'
);
// …
Pemeriksaan dan peringatan semacam itu biasanya tidak diperlukan dalam produksi, tetapi tetap ada dalam kode dan
meningkatkan ukuran library. Di webpack 4, hapus file tersebut dengan menambahkan
opsi optimization.nodeEnv: 'production'
:
// webpack.config.js (for webpack 4)
module.exports = {
optimization: {
nodeEnv: 'production',
minimize: true,
},
};
Di webpack 3, gunakan DefinePlugin
:
// 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()
]
};
Opsi optimization.nodeEnv
dan DefinePlugin
berfungsi dengan cara yang sama –
semua kemunculan process.env.NODE_ENV
dengan nilai yang ditentukan. Dengan
konfigurasi dari atas:
Webpack akan mengganti semua kemunculan
process.env.NODE_ENV
dengan"production"
:// 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.'); }
Lalu, minifier akan menghapus semua Cabang
if
– karena"production" !== 'production'
selalu salah, dan plugin memahami bahwa kode di dalam cabang ini tidak akan pernah dieksekusi:// 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 }; }
Bacaan lebih lanjut
- Apa yang dimaksud dengan "variabel lingkungan"
- Dokumen Webpack tentang:
DefinePlugin
,EnvironmentPlugin
Menggunakan modul ES
Cara berikutnya untuk mengurangi ukuran front-end adalah dengan menggunakan ES modul Google Cloud.
Saat Anda menggunakan modul ES, webpack akan dapat melakukan tree-shaking. Tree-shaking adalah ketika pemaket menelusuri seluruh hierarki dependensi, memeriksa dependensi apa yang digunakan, dan menghapus dependensi yang tidak digunakan. Jadi, jika Anda menggunakan sintaksis modul ES, webpack dapat menghilangkan kode yang tidak digunakan:
Anda menulis file dengan beberapa ekspor, tetapi aplikasi hanya menggunakan salah satunya:
// comments.js export const render = () => { return 'Rendered!'; }; export const commentRestEndpoint = '/rest/comments'; // index.js import { render } from './comments.js'; render();
Webpack memahami bahwa
commentRestEndpoint
tidak digunakan dan tidak membuat titik ekspor terpisah dalam paket:// 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 */ })
Minifier menghapus variabel yang tidak digunakan:
// bundle.js (part that corresponds to comments.js) (function(n,e){"use strict";var r=function(){return"Rendered!"};e.b=r})
Cara ini berfungsi bahkan dengan library jika ditulis dengan modul ES.
Namun, Anda tidak perlu menggunakan minifier bawaan webpack dengan tepat (UglifyJsPlugin
).
Setiap minifier yang mendukung penghapusan kode yang mati
(misalnya, plugin Babel Minify
atau plugin Google Closure Compiler)
bisa menyelesaikan tugasnya.
Bacaan lebih lanjut
Dokumen Webpack tentang tree shaking
Mengoptimalkan gambar
Gambar menghasilkan lebih dari
setengah dari ukuran halaman. Meskipun mereka
tidak sepenting JavaScript (misalnya, tidak memblokir rendering), masih memakan sebagian besar
mengurangi {i>bandwidth<i}. Gunakan url-loader
, svg-url-loader
, dan image-webpack-loader
untuk mengoptimalkannya di
webpack.
url-loader
menyisipkan file statis kecil ke dalam
. Tanpa konfigurasi, diperlukan file yang diteruskan, menempatkannya di samping paket yang dikompilasi, dan menampilkan
URL dari file tersebut. Namun, jika kita menentukan opsi limit
, opsi ini akan mengenkode file yang lebih kecil dari
batas ini sebagai URL data Base64 dan tampilkan url ini. Ini
menyisipkan gambar ke dalam kode JavaScript dan menyimpan permintaan HTTP:
// 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
berfungsi seperti url-loader
–
kecuali bahwa skrip tersebut mengenkode file dengan URL
encoding daripada base64
satu. Ini berguna untuk gambar SVG - karena file SVG hanya berupa teks biasa, pengkodean ini
lebih hemat ukuran.
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
loader: "svg-url-loader",
options: {
limit: 10 * 1024,
noquotes: true
}
}
]
}
};
image-webpack-loader
mengompresi gambar yang
melewatinya. Ia mendukung gambar JPG, PNG, GIF dan SVG, jadi kita akan menggunakannya untuk semua jenis ini.
Loader ini tidak menyematkan gambar ke aplikasi, sehingga harus berfungsi bersama url-loader
dan
svg-url-loader
. Untuk menghindari menyalin dan menempelnya ke kedua aturan (satu untuk gambar JPG/PNG/GIF, dan satu lagi
satu untuk SVG), kita akan menyertakan loader ini sebagai aturan terpisah dengan enforce: 'pre'
:
// 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'
}
]
}
};
Pengaturan default loader sudah siap – tetapi jika Anda ingin mengonfigurasinya lihat opsi plugin. Kepada memilih opsi yang ingin ditentukan, lihat panduan gambar yang luar biasa dari Addy Osmani pengoptimalan.
Bacaan lebih lanjut
Mengoptimalkan dependensi
Lebih dari setengah ukuran JavaScript rata-rata berasal dari dependensi, dan sebagian dari ukuran itu mungkin tidak diperlukan.
Misalnya, Lodash (mulai v4.17.4) menambahkan 72 KB kode yang diminifikasi ke paket. Tetapi jika hanya menggunakan, seperti, 20 metodenya, maka sekitar 65 KB kode yang diminifikasi tidak melakukan apa-apa.
Contoh lainnya adalah Moment.js. Versi 2.19.1-nya mengambil 223 KB kode yang diminifikasi, yang sangat besar - ukuran rata-rata JavaScript di halaman adalah 452 KB pada bulan Oktober 2017. Namun, 170 KB dari ukuran tersebut adalah pelokalan file. Jika Anda tidak menggunakan Moment.js dengan beberapa bahasa, file ini akan membengkak tanpa tujuan.
Semua dependensi ini dapat dioptimalkan dengan mudah. Kami telah mengumpulkan pendekatan pengoptimalan di repo GitHub – lihat di sini!
Mengaktifkan penyambungan modul untuk modul ES (atau pengangkatan cakupan)
Saat Anda membuat bundle, webpack akan menggabungkan setiap modul ke dalam sebuah fungsi:
// 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!');
}
})
Sebelumnya, langkah ini diperlukan untuk mengisolasi modul CommonJS/AMD dari satu sama lain. Namun, hal ini menambahkan {i>overhead<i} ukuran dan kinerja untuk setiap modul.
Webpack 2 memperkenalkan dukungan untuk modul ES yang tidak seperti modul CommonJS dan AMD, tanpa harus menggabungkan masing-masing dengan sebuah fungsi. Dan webpack 3 memungkinkan pemaketan tersebut – dengan penyambungan modul. Berikut apa yang dilakukan sambungan modul:
// 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();
})
Anda bisa lihat perbedaannya? Dalam paket biasa, modul 0 memerlukan render
dari modul 1. Dengan
penyambungan modul, require
cukup diganti dengan fungsi yang diperlukan, dan modul 1
dihapus. Paket ini memiliki lebih sedikit modul – dan lebih sedikit overhead modul.
Untuk mengaktifkan perilaku ini, di webpack 4, aktifkan opsi optimization.concatenateModules
:
// webpack.config.js (for webpack 4)
module.exports = {
optimization: {
concatenateModules: true
}
};
Di webpack 3, gunakan ModuleConcatenationPlugin
:
// webpack.config.js (for webpack 3)
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
};
Bacaan lebih lanjut
- Dokumen Webpack untuk ModuleConcatenationPlugin
- “Pengantar singkat tentang ruang lingkup pengangkatan”
- Deskripsi terperinci tentang bagaimana plugin ini melakukan
Gunakan externals
jika Anda memiliki kode webpack dan non-webpack
Anda mungkin memiliki project besar tempat beberapa kode dikompilasi dengan webpack, dan beberapa kode lainnya tidak. Suka situs hosting video, tempat widget pemutar dapat dibuat dengan webpack, dan halaman di sekitarnya mungkin tidak:
Jika kedua kode memiliki dependensi yang sama, Anda dapat membagikannya agar tidak mendownload kodenya
beberapa kali. Hal ini dilakukan dengan externals
webpack
– fungsi ini menggantikan modul dengan variabel atau
impor eksternal lainnya.
Jika dependensi tersedia di window
Jika kode non-webpack bergantung pada dependensi yang tersedia sebagai variabel di window
,
nama dependensi ke nama variabel:
// webpack.config.js
module.exports = {
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
};
Dengan konfigurasi ini, webpack tidak akan memaketkan paket react
dan react-dom
. Sebaliknya, mereka akan
diganti dengan yang seperti ini:
// 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;
})
Jika dependensi dimuat sebagai paket AMD
Jika kode non-webpack tidak mengekspos dependensi ke dalam window
, prosesnya akan menjadi lebih rumit.
Namun, Anda tetap dapat menghindari pemuatan kode yang sama dua kali jika kode non-webpack menggunakan
dependensi sebagai paket AMD.
Untuk melakukannya, kompilasi kode webpack sebagai paket AMD dan modul alias ke URL library:
// webpack.config.js
module.exports = {
output: {
libraryTarget: 'amd'
},
externals: {
'react': {
amd: '/libraries/react.min.js'
},
'react-dom': {
amd: '/libraries/react-dom.min.js'
}
}
};
Webpack akan menggabungkan paket ke dalam define()
dan membuatnya bergantung pada URL berikut:
// bundle.js (beginning)
define(["/libraries/react.min.js", "/libraries/react-dom.min.js"], function () { … });
Jika kode non-webpack menggunakan URL yang sama untuk memuat dependensinya, file ini akan dimuat hanya sekali – permintaan tambahan akan menggunakan cache loader.
Bacaan lebih lanjut
- Dokumen Webpack di
externals
Mengambil kesimpulan
- Mengaktifkan mode produksi jika Anda menggunakan webpack 4
- Minimalkan kode Anda dengan opsi minifier dan loader tingkat paket
- Hapus kode khusus pengembangan dengan mengganti
NODE_ENV
denganproduction
- Menggunakan modul ES untuk mengaktifkan tree shaking
- Kompresi gambar
- Menerapkan pengoptimalan khusus dependensi
- Aktifkan penyambungan modul
- Gunakan
externals
jika sesuai untuk Anda