Dalam codelab ini, tingkatkan performa aplikasi sederhana ini yang memungkinkan pengguna memberi rating pada kucing secara acak. Pelajari cara mengoptimalkan paket JavaScript dengan meminimalkan jumlah kode yang ditranspilasi.

Di aplikasi contoh, Anda dapat memilih kata atau emoji untuk menyampaikan seberapa besar Anda menyukai setiap kucing. Saat Anda mengklik tombol, aplikasi akan menampilkan nilai tombol di bawah gambar kucing saat ini.
Ukur
Sebaiknya mulai dengan memeriksa situs sebelum menambahkan pengoptimalan apa pun:
- Untuk melihat pratinjau situs, tekan View App. Lalu tekan
Fullscreen
.
- Tekan `Control+Shift+J` (atau `Command+Option+J` di Mac) untuk membuka DevTools.
- Klik tab Jaringan.
- Centang kotak Disable cache.
- Muat ulang aplikasi.

Lebih dari 80 KB digunakan untuk aplikasi ini. Saatnya mencari tahu apakah ada bagian paket yang tidak digunakan:
Tekan
Control+Shift+P(atauCommand+Shift+Pdi Mac) untuk membuka menu Command.
Masukkan
Show Coveragedan tekanEnteruntuk menampilkan tab Coverage.Di tab Coverage, klik Reload untuk memuat ulang aplikasi saat merekam cakupan.

Lihat berapa banyak kode yang digunakan dibandingkan dengan berapa banyak yang dimuat untuk paket utama:
Lebih dari setengah paket (44 KB) bahkan tidak digunakan. Hal ini karena banyak kode di dalamnya terdiri dari polyfill untuk memastikan bahwa aplikasi berfungsi di browser lama.
Menggunakan @babel/preset-env
Sintaksis bahasa JavaScript sesuai dengan standar yang dikenal sebagai ECMAScript, atau ECMA-262. Versi spesifikasi yang lebih baru dirilis setiap tahun dan menyertakan fitur baru yang telah melewati proses proposal. Setiap browser utama selalu berada pada tahap yang berbeda dalam mendukung fitur ini.
Fitur ES2015 berikut digunakan dalam aplikasi:
Fitur ES2017 berikut juga digunakan:
Jangan ragu untuk mempelajari kode sumber di src/index.js untuk melihat cara penggunaan semua ini.
Semua fitur ini didukung di Chrome versi terbaru, tetapi bagaimana dengan browser lain yang tidak mendukungnya? Babel, yang disertakan dalam aplikasi, adalah library paling populer yang digunakan untuk mengompilasi kode yang berisi sintaksis yang lebih baru menjadi kode yang dapat dipahami oleh browser dan lingkungan yang lebih lama. Hal ini dilakukan dengan dua cara:
- Polyfill disertakan untuk mengemulasi fungsi ES2015+ yang lebih baru sehingga API-nya
dapat digunakan meskipun tidak didukung oleh browser. Berikut adalah contoh
polyfill
metode
Array.includes. - Plugin digunakan untuk mengubah kode ES2015 (atau yang lebih baru) menjadi sintaksis ES5 yang lebih lama. Karena ini adalah perubahan terkait sintaksis (seperti fungsi panah), perubahan ini tidak dapat diemulasikan dengan polyfill.
Lihat package.json untuk melihat library Babel mana yang disertakan:
"dependencies": {
"@babel/polyfill": "^7.0.0"
},
"devDependencies": {
//...
"babel-loader": "^8.0.2",
"@babel/core": "^7.1.0",
"@babel/preset-env": "^7.1.0",
//...
}
@babel/coreadalah compiler Babel inti. Dengan demikian, semua konfigurasi Babel ditentukan dalam.babelrcdi root project.babel-loadermenyertakan Babel dalam proses build webpack.
Sekarang lihat webpack.config.js untuk melihat bagaimana babel-loader disertakan sebagai
aturan:
module: {
rules: [
//...
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
},
@babel/polyfillmenyediakan semua polyfill yang diperlukan untuk fitur ECMAScript yang lebih baru sehingga dapat berfungsi di lingkungan yang tidak mendukungnya. Library ini sudah diimpor di bagian paling atassrc/index.js.
import "./style.css";
import "@babel/polyfill";
@babel/preset-envmengidentifikasi transformasi dan polyfill yang diperlukan untuk browser atau lingkungan apa pun yang dipilih sebagai target.
Lihat file konfigurasi Babel, .babelrc, untuk melihat cara
menyertakannya:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions"
}
]
]
}
Ini adalah penyiapan Babel dan webpack. Pelajari cara menyertakan Babel dalam aplikasi Anda jika Anda menggunakan pemaket modul yang berbeda dengan webpack.
Atribut targets
di .babelrc mengidentifikasi browser yang ditargetkan. @babel/preset-env
terintegrasi dengan browserslist, yang berarti Anda dapat menemukan daftar lengkap kueri
yang kompatibel dan dapat digunakan di kolom ini dalam
dokumentasi browserlist.
Nilai "last 2 versions" mentranspilasi kode dalam aplikasi untuk
dua versi terakhir setiap browser.
Proses debug
Untuk melihat semua target Babel browser serta semua transformasi dan polyfill yang disertakan, tambahkan kolom debug ke .babelrc:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true
}
]
]
}
- Klik Alat.
- Klik Logs.
Muat ulang aplikasi dan lihat log status Glitch di bagian bawah editor.
Browser yang ditargetkan
Babel mencatat sejumlah detail ke konsol tentang proses kompilasi, termasuk semua lingkungan target tempat kode telah dikompilasi.

