Makluman dan skop
Anda menyelenggara papan pemuka berbilang lajur. Saiz kad ditentukan oleh grid, seretan, bar sisi dan pemuatan fon, jadi ia tidak boleh dianggap sebagai fungsi saiz viewport semata-mata. Di bawah 320px ia memaparkan tajuk padat dan metrik satu lajur; di atas ambang tersebut ia memaparkan susun atur penuh. Tindakan meruntuhkan bar sisi tanpa mengubah saiz tetingkap mesti berkuat kuasa serta-merta.
Soalan ini menguji pengukuran peringkat elemen, pemasaan susun atur pelayar, kitaran hayat React, dan sempadan prestasi. Bahan temu duga front-end awam biasanya merangkumi DOM, CSS, peristiwa pelayar dan prestasi; dokumentasi platform menyediakan tingkah laku pemberitahuan dan gelung ResizeObserver yang tepat untuk soalan susulan yang lebih mendalam.
Perkara yang diuji oleh penemu duga
- Menerangkan bahawa
window.resizemenerangkan perubahan viewport, bukan penyusunan semula grid, pertukaran fon atau perubahan saiz induk. - Memilih
content-boxatauborder-boxdan mentakrifkan maksud ambang tersebut. - Menulis ukuran ke dalam keadaan (state) atau pemboleh ubah CSS tanpa mengubah saiz elemen yang diperhatikan secara segerak.
- Mengendalikan penggantian sasaran, penyahlekapannya (unmounting), nod tersembunyi, pelayar lama dan kad yang banyak.
- Membuktikan kelancaran dengan metrik pengguna, tugas panjang (long tasks) dan surihan susun atur dan bukannya jumlah panggilan balik semata-mata.
Soalan untuk dijelaskan terlebih dahulu
- Adakah ambang itu mengenai lebar content-box, lebar border-box, atau keputusan gaya semata-mata yang boleh dinyatakan oleh pertanyaan bekas (CSS container queries)?
- Patutkah perubahan saiz mencetuskan kerja data, atau hanya menukar persembahan visual?
- Adakah UI mesti mengikuti setiap bingkai (frame), atau adakah satu kemas kini yang digabungkan (coalesced) bagi setiap bingkai sudah mencukupi?
- Apakah tahap sokongan pelayar minimum, dan bolehkah rendering pelayan menyentuh
window? - Adakah terdapat berpuluh-puluh atau beribu-ribu kad, dan bolehkah kad yang kelihatan sahaja diperhatikan?
Jawapan 30 saat
“Mula-mula saya akan menetapkan bahawa ini adalah masalah saiz elemen, bukan masalah saiz viewport. Untuk titik putus khusus gaya, saya lebih suka CSS container queries. Jika JavaScript memerlukan ukuran tersebut, saya akan melampirkan ResizeObserver pada klien, membaca kotak yang ditentukan, menyahduplikasi keadaan ambang, dan menulis pemboleh ubah CSS atau keadaan diskret. Panggilan balik tidak boleh mengubah saiz yang diperhatikan secara segerak; penulisan yang tidak penting boleh dijadualkan untuk bingkai seterusnya, dan pembersihan mesti memutuskan sambungan pemerhati. Untuk kad yang banyak, saya akan mengehadkan set yang diperhatikan dan mengukur INP, tugas panjang, dan kos susun atur. Pelayar lama akan menerima sandaran eksplisit berupa susun atur tetap, container queries, atau pendengar viewport yang dihadkan kadarnya (throttled).”
Jawapan mendalam
Langkah 1: Semak sama ada CSS sudah mencukupi
Jika keperluannya hanyalah “tunjukkan gaya padat di bawah 320px,” container query biasanya lebih mudah dan mengekalkan pengukuran di luar JavaScript. ResizeObserver wajar digunakan apabila saiz memacu pensampelan carta, virtualisasi, pemapar pihak ketiga, atau logik perniagaan yang boleh diperhatikan.
Langkah 2: Tentukan kontrak saiz
ResizeObserverEntry mendedahkan ukuran content box dan border box. Tentukan kotak mana yang mengawal ambang, kemudian seragamkan unit dan pembundaran. Sampel titik terapung (floating-point) tidak semestinya merupakan peristiwa perniagaan; nyahduplikasi peralihan 319.9 ke 320.1 sebagai keadaan titik putus apabila itu adalah kontrak produk.
Langkah 3: Wujudkan kitaran hayat pemerhati
Cipta pemerhati selepas komponen klien dilekapkan, perhatikan nod kad, dan panggil unobserve atau disconnect apabila nod berubah atau komponen dinyahlekapkan. Dalam React, biarkan ref memegang sasaran dan Effect memegang penciptaan serta pembersihan pemerhati supaya rendering biasa tidak menciptanya semula.
const ref = useRef<HTMLDivElement>(null)
const [compact, setCompact] = useState(false)
useEffect(() => {
const node = ref.current
if (!node || !('ResizeObserver' in window)) return
const observer = new ResizeObserver(([entry]) => {
const width = entry.contentRect.width
const next = width < 320
setCompact((current) => (current === next ? current : next))
})
observer.observe(node)
return () => observer.disconnect()
}, [])Langkah 4: Cegah gelung maklum balas panggilan balik
Jika panggilan balik mengubah lebar atau tinggi elemen yang diperhatikan, perubahan itu boleh menjadualkan pemberitahuan lain dan akhirnya menghasilkan ResizeObserver loop completed with undelivered notifications. Pastikan panggilan balik tertumpu pada kontrak saiz, atau jadualkan penulisan visual dengan requestAnimationFrame dan jadikannya idempoten.
Langkah 5: Asingkan pengukuran daripada rendering
Tulis ukuran ke sifat tersuai CSS apabila CSS boleh menguruskan susun atur, atau kekalkan hanya keadaan diskret seperti compact. Jangan masukkan setiap perubahan piksel ke dalam keadaan React semasa penyeretan. Jika nilai berterusan diperlukan, gabungkan pemberitahuan bagi setiap bingkai animasi dan rekod bingkai yang tercicir serta tugas panjang.
Langkah 6: Kendalikan banyak kad dan nod tersembunyi
Bagi ribuan kad, perhatikan nod yang kelihatan sahaja atau biarkan lapisan susun atur mengedarkan ukuran. display: none, panel yang diruntuhkan dan virtualisasi mengubah saiz yang boleh diperhatikan; selepas nod menjadi kelihatan, sahkan bahawa pemberitahuan pertama memulihkan keadaan yang betul. Pemerhati bukanlah gelung pengundian (polling loop).
Langkah 7: Tentukan sempadan sandaran dan pelayan
Rendering pelayan tidak boleh mengakses window. Kesan sokongan di dalam Effect klien. Tanpa ResizeObserver, gunakan susun atur tetap, peraturan CSS media/container, atau pendengar viewport yang di-throttle, dan dokumentasikan tingkah laku peringkat elemen yang tidak dapat disediakan oleh sandaran tersebut.
Langkah 8: Sahkan dengan bukti
Uji tindakan meruntuhkan bar sisi, menyeret, memuatkan fon, penyusunan semula grid, putaran skrin dan zum pelayar. Rakam panggilan balik, susun atur, cat (paint), dan tugas panjang dalam Chrome Performance; gunakan INP atau kelewatan input (input delay) untuk memeriksa tindak balas seretan. Tambahkan ujian untuk amaran gelung konsol dan sahkan bahawa kad yang telah dinyahlekap tidak lagi menerima kemas kini.
Pertukaran dan sempadan
Container queries berbanding ResizeObserver
Container queries sesuai untuk titik putus gaya sahaja dan kekal deklaratif. ResizeObserver sesuai untuk pengiraan JavaScript dan rendering pihak ketiga tetapi memerlukan kawalan kitaran hayat dan prestasi. Sempadannya ialah sama ada ukuran perlu keluar daripada lapisan gaya.
content-box berbanding border-box
Gunakan content-box untuk ambang susun atur kandungan dan border-box untuk kontrak kad luar yang merangkumi padding dan sempadan. Pilihan yang salah akan mengalihkan titik putus; nyatakan pilihan tersebut dalam kontrak dan ujian.
Nilai berterusan berbanding diskret
Lebar berterusan boleh memacu carta tetapi kerap dikemas kini. Titik putus diskret adalah stabil dan lebih mudah diuji. Mulakan dengan kontrak diskret dan tambah kemas kini berterusan hanya dengan belanjawan prestasi yang diukur dan faedah visual yang jelas.
Latihan kegagalan dan evolusi
Kegagalan: hanya mendengar window.resize
Tindakan meruntuhkan bar sisi dan penyusunan semula grid tidak mengubah viewport, jadi kad kekal dalam susun atur yang salah. Perhatikan kad atau bekasnya dan uji perubahan bukan viewport.
Kegagalan: mengubah saiz yang diperhatikan dalam panggilan balik
Mutasi mencetuskan panggilan balik yang lain dan boleh mencipta gelung serta kerja susun atur tambahan. Tulis pemboleh ubah CSS bebas, gunakan keadaan diskret, atau jadualkan kemas kini bingkai seterusnya yang idempoten.
Kegagalan: setState untuk setiap piksel
Penyeretan menghasilkan rendering React frekuensi tinggi dan memburukkan tindak balas input. Nyahduplikasi ambang, gabungkan dengan rAF, perhatikan kad yang kelihatan sahaja, dan sahkan kosnya dalam Performance.
Kesilapan biasa dan susulan
Kesilapan: menganggap ResizeObserver sebagai peristiwa resize yang lebih berkuasa
Ia memerhatikan kotak elemen dan menghantar pemberitahuan mengikut pemasaan susun atur; ia bukan sekadar pengganti mudah untuk peristiwa viewport.
Susulan: bagaimanakah anda mengesahkan React Strict Mode?
Pastikan persediaan pembangunan dan pembersihan kekal berpasangan dan jumlah pemerhati tidak terkumpul. Jangan silap menganggap semakan pembangunan tambahan sebagai langganan pendua dalam pengeluaran.
Susulan: bolehkah panggilan balik memanggil getBoundingClientRect?
Boleh, tetapi pengukuran tambahan boleh menambah kos susun atur. Utamakan nilai kotak entri dan buktikan sebarang bacaan tambahan dengan surihan prestasi.
Susulan: bagaimanakah anda menguji gelung saiz?
Jadikan panggilan balik mengubah gaya yang mempengaruhi saiznya sendiri, perhatikan amaran, kiraan pemberitahuan dan saiz akhir, kemudian pastikan versi yang diperbaiki stabil tanpa pemberitahuan berterusan.
Susulan: bilakah JavaScript tidak diperlukan?
Apabila keperluannya hanyalah perubahan gaya titik putus bekas, utamakan CSS container queries. Simpan JavaScript untuk data, pengukuran atau rendering pihak ketiga yang benar-benar memerlukannya.
Susulan: bagaimanakah anda membuktikan pengoptimuman berjaya?
Bandingkan INP, jumlah tugas panjang, masa susun atur, komit React, dan memori di bawah skrip seretan yang sama. Kiraan panggilan balik semata-mata bukanlah metrik pengalaman pengguna.