前端面试题汇总(持续更新中)

1.说说Real diff 算法是怎么运作的?从tree层到component层到element层分别讲解。

Real diff 算法在React中主要指的是React在进行虚拟DOM对比时所使用的算法。它从tree层(树层)、component层(组件层)到element层(元素层)进行分层比较:

Tree层:React使用Fiber树来表示组件的树状结构。在比较两棵Fiber树时,React会进行深度优先遍历,比较每个节点的类型和属性。如果节点类型不同,则直接替换;如果类型相同,则进一步比较子节点和属性。
Component层:在组件层,React会检查组件的类型和属性是否发生变化。如果组件类型不同,则销毁旧组件并创建新组件。如果类型相同,则可能触发如shouldComponentUpdate等生命周期方法(注意,componentWillMount、componentWillReceiveProps、componentWillUpdate等已被废弃)。
Element层:在元素层,React会比较具体的虚拟DOM元素。如果元素类型或属性不同,则进行相应的更新。React使用高效的Diff算法来最小化对实际DOM的操作,特别是通过key属性来优化节点移动和重排的性能。

2.说说React 生命周期中有哪些坑?为什么要移除will相关生命周期?

React生命周期中的坑主要包括:

getDerivedStateFromProps容易编写反模式代码:使受控组件和非受控组件区分模糊。
componentWillMount和componentWillReceiveProps的弃用:它们在新版React中被标记为弃用,主要原因是新的异步架构可能导致它们被多次调用,从而引发不必要的性能问题和逻辑错误。
componentWillUpdate的弃用:同样由于新的异步渲染机制,它被标记为不推荐使用。
移除will相关生命周期的原因主要是为了提高React应用的性能和稳定性,避免由于异步渲染机制导致的多次调用和潜在的bug。

3.调和阶段setState干了什么?

在React的调和阶段(也称为更新阶段),当调用setState时,React会将传入的新状态与组件当前的状态合并,并重新计算组件的输出。然而,需要注意的是,在调和阶段,setState是“异步”的(在合成事件和生命周期钩子中),React会将这些更新收集起来,并在当前的事件处理或生命周期钩子完成后,统一进行DOM的更新。这有助于减少不必要的渲染次数,提高性能。

4.说说css3的新特性有哪些?

CSS3引入了许多新特性,包括但不限于:

选择器:增加了更复杂的选择器,如属性选择器、伪类选择器等。
布局:支持多栏布局(Flexbox)、网格布局(Grid Layout)等。
动画:提供了动画(Animations)和过渡(Transitions)效果,支持更复杂的动画效果。
媒体查询:允许根据不同的媒体类型和条件应用不同的样式规则。
边框和背景:增加了边框圆角(border-radius)、阴影(box-shadow、text-shadow)等效果,以及更复杂的背景图像和渐变。

5.说说redux的工作流程。

Redux的工作流程可以概括为以下四个步骤:

创建Action:当需要改变状态时,首先创建一个包含type属性和其他必要信息的action对象。
分发Action:使用store的dispatch方法将action分发到store中。
执行Reducer:store会调用与action的type属性相匹配的reducer函数,并传入当前的state和action作为参数。Reducer函数会返回一个新的state对象。
更新State:store会使用reducer返回的新state来更新自己的内部状态,并通过订阅(subscribe)机制通知所有监听器(如React组件)状态已发生变化。

6.React合成事件的原理,有什么好处和优势?

原理:React的合成事件是一种封装了浏览器原生事件对象的高级事件机制。React在组件树中使用事件委托机制,将事件处理程序绑定在组件树的根节点上,统一管理和处理组件内部和外部的事件。当在组件中触发事件时,React会将该事件包装成一个合成事件对象,并在组件树中冒泡传递,直到根节点处。

好处和优势:

跨浏览器兼容性:React的合成事件可以屏蔽浏览器的差异,保证在各种浏览器上运行一致。
性能优化:React的合成事件可以对事件进行池化处理,重用事件对象,避免创建大量的事件对象,从而提高性能。
事件委托:通过事件委托机制,避免了多次绑定事件处理程序的问题,提高了效率。
支持自定义事件:React的合成事件可以支持自定义事件,开发者可以自定义组件事件,提供更多的自定义能力。

7.为什么 react 元素会有一个$$type属性?

React元素中的 t y p e o f 属性主要用于防止 X S S (跨站脚本)攻击。由于 S y m b o l 无法被序列化, R e a c t 可以通过检查一个对象是否具有 typeof属性主要用于防止XSS(跨站脚本)攻击。由于Symbol无法被序列化,React可以通过检查一个对象是否具有 typeof属性主要用于防止XSS(跨站脚本)攻击。由于Symbol无法被序列化,React可以通过检查一个对象是否具有typeof属性来判断该对象是否是一个React元素,而不是从数据库或其他不可信的源中直接获取的普通JavaScript对象。如果没有$$typeof属性,React会拒绝处理该元素,从而增强应用的安全性。这是React在内部使用的一种安全措施。

8.React有哪些优化性能的手段?

React优化性能的手段包括但不限于以下几种:

使用生产环境构建:确保在部署到生产环境时使用了React的生产版本,它会进行代码压缩和性能优化。
减少不必要的渲染:利用React.memo、PureComponent或自定义shouldComponentUpdate来避免不必要的组件重新渲染。
列表渲染性能优化:在列表中使用唯一的key属性来标识每个子元素,帮助React识别每个元素的身份,减少重复渲染和操作的开销。
避免多层级的嵌套组件:尽量保持组件的层级扁平和简洁,以减少diff算法的复杂度和渲染时间。
懒加载组件:对于非首屏加载的组件,使用懒加载方式可以减少初始渲染体积,提高首屏加载速度。
使用合适的库和插件:选择合适且经过优化的第三方库和插件,避免过多冗余的库,以减少性能开销。
代码分割和懒加载:通过React.lazy()和Suspense组件实现代码分割和按需加载,进一步减小初始加载体积。

9.说说你对fiber架构的理解?解决了什么问题?

Fiber架构是React 16中引入的一种新的渲染机制,旨在解决React在渲染过程中可能出现的性能问题,提升用户体验。Fiber架构通过将渲染过程拆分为多个小任务(Fiber),并赋予不同的优先级,实现了增量渲染和可中断、恢复的机制。

Fiber架构解决了以下几个问题:

长时间阻塞问题:传统的React渲染机制是基于递归的,可能会长时间占用主线程,导致页面卡顿。Fiber架构通过将渲染过程拆分为多个任务,可以根据优先级和时间片进行调度,避免了长时间阻塞。
用户体验:Fiber架构允许React在用户交互时立即响应,提高了页面的响应性能和流畅度。
增量渲染:Fiber架构可以将渲染工作分布在多个帧中完成,逐步更新用户界面,减少了渲染的延迟和复杂度。
错误处理和并发模式:Fiber架构为React引入了新的错误处理机制,并为未来的并发模式奠定了基础,使得React能够更好地处理复杂场景和大型应用。

10.说说你对redux中间件的理解?常用的中间件有哪些?实现原理?

Redux中间件是一种拦截Redux的派发过程,并对派发的action进行一系列处理的机制。它允许我们在action到达reducer之前或之后执行自定义的逻辑,如日志记录、异步操作、路由等。

常用的Redux中间件包括:

redux-thunk:支持在action中进行异步操作,允许我们dispatch一个函数类型的action。
redux-saga:基于Generator函数的异步流程管理库,提供了强大的工具和API来处理异步操作。
redux-promise:支持在action中返回Promise对象,等待Promise完成后再将结果作为新的action发送到reducer。
redux-logger:用于在控制台输出Redux的日志信息,帮助开发者调试。
Redux中间件的实现原理基于函数的柯里化特性。当我们使用applyMiddleware函数将中间件应用到Redux Store上时,中间件被按顺序组合成一个链式结构。每个中间件都能够接收Store的dispatch和getState函数作为参数,并返回一个新的dispatch函数。这样,在派发action时,action会从中间件链式结构的起点开始流经每个中间件,每个中间件可以对action进行处理、修改或终止。最后,经过中间件链的处理后,action将达到reducer进行状态更新。

11.如何使用css实现一个三角形,写出两种以上方案得满分。

使用CSS实现三角形是Web开发中常见的一个技巧,通常通过设置元素的边框宽度和颜色,并将元素本身的大小(宽和高)设置为0来实现。以下是两种及以上实现三角形的CSS方案:
方案一:向上三角形

.triangle-up {  
  width: 0;  
  height: 0;  
  border-left: 50px solid transparent; /* 左边框透明 */  
  border-right: 50px solid transparent; /* 右边框透明 */  
  border-bottom: 100px solid black; /* 下边框为实色,形成三角形 */  
}

方案二:向右三角形

.triangle-right {  
  width: 0;  
  height: 0;  
  border-top: 50px solid transparent; /* 上边框透明 */  
  border-left: 100px solid black; /* 左边框为实色,形成三角形 */  
  border-bottom: 50px solid transparent; /* 下边框透明 */  
}

12.短轮询、长轮询和websocket的区别?

短轮询:

客户端定期向服务器发送请求,无论服务器上的数据是否更新,都会立即响应客户端。
如果数据没有更新,服务器会返回空结果或相同的旧数据。
这种方式会消耗较多的网络带宽和服务器资源,因为无论数据是否变化,请求都会定期发出。
长轮询:

与短轮询类似,客户端也定期向服务器发送请求。
但当服务器上没有数据更新时,服务器会保持连接打开,直到有新数据到达或连接超时。
当数据更新时,服务器会立即发送响应给客户端,然后关闭连接。客户端在收到响应后会重新发起请求。
长轮询减少了不必要的请求次数,提高了效率。
WebSocket:

WebSocket 是一种在单个 TCP 连接上进行全双工通讯的协议。
一旦 WebSocket 连接建立,服务器和客户端之间就可以随时互相发送消息,而不需要客户端发起额外的HTTP请求。
WebSocket 提供了更低的延迟和更高的效率,特别适合于需要实时数据交互的应用场景。

13.前端跨域的解决方式?

前端跨域问题主要发生在浏览器端,由于同源策略的限制,浏览器会阻止跨域的HTTP请求。以下是一些常见的跨域解决方式:

JSONP:
利用

14.说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?

@reduxjs/toolkit:

是Redux官方提供的一个官方工具包,旨在简化和改进Redux的开发流程。
提供了一系列简化的API和工具,如createSlice、configureStore等,用于生成Redux的slice(包含reducer和action)和管理Redux的状态。
内部集成了Immer库,使得在reducer中直接修改state变得更加容易和直观。
默认集成了Redux DevTools Extension,提供了可视化的状态跟踪和调试工具。
react-redux:

是Redux官方提供的与React集成的库。
提供了一些React组件(如和)来将Redux集成到React应用中,使得React组件能够访问和操作Redux的状态。
区别:

职责不同:@reduxjs/toolkit关注于简化和改进Redux的开发流程,而react-redux则关注于Redux在React中的集成。
功能不同:@reduxjs/toolkit提供了一系列用于创建和管理Redux状态和操作的工具,而react-redux提供了一些用于在React组件中连接Redux的工具。
目标不同:@reduxjs/toolkit旨在提供更好的开发体验,而react-redux旨在使Redux能够方便地与React应用集成。

15.React render方法的原理,在什么时候会触发?

React render方法的原理:

React的render方法是组件类的一个核心生命周期方法,用于定义组件的UI结构。它的主要原理是将组件的虚拟DOM(Virtual DOM)表示转化为真实的DOM元素,并将其插入到页面中。React通过维护一个虚拟DOM树来跟踪界面上所有元素的状态,并在需要时仅更新发生变化的部分,以提高性能。

React render方法的触发时机:

组件初始化:在组件第一次被创建和渲染到DOM上时,会调用render方法来生成组件的虚拟DOM,并将其转化为真实的DOM元素。
组件状态变化:当组件的状态(通过this.setState或useState Hook)发生变化时,React会自动重新调用render方法,以根据新的状态重新生成组件的UI。
组件属性变化:当组件接收到的属性(props)发生变化时,组件会接收到新的props并重新调用render方法。
强制更新:虽然不常见,但可以通过调用组件实例的forceUpdate()方法(在类组件中)或某些特定条件下(如Hooks的依赖项变化)来强制React重新调用render方法,即使组件的状态和属性没有变化。

16.Redux中同步 action 与异步 action最大的区别?

Redux中同步action与异步action的最大区别:

执行时机:
同步action:立即执行,当被dispatch时,会立即被Redux的中间件捕获并发送给reducer进行处理。同步action通常用于处理一些简单的、立即触发的操作,如用户点击按钮、输入框内容变化等。
异步action:不立即执行,而是返回一个函数,这个函数可以异步地执行一些操作(如发送网络请求、定时器等),并在操作完成后通过dispatch触发同步action来更新应用的状态。异步action通常使用Redux中间件(如redux-thunk、redux-saga等)来处理。
结构:
**同步action:**通常是一个简单的对象,包含type字段来描述动作类型,以及可选的payload字段来携带数据。
**异步action:**通常是一个函数,这个函数返回一个函数,返回的函数接受dispatch和getState作为参数(或类似结构),以便在异步操作完成后触发同步action。
用途:
**同步action:**适用于处理那些不需要等待异步操作完成即可立即得到结果的情况。
**异步action:**适用于需要处理异步操作(如数据请求、文件上传等)的情况,通过异步action可以更好地管理异步流程,并在操作完成后更新应用状态。

17.React的props.children使用map函数来遍历会收到异常显示,为什么?应该如何遍历?

在React中,props.children可以是一个单独的React元素,也可以是一个由多个React元素组成的数组或类似数组的对象(如React.Fragment)。直接使用map函数遍历props.children可能会引发异常,因为map函数只能用于遍历数组,而无法直接应用于单个React元素。

解决方法:

使用React.Children.map:
React提供了React.Children.map方法来遍历props.children,无论props.children是单个React元素还是多个React元素组成的数组,都能正常工作。这个方法类似于数组的map方法,可以对每个子元素进行操作,并返回一个新的数组。
将props.children转换为数组:
如果出于某种原因需要使用数组的map方法,可以先将props.children转换为一个数组。这可以通过Array.from方法或展开运算符[…]来实现。但请注意,如果props.children已经是数组,这种转换可能是多余的。
使用React.Children.toArray:
与转换为数组类似,React还提供了React.Children.toArray函数,它可以将props.children转换为一个数组,并允许你传递一个可选的keyFunc参数来为每个子元素指定一个唯一的key。然后,你可以直接使用数组的map方法来遍历这个数组。

18.React 组件之间如何通信?

React组件之间的通信方式多种多样,主要取决于组件之间的层级关系。以下是一些常见的通信方式:

父组件向子组件通信:
父组件在调用子组件时,可以通过子组件的props传递数据或函数给子组件。子组件通过props接收这些数据或函数,并使用它们来渲染UI或执行操作。
子组件向父组件通信:
子组件可以通过props接收一个由父组件定义的函数(通常称为回调函数),并在需要时调用这个函数,

19.说说你对受控组件和非受控组件的理解?应用场景?