Perhatikan bahwa browser yang tidak lagi digunakan, seperti Internet Explorer, disertakan dalam daftar ini. Hal ini menjadi masalah karena browser yang tidak didukung tidak akan memiliki fitur baru yang ditambahkan, dan Babel terus mentranspilasi sintaksis tertentu untuk browser tersebut. Hal ini akan meningkatkan ukuran paket Anda secara tidak perlu jika pengguna tidak menggunakan browser ini untuk mengakses situs Anda.
Babel juga mencatat daftar plugin transformasi yang digunakan:

Daftar yang cukup panjang. Ini adalah semua plugin yang perlu digunakan Babel untuk mengubah sintaksis ES2015+ menjadi sintaksis lama untuk semua browser yang ditargetkan.
Namun, Babel tidak menampilkan polyfill spesifik yang digunakan:
![]()
Hal ini karena seluruh @babel/polyfill diimpor secara langsung.
Memuat polyfill satu per satu
Secara default, Babel menyertakan setiap polyfill yang diperlukan untuk lingkungan ES2015+ lengkap saat
@babel/polyfill diimpor ke dalam file. Untuk mengimpor polyfill tertentu yang diperlukan untuk browser target, tambahkan useBuiltIns: 'entry' ke konfigurasi.
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true
"useBuiltIns": "entry"
}
]
]
}
Muat ulang aplikasi. Sekarang Anda dapat melihat semua polyfill spesifik yang disertakan:

Meskipun hanya polyfill yang diperlukan untuk "last 2 versions" yang kini disertakan, daftarnya masih sangat panjang. Hal ini karena
polyfill yang diperlukan untuk browser target untuk setiap fitur yang lebih baru masih disertakan. Ubah nilai atribut menjadi usage
untuk hanya menyertakan nilai yang diperlukan untuk fitur yang digunakan dalam kode.
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true,
"useBuiltIns": "entry"
"useBuiltIns": "usage"
}
]
]
}
Dengan demikian, polyfill akan otomatis disertakan jika diperlukan.
Artinya, Anda dapat menghapus impor @babel/polyfill di src/index.js.
import "./style.css";
import "@babel/polyfill";
Sekarang, hanya polyfill yang diperlukan untuk aplikasi yang disertakan.

Ukuran app bundle berkurang secara signifikan.

