面试题:初试之初次面试(病句,笑)

10,081 阅读25分钟

前言

看到身边的同学渐渐的都有了一些面试之后,我逐渐感到了焦虑,甚至都有些对自己感到不自信,之后在上周三的上午,终于时来运转,先是梭翱打电话来,之后就是美云和琻瑢那边的简历初筛通过,通知我面试,之后按照自己的回忆写下了一些感悟与题目,希望对你们有所帮助。

浙江杭州(实习 130-160/天)

这是我的第一场面试,面试官问的都是 vue 的问题。这场面试全程懵逼下来的,因为我前面基本都在准备 js 和 css 方面,vue 方面也就瞄了几眼,结果就是和面试官疯狂的扯。面试完之后反思,在自我介绍中一定要讲清楚自己使用了是vue2 还是 vue3,不熟悉或者面试前没准备好的知识点一定不要讲出来,全程懵下来血的教训。然后也是电话面试,所以在听面试官老师的问题方面可能有点费力。在看面试题的时候,不要死记硬背,可以根据自己熟悉的语句自己表达出来就行。

1. 说一下vue2和vue3生命周期的实现和它们的不同点?

每个 Vue 实例在创建时都会经过一系列的初始化过程,vue 的生命周期钩子,就是说在达到某一阶段或条件时去触发的函数,目的就是为了完成一些动作或者事件

Vue2的生命周期函数

  • create阶段:vue实例被创建(只触发一次)

    beforeCreate: 创建前,此时data和methods中的数据都还没有初始化

    created: 创建完毕,data中有值,未挂载,可以网络请求初始化数据

  • mount阶段: vue实例被挂载到真实DOM节点(只触发一次)

    beforeMount:未经Vue编译的DOM结构

    mounted: 可以发起其他网络请求,也可以操作DOM

  • update阶段:当vue实例里面的data数据变化(对数据增删改)时,触发组件的重新渲染(触发多次,局部数据变化)

    beforeUpdate:更新前

    updated:更新后

  • destroy阶段:vue实例被销毁(只触发一次)

    beforeDestroy:实例被销毁前,此时可以手动销毁一些方法

    destroyed:销毁后

上述生命周期钩子函数中,beforeCreate 和 created 钩子函数在组件创建时只会执行一次,而 beforeUpdate 和 updated 钩子函数则会在组件的数据发生变化时多次执行。在组件销毁时,beforeDestroy 和 destroyed 钩子函数也只会执行一次。

Vue3的生命周期函数

  • setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method

  • mount阶段: vue实例被挂载到真实 DOM 节点

    onBeforeMount() : 组件挂载到节点上之前执行的函数;

    onMounted() : 组件挂载完成后执行的函数;

  • update阶段:当vue实例里面的data数据变化时,触发组件的重新渲染

    onBeforeUpdate(): 组件更新之前执行的函数;

    onUpdated(): 组件更新完成之后执行的函数;

  • unmount阶段:vue实例被销毁

    onBeforeUnmount(): 组件卸载之前执行的函数;

    onUnmounted(): 组件卸载完成后执行的函数;

在 Vue3 中,Destroy 钩子函数被废弃,取而代之的是 Unmounted 钩子函数。此外,Vue3 还新增了一个 onErrorCaptured 钩子函数,用于处理子孙组件抛出的错误。

不同

1. vue3和vue2的生命周期函数名称

重命名的原因是为了更好地反映生命周期的不同阶段,方便开发者进行理解和使用。

常用生命周期对比如下表所示。

vue2vue3
setup()
beforeCreatebeforeCreate
createdcreated
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted

2. 新增和废弃生命周期函数

Vue3 为我们提供了一些新的生命周期函数,这些函数可以帮助我们更好地管理组件,这些新的生命周期函数分别是:

onRenderTracked: 用于跟踪渲染过程中被访问的属性,并在首次渲染时进行记录或处理(只在首次调用,不会重复触发)。

const message = ref('Hello, world!'); 
const count = ref(0); 
// 监听 message 的渲染情况,target为vue中所有渲染的属性,key为单独的某个属性
onRenderTracked((target, key) => { 
    if (target === message) { 
        console.log(`"${key}" 被渲染了。当前值为: ${message.value}`); 
    } 
});

