Meminifikasi dan mengompresi payload jaringan dengan gzip

Codelab ini mempelajari cara meminifikasi dan mengompresi paket JavaScript untuk aplikasi berikut dapat meningkatkan performa halaman dengan mengurangi ukuran permintaan aplikasi.

Screenshot aplikasi

Ukur

Sebelum mulai menambahkan pengoptimalan, sebaiknya Anda menganalisis status aplikasi saat ini terlebih dahulu.

  • Untuk melihat pratinjau situs, tekan Lihat Aplikasi. Kemudian tekan Layar Penuh layar penuh.

Aplikasi ini, yang juga dibahas dalam codelab "Menghapus kode yang tidak digunakan", memungkinkan Anda memberikan suara untuk kucing favorit Anda. 🐈

Sekarang lihat seberapa besar aplikasi ini:

  1. Tekan `Control+Shift+J` (atau `Command+Option+J` di Mac) untuk membuka DevTools.
  2. Klik tab Jaringan.
  3. Centang kotak Nonaktifkan cache.
  4. Muat ulang aplikasi.

Ukuran paket asli di panel Jaringan

Meskipun banyak progres yang dibuat dalam codelab "Remove unused code" untuk memangkas ukuran paket ini, 225 KB masih cukup besar.

Minifikasi

Pertimbangkan blok kode berikut.

function soNice() {
  let counter = 0;

  while (counter < 100) {
    console.log('nice');
    counter++;
  }
}

Jika fungsi ini disimpan dalam filenya sendiri, ukuran filenya sekitar 112 B (byte).

Jika semua spasi kosong dihapus, kode yang dihasilkan akan terlihat seperti ini:

function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}

Ukuran file sekarang akan menjadi sekitar 83 B. Jika dirusak lebih lanjut dengan mengurangi panjang nama variabel dan mengubah beberapa ekspresi, kode akhir mungkin akan terlihat seperti ini:

function soNice(){for(let i=0;i<100;)console.log("nice"),i++}

Ukuran file kini mencapai 62 B.

Dengan setiap langkah, kode menjadi lebih sulit dibaca. Namun, mesin JavaScript browser menafsirkan setiap hal ini dengan cara yang sama persis. Manfaat obfuscation kode dengan cara ini dapat membantu mencapai ukuran file yang lebih kecil. 112 B memang tidak terlalu besar, tetapi masih ada pengurangan ukuran sebesar 50%.

Dalam aplikasi ini, webpack versi 4 digunakan sebagai paket modul. Versi tertentu dapat dilihat di package.json.

"devDependencies": {
  //...
  "webpack": "^4.16.4",
  //...
}

Versi 4 sudah melakukan minifikasi paket secara default selama mode produksi. Plugin ini menggunakan TerserWebpackPlugin plugin untuk Terser. Terser adalah alat populer yang digunakan untuk mengompresi kode JavaScript.

Untuk mendapatkan gambaran tentang tampilan kode yang diminifikasi, lanjutkan dan klik main.bundle.js saat masih berada di panel Jaringan DevTools. Sekarang, klik tab Response.

Respons yang diminifikasi

Kode dalam bentuk akhirnya, yang diminifikasi dan diubah, ditampilkan dalam isi respons. Untuk mengetahui ukuran paket jika tidak diminifikasi, buka webpack.config.js dan perbarui konfigurasi mode.

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

Muat ulang aplikasi dan lihat lagi ukuran paket melalui panel Network DevTools

Ukuran paket 767 KB

Itu adalah perbedaan yang cukup besar. 😅

Pastikan untuk mengembalikan perubahan di sini sebelum melanjutkan.

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

Menyertakan proses untuk melakukan minifikasi kode di aplikasi Anda bergantung pada alat yang Anda gunakan:

  • Jika webpack v4 atau yang lebih baru digunakan, tidak ada pekerjaan tambahan yang perlu dilakukan karena kode diminifikasi secara default dalam mode produksi. 👍
  • Jika webpack versi lama digunakan, instal dan sertakan TerserWebpackPlugin ke dalam proses build webpack. Dokumentasi menjelaskan hal ini secara mendetail.
  • Plugin minifikasi lainnya juga ada dan dapat digunakan sebagai gantinya, seperti BabelMinifyWebpackPlugin dan ClosureCompilerPlugin.
  • Jika penggabungan modul tidak digunakan sama sekali, gunakan Terser sebagai alat CLI atau sertakan langsung sebagai dependensi.