Mempersempit daftar browser yang didukung
Jumlah target browser yang disertakan masih cukup besar, dan tidak banyak pengguna yang menggunakan browser yang tidak lagi digunakan seperti Internet Explorer. Perbarui konfigurasi menjadi berikut:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"targets": [">0.25%", "not ie 11"],
"debug": true,
"useBuiltIns": "usage",
}
]
]
}
Lihat detail untuk paket yang diambil.

Karena aplikasinya sangat kecil, perubahan ini tidak terlalu berpengaruh. Namun, pendekatan yang direkomendasikan adalah menggunakan persentase pangsa pasar browser (seperti
">0.25%") serta mengecualikan browser tertentu yang Anda yakini tidak digunakan oleh
pengguna. Baca artikel
"Last 2 versions" considered harmful
oleh James Kyle untuk mempelajari lebih lanjut hal ini.
Menggunakan <script type="module">
Masih ada banyak hal yang dapat ditingkatkan. Meskipun sejumlah polyfill yang tidak digunakan telah dihapus, ada banyak polyfill yang dikirimkan yang tidak diperlukan untuk beberapa browser. Dengan menggunakan modul, sintaksis yang lebih baru dapat ditulis dan dikirim ke browser secara langsung tanpa menggunakan polyfill yang tidak diperlukan.
Modul JavaScript adalah fitur yang relatif baru dan didukung di semua browser utama.
Modul dapat dibuat menggunakan atribut type="module" untuk menentukan skrip yang mengimpor dan mengekspor dari modul lain. Contoh:
// math.mjs
export const add = (x, y) => x + y;
<!-- index.html -->
<script type="module">
import { add } from './math.mjs';
add(5, 2); // 7
</script>
Banyak fitur ECMAScript yang lebih baru sudah didukung di lingkungan yang mendukung modul JavaScript (bukan memerlukan Babel). Artinya, konfigurasi Babel dapat diubah untuk mengirim dua versi aplikasi yang berbeda ke browser:
- Versi yang akan berfungsi di browser yang lebih baru yang mendukung modul dan yang menyertakan modul yang sebagian besar tidak ditranspilasi, tetapi memiliki ukuran file yang lebih kecil
- Versi yang menyertakan skrip yang lebih besar dan ditranspilasi yang akan berfungsi di browser lama mana pun
Menggunakan modul ES dengan Babel
Agar memiliki setelan @babel/preset-env terpisah untuk dua versi
aplikasi, hapus file .babelrc. Setelan Babel dapat ditambahkan ke konfigurasi webpack dengan menentukan dua format kompilasi yang berbeda untuk setiap versi aplikasi.
Mulailah dengan menambahkan konfigurasi untuk skrip lama ke webpack.config.js:
const legacyConfig = {
entry,
output: {
path: path.resolve(__dirname, "public"),
filename: "[name].bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env", {
useBuiltIns: "usage",
targets: {
esmodules: false
}
}]
]
}
},
cssRule
]
},
plugins
}
Perhatikan bahwa alih-alih menggunakan nilai targets untuk "@babel/preset-env",
esmodules dengan nilai false digunakan sebagai gantinya. Artinya, Babel
menyertakan semua transformasi dan polyfill yang diperlukan untuk menargetkan setiap browser yang
belum mendukung modul ES.
Tambahkan objek entry, cssRule, dan corePlugins ke awal file webpack.config.js. Semua ini digunakan bersama oleh modul dan skrip lama yang disajikan ke browser.
const entry = {
main: "./src"
};
const cssRule = {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
};
const plugins = [
new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
new HtmlWebpackPlugin({template: "./src/index.html"})
];
Sekarang, buat objek konfigurasi untuk skrip modul di bawah tempat legacyConfig ditentukan:
const moduleConfig = {
entry,
output: {
path: path.resolve(__dirname, "public"),
filename: "[name].mjs"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env", {
useBuiltIns: "usage",
targets: {
esmodules: true
}
}]
]
}
},
cssRule
]
},
plugins
}
Perbedaan utamanya adalah ekstensi file .mjs digunakan untuk nama file output. Nilai esmodules ditetapkan ke true di sini, yang berarti kode
yang dihasilkan ke dalam modul ini adalah skrip yang lebih kecil dan kurang dikompilasi yang tidak
melalui transformasi apa pun dalam contoh ini karena semua fitur yang digunakan
sudah didukung di browser yang mendukung modul.
Di bagian paling akhir file, ekspor kedua konfigurasi dalam satu array.
module.exports = [
legacyConfig, moduleConfig
];
Sekarang, tindakan ini akan membuat modul yang lebih kecil untuk browser yang mendukungnya dan skrip yang ditranspilasi yang lebih besar untuk browser lama.
Browser yang mendukung modul akan mengabaikan skrip dengan atribut nomodule.
Sebaliknya, browser yang tidak mendukung modul akan mengabaikan elemen skrip dengan
type="module". Artinya, Anda dapat menyertakan modul serta penggantian yang dikompilasi. Idealnya, kedua versi aplikasi harus berada di index.html
seperti ini:
<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js"></script>
Browser yang mendukung modul akan mengambil dan mengeksekusi main.mjs serta mengabaikan
main.bundle.js. Browser yang tidak mendukung modul akan melakukan hal sebaliknya.
Penting untuk diperhatikan bahwa tidak seperti skrip reguler, skrip modul selalu ditangguhkan secara default.
Jika Anda ingin skrip nomodule yang setara juga ditangguhkan dan hanya dieksekusi setelah
penguraian, Anda harus menambahkan atribut defer:
<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js" defer></script>
Hal terakhir yang perlu dilakukan di sini adalah menambahkan atribut module dan nomodule
masing-masing ke modul dan skrip lama, Impor
ScriptExtHtmlWebpackPlugin
di bagian paling atas webpack.config.js:
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
Sekarang, perbarui array plugins dalam konfigurasi untuk menyertakan plugin ini:
const plugins = [
new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
new HtmlWebpackPlugin({template: "./src/index.html"}),
new ScriptExtHtmlWebpackPlugin({
module: /\.mjs$/,
custom: [
{
test: /\.js$/,
attribute: 'nomodule',
value: ''
},
]
})
];
Setelan plugin ini menambahkan atribut type="module" untuk semua elemen skrip .mjs
serta atribut nomodule untuk semua modul skrip .js.
Menyajikan modul dalam dokumen HTML
Hal terakhir yang perlu dilakukan adalah menghasilkan elemen skrip lama dan modern ke file HTML. Sayangnya, plugin yang membuat file HTML akhir, HTMLWebpackPlugin, saat ini tidak mendukung output skrip modul dan nomodule. Meskipun ada solusi dan plugin terpisah yang dibuat untuk mengatasi masalah ini, seperti BabelMultiTargetPlugin dan HTMLWebpackMultiBuildPlugin, pendekatan yang lebih sederhana untuk menambahkan elemen skrip modul secara manual digunakan untuk tujuan tutorial ini.
Tambahkan kode berikut ke src/index.js di bagian akhir file:
...
</form>
<script type="module" src="main.mjs"></script>
</body>
</html>
Sekarang muat aplikasi di browser yang mendukung modul, seperti Chrome versi terbaru.

Hanya modul yang diambil, dengan ukuran paket yang jauh lebih kecil karena sebagian besar tidak ditranspilasi. Elemen skrip lainnya sepenuhnya diabaikan oleh browser.
Jika Anda memuat aplikasi di browser lama, hanya skrip yang lebih besar dan ditranspilasi dengan semua polyfill dan transformasi yang diperlukan yang akan diambil. Berikut adalah screenshot untuk semua permintaan yang dibuat di Chrome versi lama (versi 38).

Kesimpulan
Sekarang Anda memahami cara menggunakan @babel/preset-env untuk hanya menyediakan polyfill yang diperlukan untuk browser yang ditargetkan. Anda juga tahu cara modul JavaScript
dapat meningkatkan performa lebih lanjut dengan mengirimkan dua versi aplikasi yang ditranspilasi
yang berbeda. Dengan pemahaman yang baik tentang cara kedua teknik ini dapat mengurangi
ukuran paket secara signifikan, lanjutkan dan optimalkan.