受控组件(Controlled Components)和非受控组件(Uncontrolled Components)是React中处理表单元素的两种主要方式,它们在React的状态管理和数据流控制上有所不同。

受控组件
定义:
受控组件是指其值(如输入框的值)由React的状态(state)来控制并更新的组件。在受控组件中,组件的值与React状态之间保持同步。

特点:

表单元素的值通过props传递的value或checked属性与React组件的状态绑定。
用户输入或选择操作会触发onChange等事件,通过事件处理函数更新React组件的状态,进而重新渲染组件并更新表单元素的值。
受控组件允许父组件完全控制子组件的数据流,并在React状态中保存表单数据,便于进行表单验证、提交等操作。
应用场景:

需要对用户输入进行实时验证或处理的场景。
表单元素之间有复杂的关联关系,需要根据其他输入的值动态更新的场景。
需要手动控制表单元素的值、禁用状态等行为的场景。
非受控组件
定义:
非受控组件是指其值不由React状态控制的组件。在非受控组件中,表单元素的值通常由DOM自身维护,并通过对DOM进行引用来获取表单元素的值。

特点:

表单元素的值不由React状态管理,而是由用户输入直接赋值。
通常通过ref属性获取DOM元素的引用,并在需要时手动获取或设置其值。
非受控组件相对简单,不需要维护额外的状态和事件处理函数。
应用场景:

处理简单的表单场景,不需要对表单数据进行额外处理的情况。
某些只读或只写的输入框,不希望React状态改变的场景。
需要直接访问或操作表单元素值,且不需要通过React状态进行管理的场景。
总结
受控组件和非受控组件各有优缺点,选择哪种方式取决于具体的应用场景和需求。对于复杂的表单验证和数据处理,受控组件提供了更好的控制和可维护性;而对于简单的表单场景,非受控组件则能够简化代码和逻辑。在实际开发中,应根据具体需求和场景灵活选择使用受控组件或非受控组件。

20.说说javascript内存泄漏的几种情况?

JavaScript内存泄漏是一个常见且可能严重的问题,通常发生在程序在不需要某些数据时仍然保留这些数据的引用,导致这些数据无法被垃圾回收机制清除。以下是一些JavaScript中常见的内存泄露情况:
全局变量:
如果在JavaScript中忘记使用var、let或const关键字声明变量,那么这个变量将自动成为全局变量。全局变量在整个应用程序的生命周期内都保持存在,即使它们不再被需要,也不会被垃圾回收。这可能导致内存泄露。
闭包:
闭包是JavaScript中一个强大的特性,允许函数访问其外部词法环境。然而,如果不当使用闭包,可能会导致内存泄露。当闭包引用外部变量,并且这些变量不再需要时,如果闭包仍然被引用,那么这些变量就不会被垃圾回收。
DOM元素引用:
在Web开发中,如果JavaScript代码保留了对不再需要的DOM元素的引用,那么这些元素就不会被垃圾回收。这通常发生在将DOM元素赋值给JavaScript变量,并在稍后忘记解除引用时。
定时器或回调函数:
使用setInterval、setTimeout或事件监听器创建的定时器或回调函数,如果未正确清除,可能会导致内存泄露。特别是当回调函数或定时器引用外部变量时,这些变量可能会因为回调函数或定时器的存在而无法被垃圾回收。
如何避免JavaScript内存泄漏?
始终使用var、let或const来声明变量,避免创建不必要的全局变量。
仔细管理闭包,确保不再需要的闭包能够被垃圾回收。
及时解除对不再需要的DOM元素的引用。
清除不再需要的定时器或回调函数。
注意避免循环引用,特别是在使用复杂数据结构时。
使用工具(如Chrome DevTools的内存分析器)和编写可维护、可测试的代码,以帮助识别和修复内存泄露问题。
通过这些措施,可以有效地减少JavaScript应用程序中的内存泄露问题,提高应用程序的性能和稳定性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值