Gulp: Senjata Rahasia Pengembang Web untuk Memaksimalkan Kecepatan Situs
Diterbitkan: 2022-03-11Banyak dari kita harus menangani proyek berbasis web yang digunakan dalam produksi, yang menyediakan berbagai layanan kepada publik. Saat menangani proyek semacam itu, penting untuk dapat membangun dan menerapkan kode kita dengan cepat. Melakukan sesuatu dengan cepat sering menyebabkan kesalahan, terutama jika suatu proses berulang, oleh karena itu adalah praktik yang baik untuk mengotomatiskan proses seperti itu sebanyak mungkin.
Dalam posting ini, kita akan melihat alat yang dapat menjadi bagian dari apa yang memungkinkan kita mencapai otomatisasi tersebut. Alat ini adalah paket npm yang disebut Gulp.js. Untuk menjadi akrab dengan terminologi dasar Gulp.js yang digunakan dalam posting ini, silakan merujuk ke "Pengantar Otomatisasi JavaScript dengan Gulp" yang sebelumnya diterbitkan di blog oleh Antonios Minas, salah satu sesama pengembang Toptal kami. Kami akan mengasumsikan keakraban dasar dengan lingkungan npm, karena digunakan secara luas di seluruh posting ini untuk menginstal paket.
Melayani Aset Front-End
Sebelum melanjutkan, mari kita mundur beberapa langkah untuk mendapatkan gambaran umum tentang masalah yang dapat diselesaikan Gulp.js untuk kita. Banyak proyek berbasis web menampilkan file JavaScript front-end yang disajikan kepada klien untuk menyediakan berbagai fungsi ke halaman web. Biasanya ada juga satu set stylesheet CSS yang disajikan ke klien juga. Terkadang saat melihat source code sebuah website atau aplikasi web, kita bisa melihat kode seperti ini:
<link href="css/main.css" rel="stylesheet"> <link href="css/custom.css" rel="stylesheet"> <script src="js/jquery.min.js"></script> <script src="js/site.js"></script> <script src="js/module1.js"></script> <script src="js/module2.js"></script>
Ada beberapa masalah dengan kode ini. Ini memiliki referensi ke dua lembar gaya CSS terpisah dan empat file JavaScript terpisah. Ini berarti bahwa server harus membuat total enam permintaan ke server, dan setiap permintaan harus memuat sumber daya secara terpisah sebelum halaman siap. Ini bukan masalah dengan HTTP/2 karena HTTP/2 memperkenalkan paralelisme dan kompresi header, tetapi masih menjadi masalah. Ini meningkatkan total volume lalu lintas yang diperlukan untuk memuat halaman ini dan mengurangi kualitas pengalaman pengguna karena membutuhkan waktu lebih lama untuk memuat file. Dalam kasus HTTP 1.1, itu juga memonopoli jaringan dan mengurangi jumlah saluran permintaan yang tersedia. Akan jauh lebih baik untuk menggabungkan file CSS dan JavaScript menjadi satu bundel untuk masing-masing. Dengan begitu, hanya akan ada total dua permintaan. Juga akan menyenangkan untuk menyajikan versi yang diperkecil dari file-file ini, yang biasanya jauh lebih kecil daripada aslinya. Aplikasi web kami mungkin juga rusak jika salah satu aset di-cache, dan klien akan menerima versi yang sudah usang.
Salah satu pendekatan primitif untuk memecahkan beberapa masalah ini adalah dengan menggabungkan setiap jenis aset secara manual ke dalam bundel menggunakan editor teks, dan kemudian menjalankan hasilnya melalui layanan minifier, seperti http://jscompress.com/. Ini terbukti sangat membosankan untuk dilakukan terus menerus selama proses pengembangan. Sedikit peningkatan namun patut dipertanyakan adalah meng-host server minifier kami sendiri, menggunakan salah satu paket yang tersedia di GitHub. Kemudian kita bisa melakukan hal-hal yang akan terlihat agak mirip dengan berikut ini:
<script src="min/f=js/site.js,js/module1.js"></script>
Ini akan menyajikan file yang diperkecil ke klien kami, tetapi itu tidak akan menyelesaikan masalah caching. Itu juga akan menyebabkan beban tambahan di server karena server kami pada dasarnya harus menggabungkan dan mengecilkan semua file sumber secara berulang pada setiap permintaan.
Mengotomatiskan dengan Gulp.js
Tentunya kita bisa melakukan lebih baik daripada salah satu dari dua pendekatan ini. Yang kami inginkan adalah mengotomatiskan bundling dan memasukkannya ke dalam fase build proyek kami. Kami ingin mengakhiri dengan kumpulan aset pra-bangun yang sudah diperkecil dan siap ditayangkan. Kami juga ingin memaksa klien untuk menerima versi terbaru dari kumpulan aset kami pada setiap permintaan, tetapi kami masih ingin memanfaatkan caching jika memungkinkan. Beruntung bagi kami, Gulp.js bisa mengatasinya. Di sisa artikel, kami akan membangun solusi yang akan memanfaatkan kekuatan Gulp.js untuk menggabungkan dan mengecilkan file. Kami juga akan menggunakan plugin untuk memecahkan cache saat ada pembaruan.
Kami akan membuat direktori dan struktur file berikut dalam contoh kami:
public/ |- build/ |- js/ |- bundle-{hash}.js |- css/ |- stylesheet-{hash}.css assets/ |- js/ |- vendor/ |- jquery.js |- site.js |- module1.js |- module2.js |- css/ |- main.css |- custom.css gulpfile.js package.json
File gulpfile.js adalah tempat kita akan menentukan tugas yang akan dilakukan Gulp untuk kita. package.json
digunakan oleh npm untuk mendefinisikan paket aplikasi kita dan melacak dependensi yang akan kita instal. Direktori publik adalah apa yang harus dikonfigurasi untuk menghadapi web. Direktori aset adalah tempat kami akan menyimpan file sumber kami. Untuk menggunakan Gulp dalam proyek, kita perlu menginstalnya melalui npm dan menyimpannya sebagai ketergantungan pengembang untuk proyek tersebut. Kami juga ingin memulai dengan plugin concat
untuk Gulp, yang memungkinkan kami untuk menggabungkan beberapa file menjadi satu.
Untuk menginstal dua item ini, kita akan menjalankan perintah berikut:
npm install --save-dev gulp gulp-concat
Selanjutnya, kita ingin mulai menulis konten gulpfile.js.
var gulp = require('gulp'); var concat = require('gulp-concat'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(gulp.dest('public/build/js')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(gulp.dest('public/build/css')); }); gulp.task('default', ['pack-js', 'pack-css']);
Di sini, kami memuat pustaka gulp dan plugin concat-nya. Kami kemudian mendefinisikan tiga tugas.
Tugas pertama ( pack-js
) mendefinisikan prosedur untuk mengompresi beberapa file sumber JavaScript menjadi satu bundel. Kami mencantumkan file sumber, yang akan dibulatkan, dibaca, dan digabungkan dalam urutan yang ditentukan. Kami menyalurkannya ke plugin concat untuk mendapatkan satu file terakhir bernama bundle.js
. Terakhir, kami memberi tahu gulp untuk menulis file ke public/build/js
.
Tugas kedua ( pack-css
) melakukan hal yang sama seperti di atas, tetapi untuk stylesheet CSS. Ini memberitahu Gulp untuk menyimpan output gabungan sebagai stylesheet.css
di public/build/css
.
Tugas ketiga ( default
) adalah tugas yang dijalankan Gulp saat kita menjalankannya tanpa argumen. Pada parameter kedua, kami meneruskan daftar tugas lain untuk dieksekusi saat tugas default dijalankan.
Mari kita tempel kode ini ke gulpfile.js menggunakan editor kode sumber apa pun yang biasa kita gunakan, lalu simpan file ke root aplikasi.
Selanjutnya, kita akan membuka baris perintah dan menjalankan:
gulp
Jika kita melihat file kita setelah menjalankan perintah ini, kita akan menemukan dua file baru: public/build/js/bundle.js
dan public/build/css/stylesheet.css
. Mereka adalah gabungan dari file sumber kami, yang memecahkan sebagian dari masalah aslinya. Namun, mereka tidak diperkecil, dan belum ada penghilang cache. Mari tambahkan minifikasi otomatis.
Mengoptimalkan Aset yang Dibangun
Kami akan membutuhkan dua plugin baru. Untuk menambahkannya, kita akan menjalankan perintah berikut:
npm install --save-dev gulp-clean-css gulp-minify
Plugin pertama untuk mengecilkan CSS, dan yang kedua untuk mengecilkan JavaScript. Yang pertama menggunakan paket clean-css, dan yang kedua menggunakan paket UglifyJS2. Kami akan memuat dua paket ini di gulpfile.js kami terlebih dahulu:
var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css');
Kami kemudian perlu menggunakannya dalam tugas kami tepat sebelum kami menulis output ke disk:
.pipe(minify()) .pipe(cleanCss())
gulpfile.js sekarang akan terlihat seperti ini:
var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify()) .pipe(gulp.dest('public/build/js')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(gulp.dest('public/build/css')); }); gulp.task('default', ['pack-js', 'pack-css']);
Ayo lari gulp lagi. Kita akan melihat bahwa file stylesheet.css
disimpan dalam format yang diperkecil, dan file bundle.js
masih disimpan apa adanya. Kami akan melihat bahwa kami sekarang juga memiliki bundle-min.js, yang diperkecil. Kami hanya menginginkan file yang diperkecil, dan kami ingin disimpan sebagai bundle.js
, jadi kami akan memodifikasi kode kami dengan parameter tambahan:

.pipe(minify({ ext:{ min:'.js' }, noSource: true }))
Sesuai dengan dokumentasi plugin gulp-minify (https://www.npmjs.com/package/gulp-minify), ini akan menetapkan nama yang diinginkan untuk versi yang diperkecil, dan memberi tahu plugin untuk tidak membuat versi yang berisi sumber aslinya. Jika kita menghapus konten direktori build dan menjalankan gulp dari baris perintah lagi, kita hanya akan mendapatkan dua file yang diperkecil. Kami baru saja selesai mengimplementasikan fase minifikasi dari proses pembangunan kami.
Penghancur Cache
Selanjutnya, kita ingin menambahkan penghilang cache, dan kita perlu menginstal plugin untuk itu:
npm install --save-dev gulp-rev
Dan memerlukannya di file tegukan kami:
var rev = require('gulp-rev');
Menggunakan plugin ini agak rumit. Kita harus menyalurkan output yang diperkecil melalui plugin terlebih dahulu. Kemudian, kita harus memanggil plugin lagi setelah kita menulis hasilnya ke disk. Plugin mengganti nama file sehingga diberi tag dengan hash unik, dan juga membuat file manifes. File manifes adalah peta yang dapat digunakan oleh aplikasi kita untuk menentukan nama file terbaru yang harus kita rujuk dalam kode HTML kita. Setelah kita memodifikasi file gulp, seharusnya terlihat seperti ini:
var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); var rev = require('gulp-rev'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify({ ext:{ min:'.js' }, noSource: true })) .pipe(rev()) .pipe(gulp.dest('public/build/js')) .pipe(rev.manifest()) .pipe(gulp.dest('public/build')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(rev()) .pipe(gulp.dest('public/build/css')) .pipe(rev.manifest()) .pipe(gulp.dest('public/build')); }); gulp.task('default', ['pack-js', 'pack-css']);
Mari kita hapus isi direktori build kita dan jalankan gulp lagi. Kami akan menemukan bahwa kami sekarang memiliki dua file dengan tagar yang ditempelkan ke masing-masing nama file, dan manifest.json disimpan ke public/build
. Jika kita membuka file manifes, kita akan melihat bahwa file tersebut hanya memiliki referensi ke salah satu file yang diperkecil dan diberi tag. Apa yang terjadi adalah bahwa setiap tugas menulis file manifes yang terpisah, dan salah satunya berakhir dengan menimpa yang lain. Kita perlu memodifikasi tugas dengan parameter tambahan yang akan memberitahu mereka untuk mencari file manifes yang ada dan menggabungkan data baru ke dalamnya jika ada. Sintaks untuk itu agak rumit, jadi mari kita lihat seperti apa kodenya dan kemudian membahasnya:
var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); var rev = require('gulp-rev'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify({ ext:{ min:'.js' }, noSource: true })) .pipe(rev()) .pipe(gulp.dest('public/build/js')) .pipe(rev.manifest('public/build/rev-manifest.json', { merge: true })) .pipe(gulp.dest('')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(rev()) .pipe(gulp.dest('public/build/css')) .pipe(rev.manifest('public/build/rev-manifest.json', { merge: true })) .pipe(gulp.dest('')); }); gulp.task('default', ['pack-js', 'pack-css']);
Kami menyalurkan output ke rev.manifest()
terlebih dahulu. Ini membuat file yang diberi tag alih-alih file yang kita miliki sebelumnya. Kami menyediakan jalur yang diinginkan dari rev-manifest.json
kami, dan memberi tahu rev.manifest()
untuk bergabung ke dalam file yang ada, jika ada. Kemudian kami meminta gulp untuk menulis manifes ke direktori saat ini, yang pada saat itu akan menjadi public/build. Masalah jalur ini disebabkan oleh bug yang dibahas lebih detail di GitHub.
Kami sekarang memiliki minifikasi otomatis, file yang diberi tag, dan file manifes. Semua ini akan memungkinkan kami untuk mengirimkan file lebih cepat ke pengguna, dan menghancurkan cache mereka setiap kali kami melakukan modifikasi. Hanya ada dua masalah yang tersisa.
Masalah pertama adalah jika kita membuat modifikasi pada file sumber kita, kita akan mendapatkan file yang baru ditandai, tetapi yang lama juga akan tetap ada. Kami memerlukan beberapa cara untuk menghapus file lama yang diperkecil secara otomatis. Mari kita selesaikan masalah ini menggunakan plugin yang memungkinkan kita untuk menghapus file:
npm install --save-dev del
Kami akan memerlukannya dalam kode kami dan mendefinisikan dua tugas baru, satu untuk setiap jenis file sumber:
var del = require('del'); gulp.task('clean-js', function () { return del([ 'public/build/js/*.js' ]); }); gulp.task('clean-css', function () { return del([ 'public/build/css/*.css' ]); });
Kami kemudian akan memastikan bahwa tugas baru selesai berjalan sebelum dua tugas utama kami:
gulp.task('pack-js', ['clean-js'], function () { gulp.task('pack-css', ['clean-css'], function () {
Jika kita menjalankan gulp
lagi setelah modifikasi ini, kita hanya akan memiliki file yang diperkecil terbaru.
Masalah kedua adalah kita tidak ingin terus berlari setiap kali kita melakukan perubahan. Untuk mengatasi ini, kita perlu mendefinisikan tugas pengamat:
gulp.task('watch', function() { gulp.watch('assets/js/**/*.js', ['pack-js']); gulp.watch('assets/css/**/*.css', ['pack-css']); });
Kami juga akan mengubah definisi tugas default kami:
gulp.task('default', ['watch']);
Jika sekarang kita menjalankan gulp dari baris perintah, kita akan menemukan bahwa itu tidak lagi membangun apa pun berdasarkan pemanggilan. Ini karena sekarang memanggil tugas pengamat yang akan mengawasi file sumber kami untuk setiap perubahan, dan membangun hanya ketika mendeteksi perubahan. Jika kami mencoba mengubah file sumber kami dan kemudian melihat konsol kami lagi, kami akan melihat bahwa tugas pack-js
dan pack-css
berjalan secara otomatis bersama dengan dependensinya.
Sekarang, yang harus kita lakukan adalah memuat file manifest.json di aplikasi kita dan mendapatkan nama file yang ditandai dari itu. Bagaimana kami melakukannya tergantung pada bahasa back-end dan tumpukan teknologi kami, dan akan sangat sepele untuk diterapkan, jadi kami tidak akan membahasnya secara mendetail. Namun, ide umumnya adalah bahwa kita dapat memuat manifes ke dalam array atau objek, lalu mendefinisikan fungsi pembantu yang memungkinkan kita memanggil aset berversi dari template kita dengan cara yang mirip dengan berikut ini:
gulp('bundle.js')
Setelah kami melakukannya, kami tidak perlu khawatir tentang perubahan tag di nama file kami lagi, dan kami akan dapat fokus pada penulisan kode berkualitas tinggi.
Kode sumber terakhir untuk artikel ini, bersama dengan beberapa contoh aset, dapat ditemukan di repositori GitHub ini.
Kesimpulan
Pada artikel ini, kami membahas cara menerapkan otomatisasi berbasis Gulp untuk proses pembuatan kami. Saya harap ini terbukti bermanfaat bagi Anda dan memungkinkan Anda mengembangkan proses pembuatan yang lebih canggih dalam aplikasi Anda sendiri.
Harap diingat bahwa Gulp hanyalah salah satu alat yang dapat digunakan untuk tujuan ini, dan masih banyak alat lainnya seperti Grunt, Browserify, dan Webpack. Mereka berbeda dalam tujuan mereka dan dalam lingkup masalah yang dapat mereka pecahkan. Beberapa dapat memecahkan masalah yang Gulp tidak bisa, seperti menggabungkan modul JavaScript dengan dependensi yang dapat dimuat sesuai permintaan. Ini disebut sebagai "pemecahan kode", dan ini merupakan peningkatan dari gagasan menyajikan satu file besar dengan semua bagian program kami di setiap halaman. Alat-alat ini cukup canggih tetapi mungkin akan dibahas di masa depan. Dalam posting berikut, kita akan membahas cara mengotomatiskan penerapan aplikasi kita.