Kompresi

Meskipun istilah "kompresi" terkadang digunakan secara longgar untuk menjelaskan cara kode dikurangi selama proses minifikasi, kode tersebut sebenarnya tidak dikompresi dalam arti harfiah.

Kompresi biasanya mengacu pada kode yang telah dimodifikasi menggunakan algoritma kompresi data. Tidak seperti minifikasi yang akhirnya memberikan kode yang benar-benar valid, kode yang dikompresi harus didekompresi sebelum digunakan.

Dengan setiap permintaan dan respons HTTP, browser dan server web dapat menambahkan header untuk menyertakan informasi tambahan tentang aset yang diambil atau diterima. Hal ini dapat dilihat di tab Headers dalam panel Jaringan DevTools tempat tiga jenis ditampilkan:

  • General mewakili header umum yang relevan dengan seluruh interaksi permintaan-respons.
  • Header Respons menampilkan daftar header khusus untuk respons sebenarnya dari server.
  • Header Permintaan menampilkan daftar header yang dilampirkan ke permintaan oleh klien.

Lihat header accept-encoding di Request Headers.

Menerima header encoding

accept-encoding digunakan oleh browser untuk menentukan format encoding konten, atau algoritma kompresi, yang didukungnya. Ada banyak algoritma kompresi teks di luar sana, tetapi hanya ada tiga yang didukung di sini untuk kompresi (dan dekompresi) permintaan jaringan HTTP:

  • Gzip (gzip): Format kompresi yang paling banyak digunakan untuk interaksi server dan klien. Library ini dibuat berdasarkan algoritma Deflate dan didukung di semua browser saat ini.
  • Deflate (deflate): Tidak umum digunakan.
  • Brotli (br): Algoritma kompresi baru yang bertujuan untuk lebih meningkatkan rasio kompresi, yang dapat menghasilkan pemuatan halaman yang lebih cepat. Fitur ini didukung di versi terbaru sebagian besar browser.

Aplikasi contoh dalam tutorial ini identik dengan aplikasi yang diselesaikan di codelab "Remove unused code", kecuali fakta bahwa Express kini digunakan sebagai framework server. Dalam beberapa bagian berikutnya, kompresi statis dan dinamis akan dipelajari.

Kompresi dinamis

Kompresi dinamis melibatkan kompresi aset dengan cepat saat diminta oleh browser.

Kelebihan

  • Anda tidak perlu membuat dan mengupdate versi terkompresi yang tersimpan dari aset.
  • Kompresi on-the-fly sangat cocok untuk halaman web yang dibuat secara dinamis.

Kekurangan

  • Kompresi file pada tingkat yang lebih tinggi untuk mencapai rasio kompresi yang lebih baik memerlukan waktu lebih lama. Hal ini dapat menyebabkan penurunan performa saat pengguna menunggu aset dikompresi sebelum dikirim oleh server.

Kompresi dinamis dengan Node/Express

File server.js bertanggung jawab untuk menyiapkan server Node yang menghosting aplikasi.

const express = require('express');

const app = express();

app.use(express.static('public'));

const listener = app.listen(process.env.PORT, function() {
  console.log('Your app is listening on port ' + listener.address().port);
});

Semua yang dilakukan saat ini adalah mengimpor express dan menggunakan middleware express.static untuk memuat semua file HTML, JS, dan CSS statis di direktori public/ (dan file tersebut dibuat oleh webpack dengan setiap build).

Untuk memastikan semua aset dikompresi setiap kali diminta, library middleware compression dapat digunakan. Mulai dengan menambahkannya sebagai devDependency di package.json:

"devDependencies": {
  //...
  "compression": "^1.7.3"
},

Lalu impor ke dalam file server, server.js:

const express = require('express');
const compression = require('compression');

Dan tambahkan sebagai middleware sebelum express.static dipasang:

//...