onRenderTriggered: 用于监视渲染过程中被访问的属性,并在每次渲染时进行处理(不仅仅是在首次渲染时),用法同上.

需要注意的是,这两个钩子函数只在开发模式下生效,生产模式下会被自动删除。因此,在生产环境中不需要担心它们会对性能产生任何影响。

这些新的生命周期函数可以帮助我们更好地调试、优化组件,提升应用的性能。

3. 使用方法,使用 Composition API 代替 OPTIONS API

Vue3 引入了新的 API —— Composition API,通过这些 API 可以使用 hook 函数来代替 Vue2的OPTIONS API,更好的按需引用。 Composition API 可以让我们更好地管理代码逻辑,将不同的功能划分为不同的小函数,便于维护和复用。hook 函数在组件中的调用顺序与生命周期函数类似,但是更加灵活,可以根据需要进行组合和抽离。

4. 新增了一道兼容 OPTIONS API 的流程,即init options API

vue2生命周期执行过程

生命周期.png

vue3生命周期执行过程

init options API 的目的是将 Options API 中的选项转换为 Composition API 中的逻辑组合,避免在 vue3 中写了 vue2 的语法而识别不出来。

image.png

2. Vue2和Vue3数据更新时有什么不一样?

Proxy 替代 Object.defineProperty:在 Vue2 中,使用 Object.defineProperty 来拦截数据的变化,但是该方法存在一些缺陷,比如不能监听新增的属性和数组变化等。

为什么不能监听新增的属性和数组变化?

(1) 在vue实例初始化时,当使用Object.defineProperty 定义属性时,对象已经定义好了,只有被定义的属性才会成为监听对象的一部分,而新增的那些属性是在Object.defineProperty之后定义的(也就是没在一开始的data中的),导致新增的属性不会被监听;

(2)在 JavaScript 中,数组是一种特殊的对象,其索引可以视为对象的属性,而数组元素则是这些属性的值。而一个数组的元素可能远远大于对象的属性,如果要对一个数组的每个元素都使用 Object.defineProperty 进行监听,那么会导致大量的属性拦截器被创建,这对性能会有较大的影响。因此,在vue2中,对数组的直接修改并不会触发属性的变化,得用重写过的7个变异方法(push、pop、shift、unshift、splice、sort、reverse)来进行增删改。

// 定义一个对象
let obj = {
    property: ''
};

Object.defineProperty(obj, property, {
  // 可枚举
  enumerable: true,
  // 可配置
  configurable: true,
  // 获取属性值时触发的函数,但通过obj读取属性property时会触发get方法直接返回value
  get() {
    console.log('Get value:', obj.property);
    return obj.property;
  },
  // 设置属性值时触发的函数,当通过obj设置属性property时会触发set方法 
  set(newValue) {
    console.log('Set value:', newValue);
    obj.property = newValue;
  }
});

// 测试属性的读写
console.log(obj.property); // 输出: Get value: 
obj.property = 'New Value'; // 输出: Set value: New Value
console.log(obj.property); // 输出: Get value: New Value

而Vue3 中使用了 ES6 中的 Proxy 来拦截数据的变化,能够完全监听数据变化,并且能够监听新增的属性(即Object.defineProperty只能监听对象的一个属性变化,而proxy监听的是整个对象变化)。

let user = {
  name: 'Alice',
  age: 30
};

let proxy = new Proxy(user, {
  get(target, property) {
    console.log(`获取属性 ${property}`);
    return Reflect.get(target, property); // 等同return target[property];
  },
  set(target, property, value) {
    console.log(`设置属性 ${property}${value}`);
    return Reflect.set(target, property, value); // 等同target[property] = value; return true;
  }
});

// 测试属性的读写
console.log(proxy.name); // 输出: 获取属性 name,输出:Alice
proxy.age = 31; // 输出: 设置属性 age 为 31
console.log(proxy.age); // 输出: 获取属性 age,输出:31

批量更新:Vue2 中,在数据变化时,会立即触发虚拟 DOM 的重渲染,如果在一个事件循环中连续修改多个数据(即多次更新 dom),可能会造成性能问题。而 Vue3 中,使用了更高效的批量更新策略,会在下一个事件循环中统一处理数据变化(更新 dom),提高了性能。

