Petunjuk dan konteks
Sebuah tim perkakas frontend memelihara aplikasi TypeScript berukuran besar. Sebuah modul analitik mendaftarkan listener global dan membaca konfigurasi lingkungan selama proses impor, yang memperlambat startup. Tim ingin menggunakan import defer dari TypeScript 5.9 untuk menunda efek samping tersebut, sementara artefaknya harus tetap berjalan di runtime lawas yang tidak memahami sintaks tersebut. Jelaskan pemuatan (loading), evaluasi (evaluation), pemicu akses, dan gerbang migrasi.
Catatan rilis TypeScript 5.9 menyatakan bahwa import defer hanya mengizinkan namespace import. Modul dan dependensinya dapat dimuat terlebih dahulu, tetapi kode modul dievaluasi ketika anggota namespace diakses. TypeScript tidak melakukan downlevel sintaks, sehingga pelestarian langsung ditujukan untuk mode modul preserve atau esnext.
Hal yang diuji oleh pewawancara
Pewawancara mencari pemahaman mengenai perbedaan antara pemuatan dan evaluasi, impor statis dan impor dinamis, serta pemahaman tentang efek samping tingkat atas (top-level). Jawaban yang kuat juga mencakup bundler, browser lawas, SSR, preloading, isolasi pengujian, dan rollback alih-alih memperlakukan import defer hanya sebagai penulisan singkat dari lazy loading.
Pertanyaan klarifikasi awal
- Apakah runtime target dan bundler memahami
import defersecara native? - Bisakah efek samping modul dipindahkan dengan aman ke tahap berikutnya, atau harus terjadi selama startup?
- Akses anggota mana yang memicu evaluasi, dan apakah ada pembacaan top-level yang tersembunyi?
- Apakah SSR, hidrasi klien, preloading, dan pengujian memerlukan urutan yang deterministik?
- Bisakah migrasi yang gagal beralih kembali ke impor statis normal atau
import()dinamis?
Jawaban 30 detik
"Saya akan mendefinisikan import defer sebagai evaluasi yang ditangguhkan (deferred evaluation), bukan pemuatan dinamis. TypeScript 5.9 memerlukan namespace import; sumber daya dapat dimuat terlebih dahulu, dan akses anggota namespace pertama akan mengevaluasi modul. Kompiler tidak menyediakan transformasi runtime lawas. Saya akan mengaudit efek tingkat atas dan urutan SSR, menjalankan eksperimen kecil dengan bundler dan runtime pendukung, serta mempertahankan sakelar ke impor normal atau import() dinamis. Saya hanya akan memperluas penggunaannya setelah pengujian build, hidrasi, performa, dan efek samping berhasil lolos."
Pembahasan mendalam langkah demi langkah
1. Tetapkan semantik dan versi
Catat versi TypeScript 5.9, mode modul, dan status proposal TC39. Pemuatan dan evaluasi adalah dua hal yang terpisah: import defer menunda evaluasi, bukan selalu menunda permintaan jaringan. Pasang instrumentasi pada kedua penanda waktu alih-alih menyimpulkan eksekusi dari grafik waterfall startup.
2. Nyatakan batasan sintaksis
Hanya namespace import yang dapat menunda evaluasi; impor default dan named tidak valid. Mengakses properti namespace memicu evaluasi, menjadikan pembacaan tersebut sebagai batas eksekusi yang dapat diamati.
import defer * as analytics from "./analytics.js";
// The module is loaded, but top-level registration has not run.
export function openPanel() {
analytics.start(); // First member access triggers evaluation.
}Jika pemanggil memerlukan pendaftaran global sebelum analytics.start, evaluasi yang ditangguhkan akan mengubah perilaku. Pertahankan impor normal atau pindahkan inisialisasi ke dalam fungsi eksplisit.
3. Bandingkan dengan dynamic import
import() dinamis biasanya mengembalikan Promise dan menempatkan pemuatan serta evaluasi dalam alur asinkron. import defer mempertahankan hubungan modul statis, dapat memuat sumber daya lebih awal, dan menunda eksekusi tingkat atas. Oleh karena itu, propagasi kesalahan, preloading, pemisahan kode (code splitting), SSR, dan waktu pengujian berbeda; menyamakan keduanya menghasilkan perbandingan performa yang tidak valid.
4. Audit efek dan grafik akses
Buat daftar efek tingkat atas: event listener, pendaftaran singleton, pembacaan lingkungan, polyfill, inisialisasi telemetri, dan pengisian cache. Lacak setiap akses anggota namespace, termasuk hidrasi, prefetch rute, dan pengaturan pengujian. Jika urutan berpengaruh, pindahkan efek ke dalam initialize() eksplisit sehingga pemanggil dapat memilih waktu eksekusi.
5. Tangani kompatibilitas build dan runtime
TypeScript tidak melakukan downlevel pada import defer. Untuk runtime lawas, verifikasi apakah bundler dapat mentransformasikan atau menolaknya; jika tidak, pertahankan impor normal atau implementasi import() dinamis. CI harus mencakup browser target, Node SSR, server dev, bundling produksi, source maps, code splitting, dan error boundaries.
6. Tetapkan gerbang peluncuran dan rollback
Gunakan feature flag untuk jalur deferred, statis, dan dinamis. Catat latensi interaksi pertama, waktu evaluasi, inisialisasi duplikat, dan kesalahan hidrasi. Nonaktifkan flag jika terjadi perubahan urutan, kesalahan sintaks runtime lawas, atau regresi metrik; kembali ke jalur impor yang stabil selama proposal atau rantai alat masih berkembang.
Jawaban model
Saya pertama-tama akan memverifikasi matriks dukungan untuk TypeScript 5.9, bundler, dan setiap runtime target, kemudian mendata efek tingkat atas. import defer mendukung namespace import, dapat memuat modul sebelum mengevaluasinya, dan mengevaluasi pada akses anggota pertama; TypeScript tidak menyediakan transformasi downlevel. Saya akan mengukur pemuatan dan evaluasi secara terpisah, menguji SSR, hidrasi, browser lawas, bundling, isolasi, dan inisialisasi berulang, serta mempertahankan fallback impor normal berbasis feature flag. Saya hanya akan bermigrasi setelah urutan, artefak, dan data performa tetap stabil.
Kesalahan umum
- Memperlakukan
import defersebagaiimport()dinamis → melewatkan dependensi statis dan pengaturan waktu Promise → ukur pemuatan, evaluasi, dan kesalahan secara terpisah. - Menggunakan impor default atau named → melanggar batasan sintaks TypeScript 5.9 → gunakan namespace import dan picu saat akses.
- Mengasumsikan TypeScript menulis ulang kodenya → runtime lawas dapat gagal karena kesalahan sintaks → verifikasi dukungan bundler dan pertahankan fallback.
- Mengabaikan efek tingkat atas → urutan listener, polyfill, atau telemetri berubah → buat inisialisasi eksplisit atau pertahankan impor normal.
- Hanya melihat jaringan startup → pemuatan awal tidak membuktikan eksekusi awal → catat kedua penanda waktu.
Pertanyaan lanjutan
Mengapa namespace import diperlukan?
Objek namespace menyediakan batas akses properti yang jelas. Impor default dan named mengekspos binding konkret selama penyiapan, sehingga tidak dapat mempertahankan aturan seragam "evaluasi pada akses anggota pertama".
Apakah ini sama dengan code splitting?
Bukan. Code splitting mengontrol bagaimana sumber daya dikemas dan dimuat; import defer terutama mengubah kapan kode modul dievaluasi. Sumber daya tersebut mungkin sudah dimuat atau di-prefetch.
Bagaimana SSR harus menanganinya?
Jaga agar urutan evaluasi server dan klien tetap kompatibel. Jika server mendaftarkan status global sementara klien belum, hidrasi dapat mengalami divergensi. Pertahankan impor server statis jika diperlukan, atau buat inisialisasi eksplisit dan dapat diulang.
Bagaimana cara menguji efek samping yang terjadi sekali?
Dalam proses pengujian yang terisolasi, hitung inisialisasi modul pada skenario tanpa akses, akses pertama, akses berulang, akses bersamaan (concurrent), dan teardown. Pastikan dengan assertion bahwa singleton dan listener tidak terdaftar dua kali.
Kapan sebaiknya Anda menghindarinya?
Hindari jika runtime atau bundler tidak memiliki dukungan, efek startup tidak dapat dipindahkan, urutan SSR tidak dapat diubah, atau peningkatan performa tidak dapat direproduksi. Gunakan impor normal atau import() dinamis sebagai gantinya.