const app = express();

app.use(compression());

app.use(express.static('public'));

//...

Sekarang muat ulang aplikasi dan lihat ukuran paket di panel Jaringan.

Ukuran paket dengan kompresi dinamis

Dari 225 KB menjadi 61,6 KB. Di Response Headers sekarang, header content-encoding menunjukkan bahwa server mengirim file ini yang dienkode dengan gzip.

Header encoding konten

Kompresi statis

Ide di balik kompresi statis adalah mengompresi dan menyimpan aset sebelumnya.

Kelebihan

  • Latensi karena tingkat kompresi yang tinggi bukan lagi masalah penting. Tidak perlu melakukan apa pun dengan cepat untuk mengompresi file karena sekarang file tersebut dapat diambil langsung.

Kekurangan

  • Aset harus dikompresi dengan setiap build. Waktu build dapat meningkat secara signifikan jika tingkat kompresi tinggi digunakan.

Kompresi statis dengan Node/Express dan webpack

Karena kompresi statis melibatkan kompresi file terlebih dahulu, setelan webpack dapat diubah untuk mengompresi aset sebagai bagian dari langkah build. CompressionPlugin dapat digunakan untuk ini.

Mulai dengan menambahkannya sebagai devDependency di package.json:

"devDependencies": {
  //...
  "compression-webpack-plugin": "^1.1.11"
},

Seperti plugin webpack lainnya, impor plugin ini dalam file konfigurasi, webpack.config.js:

const path = require("path");

//...

const CompressionPlugin = require("compression-webpack-plugin");

Dan sertakan dalam array plugins:

module.exports = {
  //...
  plugins: [
    //...
    new CompressionPlugin()
  ]
}

Secara default, plugin mengompresi file build menggunakan gzip. Lihat dokumentasi untuk mempelajari cara menambahkan opsi untuk menggunakan algoritma lain atau menyertakan/mengecualikan file tertentu.

Saat aplikasi dimuat ulang dan di-build ulang, versi terkompresi paket utama kini akan dibuat. Buka Glitch Console untuk melihat isi direktori public/ akhir yang ditayangkan oleh server Node.

  • Klik tombol Alat.
  • Klik tombol Konsol.
  • Di konsol, jalankan perintah berikut untuk beralih ke direktori public dan melihat semua filenya:
cd public
ls

File output akhir di direktori publik

Versi bundle yang di-gzip, main.bundle.js.gz, kini juga disimpan di sini. CompressionPlugin juga mengompresi index.html secara default.

Hal berikutnya yang perlu dilakukan adalah memberi tahu server untuk mengirim file yang di-gzip ini setiap kali versi JS aslinya diminta. Hal ini dapat dilakukan dengan menentukan rute baru di server.js sebelum file ditayangkan dengan express.static.

const express = require('express');
const app = express();

app.get('*.js', (req, res, next) => {
  req.url = req.url + '.gz';
  res.set('Content-Encoding', 'gzip');
  next();
});

app.use(express.static('public'));

//...

app.get digunakan untuk memberi tahu server cara merespons permintaan GET untuk endpoint tertentu. Fungsi callback kemudian digunakan untuk menentukan cara menangani permintaan ini. Rute ini berfungsi seperti ini:

  • Menentukan '*.js' sebagai argumen pertama berarti ini berfungsi untuk setiap endpoint yang diaktifkan untuk mengambil file JS.
  • Dalam callback, .gz dilampirkan ke URL permintaan dan header respons Content-Encoding ditetapkan ke gzip.
  • Terakhir, next() akan memastikan urutan berlanjut ke callback yang mungkin berikutnya.

Setelah aplikasi dimuat ulang, lihat panel Network sekali lagi.

Pengurangan ukuran paket dengan kompresi statis

Seperti sebelumnya, pengurangan ukuran paket yang signifikan.

Kesimpulan

Codelab ini membahas proses minifikasi dan kompresi kode sumber. Kedua teknik ini menjadi default di banyak alat yang tersedia saat ini, jadi penting untuk mengetahui apakah toolchain Anda sudah mendukung keduanya atau apakah Anda harus mulai menerapkan kedua proses tersebut sendiri.