2025Web前端八股面试题,精选题型,附带详细答案解析,堪称最强题库

265 阅读17分钟

2025面试前必看web前端八股文题库!25年准备换工作或者想要跳槽的小伙伴快来看啊!

需要完整的可以看文末

JavaScript

1.不会冒泡的事件有哪些?

参考答案:

在JavaScript和浏览器中,绝大多数事件都会按照DOM事件流模型

冒泡,即事件会从目标元素开始向上冒泡到它的父元素,并最终到达document元素。然而,也有一些事件是不会冒泡的。这些事件通常直接在目标元素上触发,并不会向上传播。

以下是一些不会冒泡的事件的示例:

  1. focus:当元素获得焦点时触发,例如通过键盘或鼠标点击。这是一个不会冒泡的事件。

  2. b1ur:当元素失去焦点时触发。这也是一个不会冒泡的事件。

  3. focusin:与focus类似,但会在元素或其父元素上触发(冒泡),因此这个事件是特例。

  4. focusout:与blur类似,但会在元素或其父元素上触发(冒泡),因此这个事件是特例。

  5. 1oad:当图像、音频、视频或其他资源加载完成时触发。例如,在img元素上触发的1oad事件不会冒泡。

  6. un1oad:当页面即将被导航离开时触发。这通常用于执行清理工作,也不会冒泡。

  7. stop:通常与media元素相关,例如audio或video元素。这是在媒体播放停止时触发的事件。

  8. readystatechange:当document的readyState改变时触发。这通常在页面加载时使用。

  9. scro11:当元素滚动时触发。这个事件在某些浏览器中可能会冒泡。

这些事件通常直接在目标元素上触发,并且不会传播到父元素上。

2. mouseEnter和mouseOver有什么区别?

参考答案:

mouseenter和mouseover是两个用于处理鼠标进入元素时的事件,但它们在一些关键点上有所不同:

  1. 事件冒泡:
  • mouseenter:这个事件在鼠标指针首次进入特定元素(或其子元素)时触发。当鼠标进入元素时,会触发该元素的mouseenter事件,但不会在元素的子元素上冒泡。因此,该事件通常用于检测鼠标首次进入元素时的动作。
  • mouseover:这个事件在鼠标指针移动到某个元素上时触发,不论它是直接在这个元素上触发还是在其子元素上触发。当鼠标进入一个元素时,它会在该元素上触发mouseover事件,然后冒泡到父元素。
  1. 事件触发范围:
  • mouseenter:当鼠标进入元素自身时触发,只在目标元素上触发,不会因为鼠标移动到其子元素上而再次触发。
  • mouseover:不仅在目标元素上触发,也在其子元素上触发。所以,如果鼠标从一个子元素移动到另一个子元素,这些元素的父元素会触发多个mouseover事件。
  1. 事件对象的属性:
  • mouseenter:事件对象通常会有relatedTarget属性,它指向鼠标移动前的那个元素。如果relatedTarget指向目标元素或为nul1,那么事件就不会触发。
  • mouseover:事件对象也会有relatedTarget属性,通常指向从中离开的那个元素。

使用场景

  • mouseenter更适合用来检测鼠标首次进入某个元素时的行为。
  • mouseover更适合用来检测鼠标在元素或其子元素之间移动时的行为,因为它冒泡。

在实际使用时,如果你只想在鼠标首次进入元素时触发某些行为(比如显示一个提示),可以使用mouseenter:如果你希望在鼠标移动到某个元素或其子元素上时都触发某些行为(比如动态改变样式),可以使用mouseover。

3.MessageChannel是什么,有什么使用场景?

叁考答案:

MessageChanne1是一个JavaScript API,用于在两个独立的执行环境(如 Web Workers 或者不同的 browsingcontexts)之间建立双向通信的通道。MessageChannel提供了两个通信端点(port1和port2),可以在两个不同的执行环境之间传递消息,并通过事件监听的方式来处理这些消息。

使用场景包括但不限于:

  1. Web Workers 通信:在 Web开发中,MessageChannel通常用于在主线程和 Web Worker 之间建立通信通道,以便主线程与Worker之间传递消息和数据。

  2. 不同浏览上下文(browsing context)之间的通信:在现代浏览器中,多个标签页、iframe或者其他类型的browsing context 可以通过MessageChannel实现通信。

  3. SharedWorker 通信:MessageChanne1可以用于在主线程和 Shared Worker 之间建立通信通道。

  4. 服务端和客户端之间的通信:MessageChannel可以用于客户端(如浏览器)与服务端(如WebSocket服务器)之间的通信,特别是在与WebSocket或其他类似技术结合使用时。

  5. 异步任务处理:在某些场景中,使用MessageChannel可以更方便地处理异步任务,因为它提供了独立于主线程的通信通道。

