Halo, Habr!
Apakah Anda suka wawancara kerja? Dan sering menghabiskannya? Jika jawaban untuk pertanyaan kedua adalah "Ya", maka di antara kandidat Anda mungkin bertemu dengan orang-orang yang sangat baik dan pintar yang menjawab semua pertanyaan Anda dan mendekati akhir daftar gaji.
Tetapi Anda, tentu saja, tidak ingin membayar terlalu banyak kepada para profesional. Dan sangat penting untuk terlihat lebih pintar dari mereka, biarkan mereka hanya selama wawancara.
Jika Anda memiliki masalah dengan ini, selamat datang di kucing. Di sana Anda akan menemukan pertanyaan yang paling sulit dan salah pada Vue, yang akan menempatkan kandidat apa pun di tempat dan membuat Anda meragukan keterampilan profesional Anda.

1. Pemicu pengamat di dalam kait siklus hidup
Pertanyaan ini mungkin tampak mudah, tetapi saya jamin tidak ada satu pun, bahkan pengembang yang paling maju, yang akan menjawabnya. Anda bisa menanyakannya di awal wawancara, sehingga calon langsung merasakan keunggulan Anda.
Pertanyaan:Ada komponen TestComponent yang memiliki variabel jumlah. Di dalam kait siklus hidup utama, kami mengaturnya ke nilai dalam urutan numerik dari 1 hingga 6. Watcher, yang menampilkan nilainya di konsol, berada di variabel ini.
Kami membuat instance TestComponent dan menghapusnya setelah beberapa detik. Harus dikatakan bahwa kita akan melihat di output konsol.
Kode:
/* TestComponent.vue */ <template> <span> I'm Test component </span> </template> <script> export default { data() { return { amount: 0, }; }, watch: { amount(newVal) { console.log(newVal); }, }, beforeCreate() { this.amount = 1; }, created() { this.amount = 2; }, beforeMount() { this.amount = 3; }, mounted() { this.amount = 4; }, beforeDestroy() { this.amount = 5; }, destroyed() { this.amount = 6; }, }; </script>
Saya akan memberikan petunjuk: "2345" adalah jawaban yang salah.
JawabannyaDi konsol, kita hanya akan melihat angka 4.
PenjelasanInstance itu sendiri belum dibuat di hook beforeCreate, pengamat tidak akan bekerja di sini.
Watcher memicu perubahan pada kait yang dibuat, sebelumMount, dan yang dipasang. Karena semua kait ini dipanggil selama satu centang, Vue akan memanggil pengamat sekali di akhir, dengan nilai 4.
Vue akan berhenti berlangganan dari memantau perubahan variabel sebelum memanggil hook sebelumDestroy dan hancur, sehingga 5 dan 6 tidak akan sampai ke konsol.
Kotak pasir dengan contoh untuk memastikan jawabannya2. Perilaku alat peraga implisit
Pertanyaan ini didasarkan pada perilaku alat peraga langka di Vue. Semua programmer, tentu saja, hanya memaparkan validasi yang diperlukan untuk prop'ov dan tidak pernah mengalami perilaku seperti itu. Tetapi kandidat tidak perlu mengatakan ini. Akan lebih baik untuk mengajukan pertanyaan ini, melemparkan pandangan mengecamnya setelah jawaban yang salah dan pindah ke yang berikutnya.
Pertanyaan:Bagaimana prop dengan tipe Boolean berbeda dari yang lainnya?
/* SomeComponent.vue */ <template> </template> <script> export default { props: { testProperty: { type: Boolean, }, }, }; </script>
JawabannyaPenyangga dengan tipe Boolean berbeda dari yang lainnya karena Vue memiliki tipe pemeran khusus untuknya.
Jika string kosong atau nama prop itu sendiri dalam kebab-case dilewatkan sebagai parameter, maka Vue akan mengubahnya menjadi true.
Contoh:
Kami memiliki file dengan prop Boolean:
/* TestComponent.vue */ <template> <div v-if="canShow"> I'm TestComponent </div> </template> <script> export default { props: { canShow: { type: Boolean, required: true, }, }, }; </script>
Semua kasus penggunaan yang valid untuk komponen TestComponent ditunjukkan di bawah ini.
/* TestWrapper.vue */ <template> <div> <TestComponent canShow="" /> <TestComponent canShow /> <TestComponent canShow="can-show" /> </div> </template> <script> import TestComponent from 'path/to/TestComponent'; export default { components: { TestComponent, }, }; </script>
Kotak pasir dengan contoh untuk memastikan jawabannya3. Menggunakan array dalam $ ref
Jika kandidat Anda tahu bagaimana kerangka bekerja dari dalam ke luar di tingkat Evan Yu, Anda masih memiliki beberapa kartu truf di lengan baju Anda: Anda dapat mengajukan pertanyaan tentang perilaku kerangka kerja yang tidak terdokumentasi dan tidak disadari.
Pertanyaan:Vuex berisi larik objek file, masing-masing objek dalam larik memiliki properti nama dan id yang unik. Array ini diperbarui setiap beberapa detik, elemen-elemen dihapus dan ditambahkan padanya.
Kami memiliki komponen yang menampilkan nama setiap objek array dengan sebuah tombol, dengan mengklik di mana elemen dom yang terkait dengan file saat ini harus ditampilkan di konsol:
/* FileList.vue */ <template> <div> <div v-for="(file, idx) in files" :key="file.id" ref="files" > {{ file.name }} <button @click="logDOMElement(idx)"> Log DOM element </button> </div> </div> </template> <script> import { mapState } from 'vuex'; export default { computed: { ...mapState('files'), }, methods: { logDOMElement(idx) { console.log(this.$refs.files[idx]); }, }, }; </script>
Harus dikatakan di mana potensi kesalahan dan bagaimana cara memperbaikinya.
JawabannyaMasalahnya adalah bahwa array di dalam $ ref mungkin tidak berjalan dalam urutan yang sama dengan array asli (
tautan ke masalah ). Yaitu, situasi seperti itu dapat terjadi: kami mengklik tombol elemen ketiga dari daftar, dan elemen dom yang kedua ditampilkan di konsol.
Ini hanya terjadi ketika data dalam array sering berubah.
Metode solusi ditulis dalam masalah di GitHub:
1. Buat referensi unik untuk setiap item
<template> <div> <div v-for="(file, idx) in files" :key="file.id" :ref="`file_${idx}`" > {{ file.name }} <button @click="logDOMElement(idx)"> Log DOM element </button> </div> </div> </template> <script> import { mapState } from 'vuex'; export default { computed: { ...mapState('files'), }, methods: { logDOMElement(idx) { console.log(this.$refs[`file_{idx}`]); }, }, }; </script>
2. Atribut tambahan
<template> <div> <div v-for="(file, idx) in files" :key="file.id" :data-file-idx="idx" > {{ file.name }} <button @click="logDOMElement(idx)"> Log DOM element </button> </div> </div> </template> <script> import { mapState } from 'vuex'; export default { computed: { ...mapState('files'), }, methods: { logDOMElement(idx) { const fileEl = this.$el.querySelector(`*[data-file-idx=${idx}]`); console.log(fileEl); }, }, }; </script>
4. Rekreasi komponen yang aneh
Pertanyaan:Kami memiliki komponen khusus yang menulis ke konsol setiap kali kait yang dipasang disebut:
/* TestMount.vue */ <template> <div> I'm TestMount </div> </template> <script> export default { mounted() { console.log('TestMount mounted'); }, }; </script>
Komponen ini digunakan dalam komponen TestComponent. Ini memiliki tombol, dengan menekan yang selama 1 detik pesan Pesan teratas akan muncul.
/* TestComponent.vue */ <template> <div> <div v-if="canShowTopMessage"> Top message </div> <div> <TestMount /> </div> <button @click="showTopMessage()" v-if="!canShowTopMessage" > Show top message </button> </div> </template> <script> import TestMount from './TestMount'; export default { components: { TestMount, }, data() { return { canShowTopMessage: false, }; }, methods: { showTopMessage() { this.canShowTopMessage = true; setTimeout(() => { this.canShowTopMessage = false; }, 1000); }, }, }; </script>
Klik tombol dan lihat apa yang terjadi di konsol:

Mount pertama diharapkan, tetapi di mana dua lainnya? Bagaimana cara memperbaikinya?
Sandbox dengan contoh untuk memahami kesalahan dan memperbaikinyaJawabannyaMasalahnya di sini muncul dari kekhasan mencari perbedaan DOM Virtual di Vue.
Pada awalnya, DOM Virtual kami terlihat seperti ini:
Setelah mengklik tombol, akan terlihat seperti ini:
Vue mencoba mencocokkan Virtual DOM yang lama dengan yang baru untuk mencari tahu apa yang perlu dihapus dan ditambahkan:
Item yang dihapus dicoret dalam warna merah, item yang dibuat disorot dengan warna hijauVue tidak dapat menemukan komponen TestMount, oleh karena itu membuatnya kembali.
Situasi serupa akan diulang sedetik setelah menekan tombol. Pada titik ini, komponen TestMounted menampilkan informasi tentang pembuatannya ke konsol untuk ketiga kalinya.
Untuk memperbaiki masalah, cukup cantumkan atribut kunci pada div dengan komponen TestMounted:
/* TestComponent.vue */ <template> <div> <div key="container"> <TestMount /> </div> </div> </template> /* ... */
Sekarang Vue akan dapat memetakan elemen-elemen yang diperlukan dari DOM Virtual.
5. Membuat komponen tabel
Tantangan:Penting untuk membuat komponen yang mengambil array dengan data dan menampilkannya dalam sebuah tabel. Penting untuk memberikan kesempatan untuk menentukan kolom dan tipe sel.
Informasi tentang kolom dan jenis sel harus ditransmisikan melalui komponen khusus (sama dengan
elemen-ui ):
/* SomeComponent.vue */ <template> <CustomTable :items="items"> <CustomColumn label="Name"> <template slot-scope="item"> {{ item.name }} </template> </CustomColumn> <CustomColumn label="Element Id"> <template slot-scope="item"> {{ item.id }} </template> </CustomColumn> </CustomTable> </template>
Pada awalnya, tugas tidak mengandung kebutuhan untuk melakukan hal yang sama dengan elemen-ui. Tetapi ternyata beberapa orang mampu menyelesaikan tugas dalam kata-kata aslinya. Oleh karena itu, persyaratan ditambahkan untuk mengirimkan informasi tentang kolom dan jenis sel menggunakan komponen.
Saya yakin bahwa orang yang Anda wawancarai akan terbius sepanjang waktu. Anda dapat memberi mereka waktu 30 menit untuk menyelesaikan masalah seperti itu.
SolusiGagasan utama adalah untuk mentransfer semua data ke komponen CustomTable di komponen CustomColumn, dan kemudian akan membuat semuanya sendiri.
Berikut ini adalah contoh implementasi. Itu tidak memperhitungkan beberapa poin (seperti mengganti label), tetapi prinsip dasarnya harus jelas.
export default { render() { return null; }, props: { label: { type: String, required: true, }, }, mounted() {
export default { render() { const { columnsData, items } = this; const { default: defaultSlot } = this.$slots; return ( <div> // CustomColumn {defaultSlot} <table> // <tr> {columnsData.map(columnData => ( <td key={columnData.label}> {columnData.label} </td> ))} </tr> // {items.map(item => ( <tr> {columnsData.map(columnData => ( <td key={columnData.label}> {columnData.createCell(item)} </td> ))} </tr> ))} </table> </div> ); }, props: { items: { type: Array, required: true, }, }, data() { return { columnsData: [], }; }, methods: { setColumnData(columnData) { this.columnsData.push(columnData); }, }, };
6. Membuat portal
Jika kandidat Anda belum menyelesaikan tugas sebelumnya, tidak ada yang perlu dikhawatirkan: Anda dapat memberinya satu lagi, tidak kurang sulit!
Tantangan:Buat komponen Portal dan PortalTarget, seperti pustaka
portal-vue :
/* FirstComponent.vue */ <template> <div> <Portal to="title"> Super header </Portal> </div> </template>
/* SecondComponent.vue */ <template> <div> <PortalTarget name="title" /> </div> </template>
SolusiUntuk membuat portal, Anda perlu mengimplementasikan tiga objek:
- Gudang Data Portal
- Komponen portal yang menambahkan data ke toko
- Komponen PortalTarget yang mengambil data dari toko dan menampilkannya
import Vue from 'vue'; const bus = new Vue({ data() { return { portalDatas: [], }; }, methods: { setPortalData(portalData) { const { portalDatas } = this; const portalDataIdx = portalDatas.findIndex( pd => pd.id === portalData.id, ); if (portalDataIdx === -1) { portalDatas.push(portalData); return; } portalDatas.splice(portalDataIdx, 1, portalData); }, removePortalData(portalDataId) { const { portalDatas } = this; const portalDataIdx = portalDatas.findIndex( pd => pd.id === portalDataId, ); if (portalDataIdx === -1) { return; } portalDatas.splice(portalDataIdx, 1); }, getPortalData(portalName) { const { portalDatas } = this; const portalData = portalDatas.find(pd => pd.to === portalName); return portalData || null; }, }, }); export default bus;
import dataBus from './dataBus'; let currentId = 0; export default { props: { to: { type: String, required: true, }, }, computed: {
import dataBus from './dataBus'; export default { props: { name: { type: String, required: true, }, }, render() { const { portalData } = this; if (!portalData) { return null; } return ( <div class="portal-target"> {portalData.portalEl} </div> ); }, computed: { portalData() { return dataBus.getPortalData(this.name); }, }, };
Solusi ini tidak mendukung pengubahan atribut to, tidak mendukung animasi melalui transisi, dan tidak mendukung nilai default, seperti portal-vue. Tapi ide umumnya harus jelas.
7. Pencegahan reaktivitas
Pertanyaan:Anda menerima objek besar dari api dan menampilkannya kepada pengguna. Sesuatu seperti ini:
/* ItemView.vue */ <template> <div v-if="item"> <div> {{ item.name }} </div> <div> {{ item.price }} </div> <div> {{ item.quality }} </div> </div> </template> <script> import getItemFromApi from 'path/to/getItemFromApi'; export default { data() { return { item: null, }; }, async mounted() { this.item = await getItemFromApi(); }, }; </script>
Ada masalah dalam kode ini. Kami tidak mengubah nama, harga, kualitas, dan properti lainnya dari objek item. Tetapi Vue tidak tahu tentang ini dan menambahkan reaktivitas ke setiap bidang.
Bagaimana ini bisa dihindari?
JawabannyaUntuk menghindari mengubah properti menjadi reaktif, Anda harus membekukan objek sebelum menambahkannya di dalam Vue menggunakan metode Object.freeze.
Vue akan memeriksa apakah objek dibekukan menggunakan metode Object.isFrozen. Dan jika demikian, maka Vue tidak akan menambahkan getter dan setter reaktif ke properti objek, karena mereka tidak dapat diubah dalam hal apa pun. Dengan objek yang sangat besar, pengoptimalan ini membantu menghemat hingga beberapa puluh milidetik.
Komponen yang dioptimalkan akan terlihat seperti ini:
/* ItemView.vue */ <template> </template> <script> import getItemFromApi from 'path/to/getItemFromApi'; export default { async mounted() { const item = await getItemFromApi(); Object.freeze(item); this.item = item; }, }; </script>
Object.freeze hanya membekukan properti objek itu sendiri. Jadi, jika suatu objek berisi objek bersarang, mereka juga perlu dibekukan.
Pembaruan pada 19 Januari 2019 : Atas saran
Dmitry Zlygin, saya melihat
perpustakaan vue-non-reaktif dan menemukan cara lain. Ini sempurna untuk situasi di mana Anda memiliki banyak objek bersarang.
Vue tidak akan menambahkan reaktivitas ke objek jika melihat bahwa itu sudah reaktif. Kita dapat menipu Vue dengan membuat Pengamat kosong untuk objek:
/* ItemView.vue */ <template> </template> <script> import Vue from 'vue'; import getItemFromApi from 'path/to/getItemFromApi'; const Observer = new Vue() .$data .__ob__ .constructor; export default { async mounted() { const item = await getItemFromApi(); </script>
8. Kesalahan perangkat lambat
Pertanyaan:Ada komponen dengan metode yang menampilkan salah satu properti dari objek item di konsol, dan kemudian menghapus objek item:
/* SomeComponent.vue */ <template> <div v-if="item"> <button @click="logAndClean()"> Log and clean </button> </div> </template> <script> export default { data() { return { item: { value: 124, }, }; }, methods: { logAndClean() { console.log(this.item.value); this.item = null; }, }, }; </script>
Apa yang salah di sini?
JawabannyaMasalahnya adalah bahwa setelah klik pertama pada tombol Vue, perlu beberapa waktu untuk memperbarui DOM untuk pengguna dan menghapus tombol. Oleh karena itu, pengguna terkadang dapat mengklik dua kali. Metode logAndClean bekerja normal pertama kali, dan crash kedua kali, karena tidak bisa mendapatkan properti nilai.
Saya selalu melihat masalah seperti itu di pelacak kesalahan, terutama pada ponsel murah untuk 4-5k rubel.
Untuk menghindarinya, tambahkan saja centang untuk keberadaan item di awal fungsi:
<template> </template> <script> export default { methods: { logAndClean() { const { item } = this; if (!item) { return; } console.log(item.value); this.item = null; }, }, }; </script>
Untuk mereproduksi bug, Anda dapat pergi ke kotak pasir dengan contoh, mengatur pelambatan maksimum CPU dan mengklik tombol dengan cepat. Sebagai contoh, saya melakukannya.
Tautan kotak pasir untuk memastikan jawabannyaTerima kasih telah membaca artikel sampai akhir! Saya pikir sekarang Anda bisa tampak lebih pintar di wawancara dan gaji kandidat Anda akan turun secara signifikan!