哈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”是错误的答案。
解说实例本身尚未在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> <TestComponent canShow="" /> <TestComponent canShow /> <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组件,然后它将呈现所有内容。
以下是示例实现。 它没有考虑到某些要点(例如更改标签),但是基本原理应该清楚。
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); }, }, };
此解决方案不支持更改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仅冻结对象本身的属性。 因此,如果一个对象包含嵌套对象,则还需要冻结它们。
截至 2019年
1月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(); </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的最大限制,然后快速单击该按钮。 例如,我做到了。
沙盒链接以确保答案感谢您阅读本文的结尾! 我认为现在您在面试中肯定会显得更聪明,并且候选人的薪水将大大下降!