Seperti yang mungkin Anda ketahui, Chrome DevTools adalah aplikasi web yang ditulis menggunakan HTML, CSS, dan JavaScript. Selama bertahun-tahun, DevTools menjadi lebih kaya fitur, lebih cerdas, dan lebih memahami platform web yang lebih luas. Meskipun DevTools telah berkembang selama bertahun-tahun, arsitekturnya sebagian besar menyerupai arsitektur asli saat masih menjadi bagian dari WebKit.
Postingan ini adalah bagian dari serangkaian postingan blog yang menjelaskan perubahan yang kami lakukan pada arsitektur DevTools dan cara membangunnya. Kami akan menjelaskan cara kerja DevTools secara historis, manfaat dan batasan yang ada, serta tindakan yang telah kami lakukan untuk mengurangi batasan ini. Oleh karena itu, mari kita pelajari sistem modul, cara memuat kode, dan cara kita akhirnya menggunakan modul JavaScript.
Pada awalnya, tidak ada
Meskipun lanskap frontend saat ini memiliki berbagai sistem modul dengan alat yang dibuat di sekitarnya, serta format modul JavaScript yang kini distandarisasi, tidak ada satu pun dari hal ini yang ada saat DevTools pertama kali dibuat. DevTools dibuat berdasarkan kode yang awalnya dikirimkan di WebKit lebih dari 12 tahun yang lalu.
Sebutan pertama sistem modul di DevTools berasal dari tahun 2012: pengenalan daftar modul dengan daftar sumber terkait.
Ini adalah bagian dari infrastruktur Python yang digunakan saat itu untuk mengompilasi dan mem-build DevTools.
Perubahan lanjutan telah mengekstrak semua modul ke dalam file frontend_modules.json
terpisah (commit) pada tahun 2013, lalu menjadi file module.json
terpisah (commit) pada tahun 2014.
Contoh file module.json
:
{
"dependencies": [
"common"
],
"scripts": [
"StylePane.js",
"ElementsPanel.js"
]
}
Sejak 2014, pola module.json
telah digunakan di DevTools untuk menentukan modul dan file sumbernya.
Sementara itu, ekosistem web berkembang dengan cepat dan beberapa format modul dibuat, termasuk UMD, CommonJS, dan modul JavaScript yang akhirnya distandarisasi.
Namun, DevTools tetap menggunakan format module.json
.
Meskipun DevTools tetap berfungsi, ada beberapa kekurangan menggunakan sistem modul yang unik dan tidak standar:
- Format
module.json
memerlukan alat build kustom, mirip dengan alat penggabungan modern. - Tidak ada integrasi IDE, yang memerlukan alat kustom untuk membuat file yang dapat dipahami IDE modern (skrip asli untuk membuat file jsconfig.json untuk VS Code).
- Fungsi, class, dan objek semuanya dimasukkan ke dalam cakupan global untuk memungkinkan aktivitas berbagi antar-modul.
- File bergantung pada urutan, yang berarti urutan
sources
yang tercantum sangat penting. Tidak ada jaminan bahwa kode yang Anda andalkan akan dimuat, selain bahwa manusia telah memverifikasinya.
Secara keseluruhan, saat mengevaluasi status sistem modul saat ini di DevTools dan format modul lainnya (yang lebih banyak digunakan), kami menyimpulkan bahwa pola module.json
menimbulkan lebih banyak masalah daripada yang dipecahkan dan sudah waktunya untuk merencanakan peralihan dari pola tersebut.
Manfaat dari standar
Dari sistem modul yang ada, kami memilih modul JavaScript sebagai modul yang akan dimigrasikan. Pada saat keputusan tersebut, modul JavaScript masih dikirimkan di balik tanda di Node.js dan sejumlah besar paket yang tersedia di NPM tidak memiliki paket modul JavaScript yang dapat kita gunakan. Meskipun demikian, kami menyimpulkan bahwa modul JavaScript adalah opsi terbaik.
Manfaat utama modul JavaScript adalah format modul standar untuk JavaScript.
Saat mencantumkan kelemahan module.json
(lihat di atas), kami menyadari bahwa hampir semuanya terkait dengan penggunaan format modul yang unik dan tidak standar.
Memilih format modul yang tidak standar berarti kita harus meluangkan waktu untuk membuat integrasi dengan alat build dan alat yang digunakan pengelola.
Integrasi ini sering kali rapuh dan tidak memiliki dukungan untuk fitur, sehingga memerlukan waktu pemeliharaan tambahan, terkadang menyebabkan bug halus yang pada akhirnya akan dikirim ke pengguna.
Karena modul JavaScript adalah standar, artinya IDE seperti VS Code, pemeriksa jenis seperti Closure Compiler/TypeScript, dan alat build seperti Rollup/minifier akan dapat memahami kode sumber yang kita tulis.
Selain itu, saat bergabung dengan tim DevTools, pengelola baru tidak perlu menghabiskan waktu untuk mempelajari format module.json
eksklusif, sedangkan mereka (kemungkinan) sudah terbiasa dengan modul JavaScript.
Tentu saja, saat DevTools pertama kali dibuat, tidak ada manfaat di atas. Perlu waktu bertahun-tahun untuk bekerja di grup standar, implementasi runtime, dan developer yang menggunakan modul JavaScript untuk memberikan masukan agar dapat mencapai titik seperti sekarang. Namun, ketika modul JavaScript tersedia, kami memiliki pilihan: tetap mempertahankan format kami sendiri, atau berinvestasi pada migrasi ke format yang baru.
Biaya perangkat baru
Meskipun modul JavaScript memiliki banyak manfaat yang ingin kita gunakan, kita tetap berada di dunia module.json
non-standar.
Untuk mendapatkan manfaat dari modul JavaScript, kami harus melakukan investasi yang signifikan dalam membersihkan utang teknis, melakukan migrasi yang berpotensi merusak fitur dan menyebabkan bug regresi.
Pada tahap ini, pertanyaannya bukan "Apakah kita ingin menggunakan modul JavaScript?", tetapi "Berapa mahal biaya untuk dapat menggunakan modul JavaScript?". Di sini, kami harus menyeimbangkan risiko merusak pengguna dengan regresi, biaya yang dihabiskan engineer untuk bermigrasi (sejumlah besar) waktu, dan kondisi buruk sementara yang akan kami hadapi.
Poin terakhir ternyata sangat penting. Meskipun secara teori kita dapat mengakses modul JavaScript, selama migrasi, kita akan mendapatkan kode yang harus mempertimbangkan baik modul module.json
maupun JavaScript.
Hal ini tidak hanya sulit dicapai secara teknis, tetapi juga berarti bahwa semua engineer yang bekerja di DevTools harus mengetahui cara bekerja di lingkungan ini.
Mereka harus terus bertanya pada diri sendiri "Untuk bagian codebase ini, apakah module.json
atau modul JavaScript dan bagaimana cara membuat perubahan?".
Cuplikan: Biaya tersembunyi untuk memandu rekan-rekan pengelola kami melalui migrasi lebih besar dari yang kami perkirakan.
Setelah analisis biaya, kami menyimpulkan bahwa masih ada manfaat untuk bermigrasi ke modul JavaScript. Oleh karena itu, sasaran utama kami adalah sebagai berikut:
- Pastikan penggunaan modul JavaScript mendapatkan manfaat sebanyak mungkin.
- Pastikan integrasi dengan sistem berbasis
module.json
yang ada aman dan tidak menyebabkan dampak negatif bagi pengguna (bug regresi, keluhan pengguna). - Membimbing semua pengelola DevTools melalui migrasi, terutama dengan pemeriksaan dan keseimbangan bawaan untuk mencegah kesalahan yang tidak disengaja.
Spreadsheet, transformasi, dan utang teknis
Meskipun tujuannya jelas, batasan yang diberlakukan oleh format module.json
terbukti sulit diatasi.
Diperlukan beberapa iterasi, prototipe, dan perubahan arsitektur sebelum kami mengembangkan solusi yang sesuai.
Kami menulis dokumen desain dengan strategi migrasi yang kami pilih.
Dokumen desain juga mencantumkan estimasi waktu awal kami: 2-4 minggu.
Spoiler alert: bagian paling intensif dari migrasi ini memerlukan waktu 4 bulan dan dari awal hingga akhir memerlukan waktu 7 bulan.
Namun, rencana awal tetap bertahan: kita akan mengajarkan runtime DevTools untuk memuat semua file yang tercantum dalam array scripts
dalam file module.json
menggunakan cara lama, sementara semua file dalam tercantum dalam array modules
dengan impor dinamis modul JavaScript.
Setiap file yang akan berada di array modules
akan dapat menggunakan impor/ekspor ES.
Selain itu, kami akan melakukan migrasi dalam 2 fase (akhirnya kami membagi fase terakhir menjadi 2 sub-fase, lihat di bawah): fase export
- dan import
.
Status modul mana yang akan berada di fase mana dilacak dalam spreadsheet besar:
Cuplikan sheet progres tersedia untuk publik di sini.
export
-phase
Fase pertama adalah menambahkan pernyataan export
untuk semua simbol yang seharusnya dibagikan di antara modul/file.
Transformasi akan diotomatiskan, dengan menjalankan skrip per folder.
Dengan simbol berikut akan ada di dunia module.json
:
Module.File1.exported = function() {
console.log('exported');
Module.File1.localFunctionInFile();
};
Module.File1.localFunctionInFile = function() {
console.log('Local');
};
(Di sini, Module
adalah nama modul dan File1
adalah nama file. Dalam sourcetree kita, itu akan menjadi front_end/module/file1.js
.)
Ini akan diubah menjadi:
export function exported() {
console.log('exported');
Module.File1.localFunctionInFile();
}
export function localFunctionInFile() {
console.log('Local');
}
/** Legacy export object */
Module.File1 = {
exported,
localFunctionInFile,
};
Awalnya, rencana kami adalah menulis ulang impor file yang sama selama fase ini juga.
Misalnya, dalam contoh di atas, kita akan menulis ulang Module.File1.localFunctionInFile
menjadi localFunctionInFile
.
Namun, kami menyadari bahwa akan lebih mudah untuk mengotomatiskan dan lebih aman untuk menerapkan jika kami memisahkan kedua transformasi ini.
Oleh karena itu, "migrasikan semua simbol dalam file yang sama" akan menjadi sub-fase kedua dari fase import
.
Karena menambahkan kata kunci export
dalam file akan mengubah file dari "skrip" menjadi "modul", banyak infrastruktur DevTools yang harus diperbarui.
Ini mencakup runtime (dengan impor dinamis), tetapi juga alat seperti ESLint
untuk dijalankan dalam mode modul.
Satu penemuan yang kami buat saat mengatasi masalah ini adalah pengujian kami berjalan dalam mode "sloppy".
Karena modul JavaScript menyiratkan bahwa file berjalan dalam mode "use strict"
, hal ini juga akan memengaruhi pengujian kita.
Ternyata, banyak pengujian yang tidak berat mengandalkan kelambatan ini, termasuk pengujian yang menggunakan pernyataan with
🎁.
Pada akhirnya, memperbarui folder pertama untuk menyertakan pernyataan export
memerlukan waktu sekitar seminggu dan beberapa upaya dengan reland.
import
-phase
Setelah semua simbol diekspor menggunakan pernyataan export
dan tetap berada dalam cakupan global (lama), kami harus memperbarui semua referensi ke simbol lintas file untuk menggunakan impor ES.
Sasaran akhirnya adalah menghapus semua "objek ekspor lama", sehingga membersihkan cakupan global.
Transformasi akan otomatis dilakukan, dengan menjalankan skrip per folder.
Misalnya, untuk simbol berikut yang ada di dunia module.json
:
Module.File1.exported();
AnotherModule.AnotherFile.alsoExported();
SameModule.AnotherFile.moduleScoped();
Nilai tersebut akan diubah menjadi:
import * as Module from '../module/Module.js';
import * as AnotherModule from '../another_module/AnotherModule.js';
import {moduleScoped} from './AnotherFile.js';
Module.File1.exported();
AnotherModule.AnotherFile.alsoExported();
moduleScoped();
Namun, ada beberapa hal yang perlu diperhatikan dengan pendekatan ini:
- Tidak semua simbol diberi nama
Module.File.symbolName
. Beberapa simbol hanya diberi namaModule.File
atau bahkanModule.CompletelyDifferentName
. Inkonsistensi ini berarti kita harus membuat pemetaan internal dari objek global lama ke objek baru yang diimpor. - Terkadang akan terjadi konflik antara nama moduleScoped.
Yang paling penting, kami menggunakan pola untuk mendeklarasikan jenis
Events
tertentu, dengan setiap simbol diberi namaEvents
saja. Artinya, jika Anda memproses beberapa jenis peristiwa yang dideklarasikan dalam file berbeda, nama yang bentrok akan terjadi di pernyataanimport
untukEvents
tersebut. - Ternyata, ada dependensi sirkular di antara file.
Hal ini tidak masalah dalam konteks cakupan global, karena penggunaan simbol dilakukan setelah semua kode dimuat.
Namun, jika Anda memerlukan
import
, dependensi sirkular akan dibuat eksplisit. Hal ini tidak langsung menjadi masalah, kecuali jika Anda memiliki panggilan fungsi efek samping dalam kode cakupan global, yang juga dimiliki DevTools. Secara keseluruhan, diperlukan beberapa operasi dan pemfaktoran ulang untuk membuat transformasi aman.
Dunia baru dengan modul JavaScript
Pada Februari 2020, 6 bulan setelah dimulai pada September 2019, pembersihan terakhir dilakukan di folder ui/
.
Hal ini menandai akhir tidak resmi dari migrasi.
Setelah semuanya selesai, kami secara resmi menandai migrasi tersebut sebagai selesai pada 5 Maret 2020. 🎉
Sekarang, semua modul di DevTools menggunakan modul JavaScript untuk membagikan kode.
Kita masih menempatkan beberapa simbol pada cakupan global (dalam file module-legacy.js
) untuk pengujian lama atau untuk berintegrasi dengan bagian lain dari arsitektur DevTools.
Hal tersebut akan dihapus seiring waktu, tetapi kami tidak menganggapnya sebagai penghalang untuk pengembangan di masa mendatang.
Kami juga memiliki panduan gaya untuk penggunaan modul JavaScript.
Statistik
Estimasi konservatif untuk jumlah CL (singkatan dari daftar perubahan - istilah yang digunakan di Gerrit yang mewakili perubahan - mirip dengan permintaan pull GitHub) yang terlibat dalam migrasi ini adalah sekitar 250 CL, sebagian besar dilakukan oleh 2 engineer. Kami tidak memiliki statistik definitif terkait ukuran perubahan yang dilakukan, tetapi perkiraan konservatif baris berubah (dihitung sebagai jumlah perbedaan absolut antara penyisipan dan penghapusan untuk setiap CL) kira-kira 30.000 (~20% dari semua kode frontend DevTools).
File pertama yang menggunakan export
dikirimkan di Chrome 79, dirilis ke stabil pada Desember 2019.
Perubahan terakhir untuk bermigrasi ke import
dikirimkan di Chrome 83, yang dirilis ke stabil pada Mei 2020.
Kami mengetahui satu regresi yang disertakan ke Chrome stabil dan yang diperkenalkan sebagai bagian dari migrasi ini.
Penyelesaian otomatis cuplikan di menu perintah rusak karena ekspor default
yang tidak relevan.
Kami mengalami beberapa regresi lainnya, tetapi suite pengujian otomatis dan pengguna Chrome Canary melaporkannya dan kami memperbaikinya sebelum regresi tersebut dapat menjangkau pengguna Chrome stabil.
Anda dapat melihat keseluruhan perjalanan (tidak semua CL disertakan pada bug ini, tetapi sebagian besar) dicatat di crbug.com/1006759.
Yang kami pelajari
- Keputusan yang dibuat di masa lalu dapat berdampak jangka panjang pada project Anda. Meskipun modul JavaScript (dan format modul lainnya) telah tersedia selama beberapa waktu, DevTools tidak dapat membenarkan migrasi tersebut. Menentukan kapan harus dan kapan tidak harus melakukan migrasi itu sulit dan didasarkan pada perkiraan yang tepat.
- Estimasi waktu awal kami adalah dalam minggu, bukan bulan. Hal ini sebagian besar berasal dari fakta bahwa kami menemukan lebih banyak masalah tak terduga daripada yang kami perkirakan dalam analisis biaya awal. Meskipun rencana migrasinya solid, utang teknis (lebih sering daripada yang kita inginkan) menjadi hambatan.
- Migrasi modul JavaScript mencakup pembersihan utang teknis dalam jumlah besar (tampaknya tidak terkait). Migrasi ke format modul standar modern memungkinkan kami menyesuaikan praktik terbaik coding dengan pengembangan web modern. Misalnya, kita dapat mengganti pemaket Python kustom dengan konfigurasi Rollup minimal.
- Meskipun berdampak besar pada codebase kami (~20% kode berubah), sangat sedikit regresi yang dilaporkan. Meskipun kami mengalami banyak masalah saat memigrasikan beberapa file pertama, setelah beberapa saat kami memiliki alur kerja yang solid dan sebagian otomatis. Artinya, dampak negatif pengguna untuk pengguna stabil kami minimal untuk migrasi ini.
- Mengajarkan seluk-beluk migrasi tertentu kepada sesama pengelola sulit dan terkadang tidak mungkin. Migrasi dalam skala ini sulit diikuti dan memerlukan banyak pengetahuan domain. Mentransfer pengetahuan domain tersebut kepada orang lain yang bekerja di codebase yang sama tidak diinginkan untuk pekerjaan yang mereka lakukan. Mengetahui apa yang harus dibagikan dan detail apa yang tidak boleh dibagikan adalah seni, tetapi sangat penting. Oleh karena itu, penting untuk mengurangi jumlah migrasi besar, atau setidaknya tidak melakukannya secara bersamaan.
Mendownload saluran pratinjau
Sebaiknya gunakan Chrome Canary, Dev, atau Beta sebagai browser pengembangan default Anda. Saluran pratinjau ini memberi Anda akses ke fitur DevTools terbaru, memungkinkan Anda menguji API platform web canggih, dan membantu Anda menemukan masalah di situs sebelum pengguna melakukannya.
Hubungi tim Chrome DevTools
Gunakan opsi berikut untuk membahas fitur baru, update, atau hal lain yang terkait dengan DevTools.
- Kirim masukan dan permintaan fitur kepada kami di crbug.com.
- Laporkan masalah DevTools menggunakan Opsi lainnya > Bantuan > Laporkan masalah DevTools di DevTools.
- Tweet ke @ChromeDevTools.
- Berikan komentar di video YouTube Yang baru di DevTools atau video YouTube Tips DevTools.