在 Vue 2 中的更新策略是尽可能快地进行 DOM 更新,而不是等到下一个事件循环。

在 Vue 3 中,更新 DOM 是在下一个事件循环中进行的,这样可以确保在当前事件循环中的所有代码执行完毕后再进行 DOM 的实际更新。

<template>
  <div>
    <p ref="message">{{ state.message }}</p>
    <button @click="updateMessage">Update Message</button>
  </div>
</template>

<script>
import { ref, nextTick } from 'vue';

export default {
  setup() {
    const state = {
      message: ref('Initial message')
    };

    const updateMessage = () => {
      state.message.value = 'Updated message'; // 本次循环

      // 在下一个事件循环中获取元素尺寸,因为在vue3中需要等dom更新完成才能拿到更新后dom元素
      nextTick(() => {
        const messageElement = messageRef.value;
        console.log('Element size:', messageElement.offsetWidth, messageElement.offsetHeight);
      });
    };

    const messageRef = ref(null);

    return { state, updateMessage, messageRef };
  }
};
</script>

更快的响应式系统:Vue3 中使用了更快的响应式系统(proxy 会跳过没有变化的数据更新,就不会触发页面更新),能够更快地追踪依赖关系,并在数据变化时更快地更新视图。此外,Vue3 还对 Reactivity API 进行了优化,使得开发者能够更灵活地使用响应式数据。

Composition API:Vue3 中引入了Composition API,可以更好地组织代码逻辑,也可以更好地处理数据更新。通过使用 setup 函数和 ref、reactive 等函数进行灵活的响应式添加,能够更方便地对数据进行监听和修改。

3. 为什么 vue 中更改对象和数组时,有时候页面没有进行更新

  1. 对象或数组未在初始时声明为响应式:在 Vue3 中,只有在初始时声明为响应式的对象和数组才能进行监听和更新。如果在初始时没有声明为响应式,那么更改对象或数组时,Vue 无法检测到变化,从而无法进行更新。

  2. 直接更改对象或数组的属性或元素:在 Vue2 中,如果直接更改对象或数组的属性或元素,Vue2 无法检测到变化。因此,应该使用 Vue 提供的响应式方法来更改对象或数组的属性或元素,例如Vue.setVue.$set方法。

  3. 变异方法才会触发更新:Vue2 会对一些常用的数组变异方法进行封装,使其成为响应式的,例如pushpopshiftunshiftsplicesortreverse 方法。但是,如果使用不在这个列表中的变异方法来更改数组,Vue2 就无法检测到变化。因此,应该尽可能使用 Vue2 封装过的变异方法。

  4. 异步更新:在 Vue 中,更新是异步的。当数据发生变化时,Vue 会将更新推迟到下一个事件循环中。因此,如果在一个事件循环中进行多次数据更改,Vue 只会进行一次更新。如果需要在一次事件循环中进行多次数据更改,请使用 Vue.nextTick 方法。

总之,为了确保 Vue 可以正确地监听和更新对象和数组,应该在初始时将它们声明为响应式,避免直接更改对象或数组的属性或元素,尽可能使用 Vue 提供的响应式方法,避免使用不在 Vue 封装列表中的变异方法,以及注意异步更新的特性。

4. 你在项目里面是怎么使用 vuex/pinia?

在我的项目中我使用的是pinia

首先,先通过 npm 安装 pinia

npm install pinia

其次,在根组件 app.vue 中创建 Pinia 实例并将其注册为应用程序的插件

import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app')

接着,在 src 目录下创建一个 store 文件夹中的 index.js,而在使用 Pinia 时,通过引入 Pinia 中的 defineStore 来定义一个 store 实例,类似于 Vuex 的 store。然后我定义了不同的子仓库并暴露(export),来存储对应不同的页面所需的数据与操作,之后再返回(return)数据和操作。而在组件中使用 Pinia 时,需要通过引入,useStore 辅助函数获取 store 实例,并将状态、操作和获取器映射到组件中,以便使用。

import { defineStore } from "pinia";
import { reactive } from "vue";

export const useUserStore = defineStore('user', () => {
    const state = reactive({gridList:[]})
    const loadUser = async () => {}
    return {
        state,
        loadUser
    }
})
import { useUserStore } from "@/store/user";

