8个最差的Vue.js面试问题

哈Ha!

你喜欢面试吗? 经常花钱吗? 如果第二个问题的答案是“是”,那么在候选人中您可能遇到了优秀而聪明的人,这些人回答了您所有的问题并接近薪资总额。

但是,您当然不希望付给专业人士太多的钱。 至关重要的是,看起来比他们聪明,让他们只在面试期间。

如果您对此有疑问,欢迎与我们联系。 在这里,您会发现有关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。

解说
实例本身尚未在beforeCreate挂钩中创建,监视程序将无法在此处工作。

监视程序触发对已创建的,beforeMount和已挂接的钩子的更改。 由于所有这些钩子都是在一个滴答声中被调用的,因此Vue将在末尾调用观察者一次,值为4。

Vue将在调用beforeDestroy和销毁的钩子之前取消订阅监视变量的更改,因此5和6将不会进入控制台。

带有示例的沙盒,以确保答案

2.内隐道具行为


这个问题基于Vue中罕见的道具行为。 当然,所有程序员都只是简单地公开了prop'ov的必要验证,而从未遇到这种行为。 但是候选人不必这么说。 最好提出这个问题,在错误答案后对它定罪,然后继续下一个。

问题:

布尔类型的prop与其余类型有何不同?

 /* SomeComponent.vue */ <template> <!-- ... --> </template> <script> export default { /* ... */ props: { testProperty: { type: Boolean, }, }, }; </script> 

答案
具有布尔类型的Prop与所有其他类型的Pro不同,因为Vue具有专用的类型转换。

如果将空字符串或kebab-case中prop本身的名称作为参数传递,则Vue会将其转换为true。

一个例子:

我们有一个带有布尔属性的文件:

 /* 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> <!--    canShow   true  TestComponent --> <TestComponent canShow="" /> <!--    , vue-template-compiler      prop' --> <TestComponent canShow /> <!--  canShow   true --> <TestComponent canShow="can-show" /> </div> </template> <script> import TestComponent from 'path/to/TestComponent'; export default { components: { TestComponent, }, }; </script> 


带有示例的沙盒,以确保答案

3.在$ refs中使用数组


如果您的候选人知道该框架在Evan Yu级别上从内而外的工作原理,那么您仍然可以省下一些王牌:您可以提出有关框架的无证件和明显行为的问题。

问题:

Vuex包含一个文件对象数组,该数组中的每个对象都有唯一的名称和ID属性。 该数组每隔几秒钟更新一次,删除并添加元素。

我们有一个通过按钮显示每个数组对象名称的组件,方法是单击该按钮,在控制台上应显示与当前文件关联的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元素显示在控制台上。

仅当阵列中的数据频繁更改时,才会发生这种情况。

解决方案方法已在GitHub上发布:

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组件中使用。 它有一个按钮,按下该按钮1秒钟,将显示Top消息。
 /* 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> 

单击按钮,然后查看控制台中发生的情况:



预计会进行第一次安装,但其他两个安装在哪里? 如何解决?

带有示例的沙盒,用于了解错误并进行修复

答案
这里的问题来自在Vue中搜索虚拟DOM差异的特殊性。

从一开始,我们的虚拟DOM如下所示:


单击按钮后,它看起来像这样:



Vue尝试将旧的虚拟DOM与新的虚拟DOM相匹配,以找出需要删除和添加的内容:


删除的项目以红色划掉,创建的项目以绿色突出显示

Vue找不到TestMount组件,因此重新创建了它。

按下按钮后一秒钟将重复类似的情况。 此时,TestMounted组件第三次向控制台显示有关其创建的信息。

要解决此问题,只需将带有TestMounted组件的key属性放在div上:

 /* TestComponent.vue */ <template> <div> <!-- ... --> <div key="container"> <TestMount /> </div> <!-- ... --> </div> </template> /* ... */ 

现在,Vue将能够明确映射虚拟DOM的必要元素。

5.创建表组件


挑战:

有必要创建一个组件,该组件采用包含数据的数组并将其显示在表中。 必须有机会指定单元格的列和类型。

有关列和单元格类型的信息必须通过特殊组件(与element-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> 

最初,该任务不需要执行与element-ui相同的操作。 但是事实证明,有些人能够完成原始措辞中的任务。 因此,添加了要求以使用组件传输有关列和单元格类型的信息。

我确信您的受访者将一直处于昏迷状态。 您可以给他们30分钟的时间来解决此类问题。

解决方案
主要思想是将所有数据传输到CustomColumn组件中的CustomTable组件,然后它将呈现所有内容。

以下是示例实现。 它没有考虑到某些要点(例如更改标签),但是基本原理应该清楚。

 /* CustomColumn.js */ export default { render() { return null; }, props: { label: { type: String, required: true, }, }, mounted() { //    CustomTable   this.$parent.setColumnData({ label: this.label, createCell: this.$scopedSlots.default, }); }, }; 

 /* CustomTable.js */ /*  JSX,    template     createCell,   CustomColumn.js */ 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组件

 /* dataBus.js */ /*      */ 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; 

 /* Portal.vue */ /*      dataBus */ import dataBus from './dataBus'; let currentId = 0; export default { props: { to: { type: String, required: true, }, }, computed: { //  id . //      dataBus id() { return currentId++; }, }, render() { return null; }, created() { this.setPortalData(); }, //    updated() { this.setPortalData(); }, methods: { setPortalData() { const { to, id } = this; const { default: portalEl } = this.$slots; dataBus.setPortalData({ to, id, portalEl, }); }, }, beforeDestroy() { dataBus.removePortalData(this.id); }, }; 

 /* PortalTarget.vue */ /*      */ 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); }, }, }; 

此解决方案不支持更改to属性,不支持通过过渡的动画,也不支持默认值,例如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对此一无所知,并向每个字段添加了反应性。

如何避免这种情况?

答案
为了避免将属性更改为反应式,必须先冻结对象,然后再使用Object.freeze方法将其添加到Vue中。

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仅冻结对象本身的属性。 因此,如果一个对象包含嵌套对象,则还需要冻结它们。

截至 20191月19日的更新 :在Dmitry Zlygin的建议下,我查看了vue-nonreactive库并找到了另一种方法。 对于您有很多嵌套对象的情况,它是完美的选择。

如果Vue看到对象已经具有反应性,则不会对其添加反应性。 我们可以通过为对象创建一个空的Observer来欺骗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(); //   Observer   item.__ob__ = new Observer({}); this.item = item; }, }; </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方法第一次正常工作,而第二次崩溃,因为它无法获取value属性。

我经常在错误跟踪器中看到这样的问题,特别是在便宜的4-5k卢布手机上。

为了避免这种情况,只需在函数开始处添加对item是否存在的检查:

 <template> <!-- ... --> </template> <script> export default { /* ... */ methods: { logAndClean() { const { item } = this; if (!item) { return; } console.log(item.value); this.item = null; }, }, }; </script> 

要重现该错误,您可以使用示例进入沙盒,设置CPU的最大限制,然后快速单击该按钮。 例如,我做到了。



沙盒链接以确保答案

感谢您阅读本文的结尾! 我认为现在您在面试中肯定会显得更聪明,并且候选人的薪水将大大下降!

Source: https://habr.com/ru/post/zh-CN435700/


All Articles