مرحبا يا هبر!
هل تحب مقابلات العمل؟ وغالبا ما تنفق لهم؟ إذا كانت الإجابة على السؤال الثاني هي "نعم" ، فمن المحتمل أن تقابل من بين المرشحين أشخاص ممتازين وذكيين أجابوا على جميع أسئلتك وتقترب من نهاية جدول الرواتب.
لكنك ، بالطبع ، لا تريد أن تدفع للمحترفين الكثير. ومن الضروري للغاية أن تبدو أكثر ذكاءً منهم ، اتركهم فقط طوال مدة المقابلة.
إذا كان لديك مشاكل مع هذا ، ثم مرحبا بكم في القط. ستجد هناك أكثر الأسئلة خدعةً وإخفاقًا على Vue ، والتي ستضع أي مرشح موضع التنفيذ وتجعلك تشك في مهاراتك المهنية.

1. مراقب الزناد داخل السنانير دورة حياة
قد يبدو هذا السؤال سهلاً ، لكنني أضمن أن لا أحد ، حتى المطور الأكثر تقدمًا ، سيجيب عليه. يمكنك أن تسأله في بداية المقابلة ، حتى يشعر المرشح بتفوقك على الفور.
سؤال:يوجد مكون TestComponent يحتوي على متغير مقدار. داخل خطاطيف دورة الحياة الرئيسية ، قمنا بتعيينها إلى قيمة بالترتيب العددي من 1 إلى 6. يقف Watcher ، الذي يعرض قيمته في وحدة التحكم ، على هذا المتغير.
نقوم بإنشاء مثيل TestComponent وحذفه بعد بضع ثوانٍ. يجب أن يقال أننا سوف نرى في إخراج وحدة التحكم.
الرمز:
/* 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>
سأقدم تلميحًا: "2345" هو الجواب الخاطئ.
الجوابفي وحدة التحكم ، سنرى الرقم 4 فقط.
التفسيرالمثيل نفسه لم يتم إنشاؤه بعد في الخطاف قبل ، لن يعمل مراقب هنا.
يقوم المشغل بتشغيل التغييرات على السنانير التي تم إنشاؤها قبل و MountMount. نظرًا لأن كل هذه السنانير يتم استدعاءها خلال علامة واحدة ، فسوف يتصل Vue بالمراقب مرة واحدة في النهاية ، بقيمة 4.
ستقوم Vue بإلغاء الاشتراك من مراقبة تغيير المتغير قبل الاتصال قبل السنانير المدمرة والمدمرة ، لذلك لن تصل 5 و 6 إلى وحدة التحكم.
رمل مع مثال للتأكد من الإجابة2. السلوك الدعائم الضمني
يعتمد هذا السؤال على سلوك الدعائم النادرة في Vue. جميع المبرمجين ، بالطبع ، يكشفون ببساطة عن المصادقات اللازمة لـ prop'ov ولا يواجهون مثل هذا السلوك أبدًا. لكن المرشح لا يحتاج أن يقول هذا. سيكون من الأفضل طرح هذا السؤال ، وإلقاء نظرة تدين عليه بعد إجابة غير صحيحة والانتقال إلى التالي.
سؤال:كيف الدعامة مع نوع منطقية تختلف عن بقية؟
/* SomeComponent.vue */ <template> </template> <script> export default { props: { testProperty: { type: Boolean, }, }, }; </script>
الجوابيدعم نوع Boolean نوعًا مختلفًا عن كل الأنواع الأخرى في Vue التي لها نوع خاص من الممثلين.
إذا تم تمرير سلسلة فارغة أو اسم الدعامة نفسها في kebab-case كمعلمة ، فسيقوم Vue بتحويل هذا إلى true.
مثال:
لدينا ملف مع Boolean prop:
/* TestComponent.vue */ <template> <div v-if="canShow"> I'm TestComponent </div> </template> <script> export default { props: { canShow: { type: Boolean, required: true, }, }, }; </script>
جميع حالات الاستخدام الصالحة لمكون TestComponent موضحة أدناه.
/* 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>
رمل مع مثال للتأكد من الإجابة3. استخدام صفيف في $ refs
إذا كان مرشحك يعرف كيف يعمل الإطار من الداخل إلى الخارج على مستوى إيفان يو ، فلا يزال لديك عددًا قليلاً من الأوراق الرابحة: يمكنك طرح سؤال حول السلوك غير الموثق وغير الواضح للإطار.
سؤال:يحتوي Vuex على مجموعة من كائنات الملفات ، كل كائن في المصفوفة له خصائص فريدة في الاسم والمعرّف. يتم تحديث هذه المجموعة كل بضع ثوانٍ ، ويتم حذف العناصر وإضافتها إليها.
لدينا مكون يعرض اسم كل كائن صفيف مع زر ، وذلك بالنقر فوق أي عنصر dom المرتبط بالملف الحالي يجب أن يتم عرضه في وحدة التحكم:
/* 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>
يجب أن يقال أين هو الخطأ المحتمل وكيفية إصلاحه.
الجوابالمشكلة هي أن الصفيف الموجود داخل $ refs قد لا ينتقل بنفس ترتيب الصفيف الأصلي (
رابط إلى المشكلة ). أي ، قد يحدث مثل هذا الموقف: نضغط على زر العنصر الثالث في القائمة ، ويتم عرض عنصر dom الثاني في وحدة التحكم.
يحدث هذا فقط عندما تتغير البيانات في الصفيف بشكل متكرر.
تتم كتابة طرق الحلول في جيثب:
1. إنشاء مرجع فريد لكل عنصر
<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. سمة إضافية
<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. غريب مكون الترفيه
سؤال:لدينا مكون خاص يكتب إلى وحدة التحكم في كل مرة يتم فيها استدعاء الخطاف المثبت:
/* TestMount.vue */ <template> <div> I'm TestMount </div> </template> <script> export default { mounted() { console.log('TestMount mounted'); }, }; </script>
يستخدم هذا المكون في مكون TestComponent. يحتوي على زر ، بالضغط على الرسالة التي تظهر الرسالة الأولى لمدة ثانية واحدة.
/* 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>
انقر فوق الزر وشاهد ما يحدث في وحدة التحكم:

كان أول تعليق متوقعًا ، لكن أين هما الآخران؟ كيفية اصلاحها؟
رمل مع مثال لفهم الخطأ وإصلاحهالجوابتكمن المشكلة هنا من خصائص البحث عن اختلافات DOM الظاهري في Vue.
في البداية ، يبدو DOM الظاهري لدينا كما يلي:
بعد النقر فوق الزر ، يبدو الأمر كما يلي:
تحاول Vue مواءمة DOM الظاهري القديم مع الجديدة لتكتشف ما يجب إزالته وإضافته:
يتم شطب العناصر المحذوفة باللون الأحمر ، ويتم تمييز العناصر التي تم إنشاؤها باللون الأخضرتعذر على Vue العثور على مكون TestMount ، وبالتالي إعادة إنشائه.
سيتم تكرار موقف مماثل ثانية بعد الضغط على الزر. في هذه المرحلة ، يعرض مكون TestMounted معلومات حول إنشائه إلى وحدة التحكم للمرة الثالثة.
لحل المشكلة ، فقط ضع سمة المفتاح على div مع مكون TestMounted:
/* TestComponent.vue */ <template> <div> <div key="container"> <TestMount /> </div> </div> </template> /* ... */
ستتمكن Vue الآن من تعيين العناصر الضرورية لـ DOMs الافتراضية بشكل فريد.
5. إنشاء مكون الجدول
التحدي:من الضروري إنشاء مكون يأخذ صفيفًا مع البيانات ويعرضها في جدول. من الضروري إعطاء الفرصة لتحديد الأعمدة ونوع الخلية.
يجب إرسال المعلومات حول الأعمدة ونوع الخلية من خلال مكون خاص (مثل
عنصر واجهة المستخدم ):
/* 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>
في البداية ، لم تحتوي المهمة على الحاجة إلى فعل نفس عنصر واجهة المستخدم. لكن اتضح أن بعض الأشخاص قادرون على إكمال المهمة في صيغتها الأصلية. لذلك ، تمت إضافة المتطلبات لنقل المعلومات حول الأعمدة ونوع الخلية التي تستخدم المكونات.
أنا متأكد من أن الأشخاص الذين تجري مقابلتهم سيكونون في حالة ذهول طوال الوقت. يمكنك منحهم 30 دقيقة لحل هذه المشكلة.
الحلالفكرة الرئيسية هي نقل جميع البيانات إلى مكون CustomTable في مكون CustomColumn ، ومن ثم ستعرض كل شيء بنفسه.
ما يلي هو مثال التنفيذ. لا تأخذ في الاعتبار بعض النقاط (مثل تغيير التسمية) ، ولكن يجب أن يكون المبدأ الأساسي واضحًا.
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. إنشاء بوابة
إذا لم يكمل مرشحك المهمة السابقة ، فلا يوجد ما يدعو للقلق: يمكنك إعطائه مهمة أخرى ، لا تقل صعوبة!
التحدي:قم بإنشاء مكون Portal و PortalTarget ، مثل مكتبة
portal-vue :
/* FirstComponent.vue */ <template> <div> <Portal to="title"> Super header </Portal> </div> </template>
/* SecondComponent.vue */ <template> <div> <PortalTarget name="title" /> </div> </template>
الحللإنشاء مدخل ، تحتاج إلى تطبيق ثلاثة كائنات:
- مستودع بيانات البوابة
- مكون البوابة الذي يضيف البيانات إلى المتجر
- مكون PortalTarget الذي يسترجع البيانات من المتجر ويعرضها
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); }, }, };
لا يدعم هذا الحل تغيير السمة ، ولا يدعم الرسوم المتحركة خلال عملية النقل ، ولا يدعم القيم الافتراضية ، مثل portal-vue. لكن الفكرة العامة يجب أن تكون واضحة.
7. منع التفاعل
سؤال:لقد استلمت كائنًا كبيرًا من api وعرضته على المستخدم. شيء مثل هذا:
/* 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>
هناك مشكلة في هذا الرمز. نحن لا نغير اسم والسعر والجودة وغيرها من الخصائص لكائن العنصر. لكن Vue لا تعرف عن هذا وتضيف التفاعل إلى كل حقل.
كيف يمكن تجنب ذلك؟
الجوابلتجنب تغيير الخصائص إلى رد الفعل ، يجب تجميد الكائن قبل إضافته داخل Vue باستخدام أسلوب Object.freeze.
سوف Vue تحقق مما إذا كان الكائن مجمداً باستخدام الأسلوب Object.isFrozen. وإذا كان الأمر كذلك ، فلن تضيف Vue أدوات تفاعلية وترتكز إلى خصائص الكائن ، حيث لا يمكن تغييرها في أي حال. مع الكائنات الكبيرة جدًا ، يساعد هذا التحسين على توفير ما يصل إلى عشرات الميلي ثانية.
سيبدو المكون الأمثل كما يلي:
/* 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 فقط خصائص الكائن نفسه. لذلك ، إذا كان الكائن يحتوي على كائنات متداخلة ، فيجب أيضًا تجميدها.
التحديث اعتبارًا من 19 كانون الثاني (يناير) 2019 : بناءً على نصيحة
ديميتري زيلين ، نظرت إلى
المكتبة غير الفعالة ووجدت طريقة أخرى. إنه مثالي للحالات التي يكون لديك فيها الكثير من الكائنات المتداخلة.
لن تضيف Vue تفاعلية إلى كائن ما إذا رأى أنه تفاعلي بالفعل. يمكننا خداع Vue عن طريق إنشاء مراقب فارغ للكائن:
/* 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. أخطاء الأجهزة البطيئة
سؤال:يوجد مكون بأسلوب يعرض أحد خصائص كائن العنصر في وحدة التحكم ، ثم يزيل كائن العنصر:
/* 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>
ما يمكن أن يحدث الخطأ هنا؟
الجوابالمشكلة هي أنه بعد النقرة الأولى على زر Vue ، يستغرق الأمر بعض الوقت لتحديث DOM للمستخدم وإزالة الزر. لذلك ، يمكن للمستخدم في بعض الأحيان النقر المزدوج. طريقة logAndClean تعمل بشكل طبيعي في المرة الأولى ، وتتعطل مرة أخرى ، لأنها لا تستطيع الحصول على خاصية القيمة.
أرى باستمرار مشكلة من هذا القبيل في تعقب الأخطاء ، خاصةً على الهواتف المحمولة الرخيصة مقابل 4-5 آلاف روبل.
لتجنب ذلك ، ما عليك سوى إضافة التحقق من وجود عنصر في بداية الوظيفة:
<template> </template> <script> export default { methods: { logAndClean() { const { item } = this; if (!item) { return; } console.log(item.value); this.item = null; }, }, }; </script>
لإعادة إنتاج الخلل ، يمكنك الانتقال إلى صندوق الرمال بمثال ، وتعيين الحد الأقصى لخنق وحدة المعالجة المركزية والنقر بسرعة على الزر. على سبيل المثال ، لقد فعلت ذلك.
رابط وضع الحماية للتأكد من الإجابةشكرا لقراءة المقال حتى النهاية! أعتقد أنه الآن يمكنك بالتأكيد أن تكون أكثر ذكاءً في المقابلات وسوف تنخفض رواتب المرشحين بشكل كبير!