const userStore = useUserStore();
const gridList = computed(() => userStore.state.gridList);

上海(实习 100-150/天)

该面试是通过视频面试,面试的时候题目相对比较简单,都是一些基础的问题,这也就给了我极大的自信

1. JS 的 Event Loop 你能给我介绍下吗?

因为 JS 是单线程的语言,为了防止一个函数执行时间过长阻塞后面的代码,所以就需要 Event Loop 这个事件环的运行机制。

当执行一段有同步又有异步的代码时,会先将同步任务压入执行栈中,然后把异步任务放入异步队列中等待执行,微任务放到微任务队列,宏任务放到宏任务队列,依次执行。执行完同步任务之后,Event Loop 会先执行宏任务队列的第一个项任务,接着把微任务队列执行清空,微任务队列清空后,又进入宏任务队列,取宏任务队列的第二个项任务进行执行,执行完之后,查看微任务队列是否有任务(场景呢?),有的话,清空微任务队列。然后再执行宏任务队列,反复微任务宏任务队列,直到所有队列任务执行完毕。

image.png

PS: 答完了基本的答案之后,最好可以往下继续延申,不要让面试成为一问一答,这样你的面试就会变的比较丰满,让面试官不至于太枯燥,直到面试官让你停为止。

异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列先执行。

微任务队列的代表就是,then、mutationObserver(浏览器提供,监测 DOM 变化的回调函数被放入微任务队列中)、process.nextTick(仅在 Node.js 环境中,在当前执行完成后立即执行的回调函数)

宏任务的话就是 ajax(网络请求)、script、requestAnimationFrame(每一个帧会调用一次)、UI渲染、一些浏览器api(setTimeout、setInterval、setTmmediate),I/O 操作(例如文件读写等异步操作)

2. 渲染页面的重绘回流你能给我讲一下吗?

  • 重排/回流(Reflow):当 DOM 元素发生了大小,位置,增删改的操作时,浏览器需要重新计算元素的几何属性,重新生成布局,重新排列元素。

  • 重绘(Repaint): 当一个DOM元素的外观样式发生改变,但没有改变布局,重新把DOM元素的样式渲染到页面的过程。

重排和重绘它们会破坏用户体验,并且让UI展示非常迟缓,而在两者无法避免的情况下,重排的性能影响更大,所以一般选择代价更小的重绘。

『重绘』不一定会出现『重排』,『重排』必然会出现『重绘』。

上海(实习 200-210/天)

这场面试很正常,自我感觉含金量也比较高,通过视频面试能够知道,面试官老师人也长得挺帅的,说话和蔼,讲真,人真的挺好的。不过自己还会犯傻,走进思维误区,没有理解清面试官老师的问题,所以在面试中如果没听清楚问题,千万一定要再问一下面试官。

1. 响应式开发你了解吗?响应式是如何实现的呢?

响应式开发是一种灵活性让网站或应用程序在各种屏幕尺寸、浏览器和设备上展现出来,提供良好的用户体验。

响应式开发的实现基于使用CSS媒体查询、弹性布局和流体网格等技术。以下是一些主要的实现方法:

  1. CSS媒体查询:使用CSS媒体查询可以检测设备的屏幕尺寸、分辨率和方向等特性,并根据这些特性应用不同的样式规则。通过定义不同的CSS样式,可以使网页在不同的设备上以不同的方式呈现。

  2. 弹性布局(底层如何实现的?):即(display:flex),使用弹性布局(flexbox)可以创建灵活的布局结构,使内容能够根据屏幕尺寸进行自动调整。弹性布局使得元素的大小、位置和间距能够根据可用空间进行自适应。

  3. 网格布局:即(display:grid),使用流体网格(fluid grid)可以创建基于相对单位(如百分比)的网格系统,使页面的布局能够根据屏幕大小进行缩放和调整。这样可以确保内容在不同屏幕尺寸上均匀分布和对齐。

  4. 百分比%:比如当浏览器的宽度或者高度发生变化时,通过百分比单位,通过百分比单位可以使得浏览器中的组件的宽和高随着浏览器的变化而变化,从而实现响应式的效果。

  5. vw/vh:css3中引入了一个新的单位vw/vh,与视图窗口有关,vw表示相对于视图窗口的宽度,vh表示相对于视图窗口高度。 任意层级元素,在使用vw单位的情况下,1vw都等于视图宽度的百分之一。

  6. rem:rem单位是相对于字体大小的html元素(即html元素的font-size),也称为根元素,所以只需要获取设备的宽度,然后再结合设计稿,进行一个计算,算出比例把font-size修改一下即可。