使用示例

下面是一个简单的示例,展示如何使用MessageChanne1在主线程和WebWorker之间建立通信通道:

// 创建 MessagechanneL
const channel = new Messagechannel();
const port1 = channel.port1;
const port2 = channel.port2;

// 在主线程中
const worker = new worker('worker.js');
worker.postMessage({ port: port2 },[port2]);
port1.onmessage = function(event) {
console.log('Received message from worker:', event.data);
};

// 发送消息给worker
port1.postMessage('Hello, Worker!');

在上面的示例中,我们创建了一个MessageChannel,并通过port1和port2进行通信。我们将port2发送给Web Worker,port1留在主线程。然后,主线程可以通过监听port1的onmessage事件来接收来自Web Worker 的消息,并通过port1.postMessage()向 Web Worker 发送消息。


React

1.下面代码中,点击"+3”按钮后,age的值是什么?

import {usestate } from 'react';

export default function Counter() {
const [age, setAge] = usestate(42);
function increment( {
setAge(age + 1);
}
return (
<>
<h1>Your age: {age}</h1>
<button onclick={() =>{
incrementO;
incrementO;
increment();
}}>+3</button>
</>
);
}

参考答案:

点击+3 时,可能只更新为43。

这是因为setAge(age+1)即使多次调用,也不会立即更新组件状态,而是会进行合并,最终只触发一次重新渲染。

如果要实现调用三次就增加3,可以将increment改为函数式更新:

function increment( {
setAge(a => a + 1); // 西数式更新
}

2. React Portals有什么用?

参考答案:

React Portals是React提供的一种机制,用于将子组件渲染到父组件DOM层次结构之外的位置。它在处理一些特
殊情况下的UI布局或交互时非常有用。以下是一些使用ReactPortals的常见情况:

  1. 在模态框中使用:当你需要在应用的根DOM结构之外显示模态框(对话框)时,React Portals可以帮助你将
    模态框的内容渲染到根DOM之外的地方,而不影响布局。

  2. 处理z-index问题:在一些复杂的布局中,可能存在z-index的层级关系导致组件无法按照预期的方式叠加显
    示。使用ReactPortals可以将组件渲染到具有更高z-index的容器中,以解决这些问题。

  3. 在全局位置显示组件:如果你希望某个组件在页面的固定位置显示,而不受父组件的定位影响,ReactPortals
    可以将该组件渲染到body或其他容器中。

  4. 在动画中使用:当你需要在页面中的某个位置执行动圆时,ReactPortals可以帮助你将动画的内容渲染到离该
    位置更近的DOM结构中,以提高动画性能。

使用 React Portals 的基本步骤如下:

import React from 'react';
import ReactDom from 'react-dom';

function MyPortalComponent(){
return ReactDoM.createPortal(
// 子组件的内容
<div>
This is rendered using a portal!
</div>,
// 渲染目标的 DOM 元素
document.getElementById('portal-root')
);
}

//在应用的根组件中渲染MyPortaLComponent
function AppO{
return (
<div>
{/*此的内容在正常的DOM结构中*/}
<p>This is a normal component.</p>
{/> 使用React Portals 渲染到'portal-root'元素外 */}
<MyPortalComponent />
</div>
);
}

export default App;

在上面的例子中,MyPortalComponent中的内容会被渲染到具有 id 为'portal-root'’的DOM 元素外。


Vue

1.Vue有了数据响应式,为何还要diff?

参考答案:

Vue中的数据响应式和虚拟DOM

的dif算法是两个不同的概念,它们分别解决了不同的问题,相互协作以提高页面渲染的效率和性能。

数据响应式

Vue 的数据响应式系统通过Object.defineProperty或者ES6的Proxy来实现,主要解决了以下问题:

1.数据绑定:保证了视图与数据的同步更新,当数据发生变化时,视图会自动更新,避免了手动操作DOM的繁琐和易出错性。

2,依赖追踪:Vue能够追踪每个数据的依赖关系,即哪些组件或者计算属性依赖于某个数据。当数据变化时,自动更新依赖的组件或者计算属性。

虚拟 DOM 和 Diff 算法

虚拟DOM是一种内存中的表示结构,它是对真实DOM的抽象。Diff算法是一种高效更新DOM的策略,它通过比较新日虚拟DOM树的差异,最小化了更新操作,提高了页面的渲染效率。

为什么还需要Diff 算法?

  1. 性能优化:直接操作真实DOM是非常昂贵的,而虚拟DOM可以在内存中快速进行比较和计算差异。Diff算法帮助减少了更新操作的次数和范围,从而提升了页面渲染的性能。

  2. 批量更新:Diff算法能够将多次DOM更新操作合并为一次,避免了频繁的DOM操作,减少了浏览器的重排和重绘。

  3. 跨平台兼容:虚拟DOM和Diff 算法使得Vue可以运行在不同的平台上,例如浏览器、Weex等,统一了渲染逻辑和数据响应式的实现。

  4. 更新效率:即使是响应式系统可以自动更新视图,但是如果每次数据变化都直接操作真实DOM,可能会带来性能问题。Diff算法可以智能地比较新日DOM树的变化,只更新必要的部分,从而提高了更新效率。

综合作用

Vue的数据响应式系统和虚拟DOM+Diff 算法是紧密协作的:

  • 数据响应式:保证了数据和视图的同步更新,提供了便捷的开发方式。
  • 虚拟DOM+Diff算法:提高了页面渲染的效率和性能,减少了不必要的DOM操作,确保了页面的流畅性和响应性。

总体来说,数据响应式和Diff算法是为了解决不同层面的问题,结合起来使得Vue能够提供高效、流畅的用户体验。

2.vue3为什么不需要时间分片?

参考答案:

Vue3不需要时间分片(timeslicing)主要是因为它的核心渲染机制和性能优化策略已经足够高效,能够在大多数情况下提供流畅的用户体验。以下是详细的原因:

  1. 编译器优化

Vue3引入了一个全新的编译器,能够生成更高效的渲染数。这个编译器在编译过程中进行了一系列优化,例如:

  • 静态提升:将不变的节点提升为常量,只在初次渲染时计算一次。
  • 预字符串化:将静态内容直接转化为字符串,减少了运行时的开销。
  • 缓存事件处理程序:避免了不必要的重新绑定。

这些优化措施大大减少了Vue3在更新DOM时的计算量,使得渲染过程更加高效。

  1. 响应式系统的改进

Vue 3使用了基于代理的响应式系统,替代了Vue2中基于Object.defineProperty的实现。新的响应式系统更加高效,具备以下优点:

  • 精细的依赖追踪:只追踪实际使用的属性,避免了不必要的依赖收集。
  • 懒惰计算:仅在需要时才计算依赖,减少了计算量。

这些改进使得Vue3能够更快速地响应数据变化,从而减少了渲染开销。

  1. 虚拟 DOM 和 Diff 算法的优化

Vue 3对虚拟DOM及其diff算法进行了优化,使得差异计算更加高效:

  • 静态标记:编译期间标记静态节点,跳过不变的部分。
  • 块级优化:将动态节点分块,只对发生变化的块进行更新。

这些优化措施减少了DOM更新的频率和范围,提高了整体渲染性能。

  1. 单次异步队列

Vue3的更新机制基于单次异步队列(singleasynchronousqueue),它确保在同一事件循环中只进行一次批量更新。这种方式减少了不必要的重复计算和DOM操作,使得更新过程更加高效。

5.自动批处理

Vue3实现了自动批处理机制,在同一个事件循环中对多次数据更新进行合并,从而减少了渲染次数。这种机制在避免频繁重绘的同时,保证了界面的流畅性。

  1. 现代浏览器的性能

现代浏览器的性能已经得到了极大的提升,尤其是在处理JavaScript和DOM操作方面。Vue3的优化能够充分利用这些性能改进,从而在绝大多数情况下不需要时间分片。

总结

Vue3通过编译器优化、响应式系统改进、虚拟DOM和Diff算法优化、单次异步队列、自动批处理等技术手段,大幅提升了渲染效率和性能。再加上现代浏览器的性能提升,使得Vue3能够在大多数情况下提供流畅的用户体验,而无需借助时间分片等复杂的技术。


工程化

1. package.json文件中的devDependencies和dependencies对象有什么区别?

参考答案:

前端项目的package.json文件中,dependencies和devDependencies对象都用于指定项目所依赖的软件包,但它们在项目的开发和生产环境中的使用有所不同。

  1. dependencies:
  • dependencies是指定项目在生产环境中运行所需要的依赖项。
  • 这些依赖项通常包括运行时需要的库、框架、工具等。
  • 当你通过npm install或npm ci安装依赖时,默认会安装dependencies中的包。
  • 这些依赖项会被打包和部署到生产环境中,因此它们对于项目的运行是必需的。
  1. devDependencies:
  • devDependencies是指定在开发过程中所需要的依赖项。
  • 这些依赖项通常包括开发、测试、构建、部署等过程中所需的工具、库等。
  • 例如,测试框架、构建工具、代码检查工具等通常属于devDependencies。
  • 当你在开发环境中使用npm install安装依赖时,只会安装dependencies中的包。要安装devDependencies中的包,你需要额外使用npm install --dev或npm install --only=dev等命令。
  • 这些依赖项不会被打包到生产环境中,因为它们只在开发过程中需要,对于实际部署和运行项目并不需要。

总的来说,dependencies中的依赖项是项目运行所必需的,而devDependencies中的依赖项则是在开发过程中需要的辅助工具和库。

2. webpack 5的主要升级点有哪些?

叁考答案:

  • 持久缓存(Persistent Caching):Webpack 5
  • 引I入了更好的持久缓存机制,利用了更稳定的HashedModuleIdsPlugin和NamedChunksPlugin,以改善构建性能。

  • Tree-shaking改进:Webpack 5对Tree-shaking进行了改进,提供了更好的代码优化,以便删除未使用的代码。

  • 支持WebAssembly(WASM):Webpack 5对WebAssembly提供了原生的支持,使得在项目中使用WebAssembly 更加方便。

  • ·支持ES6模块导入(Dynamic Import):Webpack 5对动态导入语法(importO)提供了更好的支持,可以更轻松地进行代码分割。

  • 模块联邦(ModuleFederation):这是Webpack5中的一项重大功能,允许将多个独立的Webpack构建连接在一起,实现模块共享,从而更好地支持微服务架构。

  • 缓存组(CachingGroups):新的缓存组概念被引入,可以更细粒度地控制模块的缓存策略。

  • 内置代码分割优化(optimization.splitChunks):Webpack 5通过optimization.splitChunks进行了重新设计,提供了更灵活的配置选项,使得代码分割更为强大和易用。

  • 默认配置优化:Webpack5默认配置中的一些优化,使得开箱即用的性能更好。

  • 提高构建性能:Webpack5引入了一些性能优化,包括更快的持久化缓存、更快的构建速度等。

  • 移除废弃特性:作为更新,Webpack5移除了一些过时的特性和API,因此在升级时需要注意潜在的破坏性变化。

3. 说下Vite的原理

这里的背景介绍会从与Vite紧密相关的两个概念的发展史说起,一个是JavaScript的模块化标准,另一个是前端构建工具。

共存的模块化标准

为什么JavaScript会有多种共存的模块化标准?因为js在设计之初并没有模块化的概念,随着前端业务复杂度不断提高,模块化越来越受到开发者的重视,社区开始涌现多种模块化解决方案,它们相互借鉴,也争议不断,形成多个派系,从CommonJs开始,到ES6正式推出ES Modules规范结束,所有争论,终成历史,ES Modules也成为前端重要的基础设施。

  • CommonJS:现主要用于Node.js(Node@13.2.0开始支持直接使用ESModule)
  • AMD:require.js依赖前置,市场存量不建议使用
  • CMD:sea.js就近执行,市场存量不建议使用
  • ES Module:ES语言规范,标准,趋势,未来

而Vite的核心正是依靠浏览器对ES Module规范的实现。

发展中的构建工具

近些年前端工程化发展迅速,各种构建工具层出不穷,目前Webpack仍然占据统治地位,npm每周下载量达到两千多万次。下面是我按npm发版时间线列出的开发者比较熟知的一些构建工具。

当前工程化痛点

现在常用的构建工具如Webpack,主要是通过抓取-编译-构建整个应用的代码(也就是常说的打包过程),生成一份编译、优化后能良好兼容各个浏览器的的生产环境代码。在开发环境流程也基本相同,需要先将整个应用构建打包后,再把打包后的代码交给dev server(开发服务器)。

Webpack等构建工具的诞生给前端开发带来了极大的便利,但随着前端业务的复杂化,js代码量呈指数增长,打包构建时间越来越久,devserver(开发服务器)性能遇到瓶颈:

  • 缓慢的服务启动:大型项目中devserver启动时间达到几十秒甚至几分钟。
  • 缓慢的HMR热更新:即使采用了HMR模式,其热更新速度也会随着应用规模的增长而显著下降,已达到性能瓶颈,无多少优化空间。

缓慢的开发环境,大大降低了开发者的幸福感,在以上背景下Vite应运而生。


篇幅限制,有需要完整的可以点此获取