2. 媒体查询这个你了解吗?

我在使用less预编译样式中使用过媒体查询(这里提一嘴自己使用过less或者其他的预编译),媒体查询使用@media规则来定义,其语法如下:

@media mediatype and|not|only (media feature) {
    /* CSS样式规则 */
}

其中,mediatype指定了媒体类型,如screen表示屏幕媒体、print表示打印媒体等。andnotonly是逻辑运算符,用于组合多个条件。media feature表示设备的特性,如width表示屏幕宽度、orientation表示屏幕方向等。

下面是一些常用的媒体特性:

  • width:屏幕宽度。
  • height:屏幕高度。
  • device-width:设备屏幕宽度。
  • device-height:设备屏幕高度。
  • orientation:屏幕方向(横向或纵向)。
  • aspect-ratio:屏幕宽高比。
  • color:设备的颜色位深。
  • resolution:屏幕分辨率。

通过结合不同的媒体特性和条件,可以根据设备的不同特性来应用不同的CSS样式。例如,可以使用媒体查询来定义在屏幕宽度小于某个阈值时应用的样式,或者根据屏幕方向调整布局等。

以下是一个示例,演示如何使用媒体查询在屏幕宽度小于600px时应用特定的样式:

@media screen and (max-width: 600px) {
    /* 在屏幕宽度小于600px时应用的样式 */
    body {
        font-size: 14px;
    }
    /* 其他样式规则 */
}

这样,当浏览器窗口宽度小于600px时,body元素的字体大小将被设置为14px。

3. CSS的伪元素你知道是什么东西吗?

伪元素是CSS中的一种特殊选择器,用于向选中的元素的特定部分添加样式,而不需要在HTML结构中添加额外的元素(在dom文档里是如何表现的?)。伪元素使用双冒号::作为标识符,用于区分伪类(pseudo-class)和伪元素。(在旧版本的CSS中,单冒号:也被用作伪元素的标识符,但在CSS3规范中,建议使用双冒号以区分伪类和伪元素。)

以下是一些常用的CSS伪元素:

  1. ::before:在选中元素的内容之前插入一个生成的内容。
  2. ::after:在选中元素的内容之后插入一个生成的内容。

这些伪元素可以与CSS的属性和样式一起使用,例如 contentcolorbackground 等,以为选中的元素的特定部分添加样式。

以下是一个示例,演示如何使用伪元素为元素的内容之前插入一个生成的内容并应用样式:

p::before {
    content: "前缀:";
    font-weight: bold;
    color: blue;
}

在上述示例中,::before 伪元素被应用于 <p> 元素,它在该段落的内容之前插入了一个生成的文本"前缀:",并为该生成的文本应用了加粗字体和蓝色的颜色。

4. 介绍一下HTML5的特有的标签?

  1. 语义化标签
  • <article>:用于表示独立的、完整的文章内容。
  • <section>:用于表示页面或应用程序中的一个区域,可以包含一个标题。
  • <header>:用于表示页面或应用程序的标题,通常包含logo和导航。
  • <footer>:用于表示页面或应用程序的页脚部分,通常包含版权信息、联系方式等。
  • <nav>:用于表示导航链接的集合,通常包含一组指向其他页面的链接。
  • <aside>:用于表示页面或应用程序的旁边栏,通常包含相关的信息、广告、链接等。
  1. <video>:用于嵌入视频文件,可以使用 <source> 标签指定多个视频文件,以便在不同的浏览器和设备上播放。
  2. <audio>:用于嵌入音频文件,可以使用 <source> 标签指定多个音频文件,以便在不同的浏览器和设备上播放。
  3. <canvas>:用于创建绘图区域,可以使用JavaScript在上面绘制图形、动画等。
  4. <progress>:用于显示进度条,表示任务完成的进度。

5. 你如果要做一个搜索引擎比较友好的页面,应该是要做到些什么东西呢?

  1. 使用语义化的HTML标记:使用适当的HTML标签来正确表示页面的结构,如使用<header><nav><article>等。

  2. 使用有意义的标题:使用恰当的标题标签(<h1><h2>等)来突出页面的主题和内容。

  3. 提供关键词和描述:在HTML文档中,可以通过<meta>标签来定义各种属性,比如页面的描述和关键字。

    keywords:向搜索引擎说明你的网页的关键词

     `<meta name="keyword" content="前端,面试,大厂">`
    

    description:告诉搜索引擎你的站点的主要内容

    <meta name="description" content="页面描述,包含关键字和吸引人的内容">
    
  4. 使用合适的图像标签:为图片使用适当的alt属性,描述图片内容,方便搜索引擎理解图像。

  5. 使用服务端渲染(SSR)的框架,比如vue中的Nuxtreact中的Next,即在服务端生成完整的 HTML 页面,并将其发送给浏览器。这使得搜索引擎可以更好地理解和索引页面的内容,因为它们可以直接看到渲染后的页面。

6. 介绍一下flex的布局吧?

## 阮一峰老师有一个博客,专门讲解一个flex布局,你可以讲一下flex布局吗?

7. 后端和前端的一些交互,你了解是什么东西?

后端和前端之间的交互通常通过前后端分离的架构来实现,其中前端负责展示界面和用户交互,后端负责处理数据和逻辑操作。

以下是一些常见的后端和前端交互的方式和技术:

  1. RESTful API:使用基于 HTTP 的 RESTful API ,前端可以向后端发送请求并获取数据。后端通过 Postman 或者 Apifox 提供 API 接口,通过 GETPOSTPUTDELETE 等 HTTP 方法来处理前端请求,并返回相应的数据。前端可以使用 Ajax、Fetch API 或 axios 等工具来发送请求和处理响应。

  2. 数据传输格式前后端交互时需要使用一致的数据传输格式。常见的数据格式包括 JSON(JavaScript Object Notation)和 XML(eXtensible Markup Language)。前端可以发送数据请求给后端,后端将数据以指定的格式进行封装和返回给前端。

  3. 然后我还使用过 nodejs 中的 koa 洋葱模型简单搭建过一个 MVC 结构的服务器。

8. 那你有遇到过跨域问题吗?实际解决方法?

  • wepback-dev-serve代理 白嫖即食:构建工具的proxy代理配置区别(解决跨域)

  • CORS:通过在服务器配置响应头,Access-Control-Allow-xxx字段来设置访问的白名单、可允许访问的方式等

  • nginx 代理服务器配置:有些项目是在 nginx 服务器上,配置好相关的代理即可,类似 devServer

  • JSONP:在DOM文档中,使用<script>标签,但却缺点只能发 GET 请求并且容易受到XSS跨站脚本攻击

  • postMessage

  • html原生的websocket

讲了这些东西之后,面试官就让我说一下实际解决方法,像jsonp,postMeassage都不是常用的

然后我就把整个CORS跨域的过程给讲了一遍,包含了浏览器的跨域拦截

首先,浏览器进行了一个跨域请求,向服务器发送了一个预检(options)请求,服务器会在响应头部中设置Access-Control-Allow-Origin和Access-Control-Allow-Methods等配置,告知浏览器是否允许跨域请求。如果该页面满足服务器设置的白名单和可允许访问的方式,那么服务器就允许跨域访问,浏览器就会接受响应,进行真实的跨域请求,否则就会报错。

面试基本必问问题

1. 你有什么想问我的吗?(问到这里一场面试结束了)

  1. 团队使用的技术栈有哪些?

  2. 团队人员结构大致是怎样的啊或者说一条业务线参与人员?(大致了解一下团队中业务的协作)

  3. 团队主要负责的业务有哪些?(如果面试官详细对你讲解,说明有戏)

  4. 平时遇到的问题有就问,没有就算了(毕竟面试你的人开发经验很丰富)

2. 你写项目的时候碰到过印象里比较深刻的一些bug或困难,你怎么解决的?

在这里基本需要在面试前进行整理和准备的,如果万一没有准备,但又答不出来,其实这部分可以从侧面分析这个问题。

问你遇到的bug可能一时半会儿不知道怎么回答,但如果问你是如何实现项目中的某个功能,这时候就好回答了,只需要转换回答成没有这个功能代码会出现什么问题。所以面试官不是问你有什么bug,而是你在项目中有哪些亮点。

前端中常见的一些bug

  1. JavaScript 错误:在应用程序中使用的 JavaScript 代码可能包含语法错误或逻辑错误,这些错误会导致应用程序在执行时出现问题,从而导致性能问题。
  2. DOM 操作错误:通过 JavaScript 操作文档对象模型 (DOM) 可以更新应用程序中的 HTML 元素。但是,如果 DOM 操作不正确或在操作过程中执行了太多的操作,可能会导致性能问题。
  3. 页面重绘:当用户与页面交互时,浏览器会执行重新绘制和重排操作。如果页面包含太多的重绘操作或页面重排操作,则可能导致性能问题。
  4. 图像和资源加载:在加载图像和其他资源时,如果没有正确管理缓存或使用适当的图像格式,则可能导致性能问题。
  5. 前端框架错误:使用前端框架时,可能会出现错误或不良的编码实践,这些问题可能会导致性能问题。

axios响应拦截

遇到bug:我是使用mockjs来模拟后端的接口,当时我在设置端口的返回值时,我返回数据有一个状态码以及把json数据中export出来的detail数据添加到data这个需要返回的数据(代码如下),这导致我在获取接口里的数据时需要多.data(引用一层data),当时我没意识,结果一直获取不到数据。

解决办法:通过使用axios进行请求和响应,并在响应的时候设置一个拦截,对响应进行一番处理之后就可以直接拿到接口返回的值,而不会导致接口返回的值不会有太多的嵌套了。

Mock.mock(/\/detail/, 'get', () => {
    return {
        code: 0,  // 返回状态码
        data: detail  // 返回数据
    }
})
import axios from "axios";
// 响应拦截器
axios.interceptors.response.use((res) => {
    return res.data
})

图片和组件的懒加载

遇到的bug:我做的项目使用了很多的组件页面和大量的图片,导致在加载页面时耗时比较久,以及在页面的切换时很多暂时不需要的页面组件一次性全部加载了,导致整个项目的性能非常差。

解决办法

图片懒加载:在App.vue中引入VueLazy并且使用app.use启用它,然后把图片中的src改成v-lazy

<img :src="xxx.png">

改成

<img v-lazy="xxx.png">

页面组件懒加载:在router配置中的component,把直接在代码一开始引入组件页面,改成箭头函数式引入。

    import Home from '@/views/Home/Home.vue' 
    {
        path: '/',
        component: Home
    },

改成

    {
        path: '/',
        component: () => import('@/views/Home/Home.vue')
    },

搜索界面节流

遇到的bug:在搜索界面的时候,当我一直点击搜索时,它会频繁的进行请求,造成了不必要的性能损耗。

解决办法:使用loadash库中的节流API,进行对触发搜索事件进行节流,防止用户进行频繁的搜索请求导致性能损耗。


import _ from 'lodash'

const value = ref(null)

const ajax1 = () => {
    console.log('开始搜索,搜索内容为' + value.value)
}

let debounceAjax1 = _.debounce(ajax1, 1000)

const onSearch = () => {
    if (!value.value) {
        showToast('搜索内容为空,请输入内容')
        return
    }
    debounceAjax1()
}

404页面

遇到的bug:当输入url中没有在路由配置中配置过的路径时,页面它会出现空白,并且浏览器发出警告,如果我这个项目上线的话,可能会造成用户的体验不友好和搜索引擎不友好。

解决办法:在路由配置中再配置一个404页面的路径,这样就能使用户不管怎么输入不合规的url后,都会提示用户输错了网址。

    {
        path: '/404',
        name: 'NotFound',
        component: () => import('@/views/NotFound/Index.vue')
    },
    // 所有未定义路由,全部重定向到404页
    {
        path: '/:pathMatch(.*)',
        redirect: '/404'
    }

结语

面试,说到底,迈开第一步其实是最重要的,别想那么多,要抱着反正有那么多家公司,我没必要非要去你这一家的心态去面试,把面试官当作一个久久未联系过的老朋友,突然有一天碰到了聊起天。面试完之后一定及时的整理复盘,不断地让自己变得更加牢固。