65道常问前端面试题总结react

面试题总结

一.Axios的实现原理

Axios 是一个基于 Promise 的 HTTP 客户端库,用于浏览器和 Node.js 环境。它可以发送 HTTP 请求并处理响应数据。下面是 Axios 实现的基本原理:

  1. 封装请求:Axios 提供了一个简单易用的 API,使得开发者能够发送各种类型的 HTTP 请求(如 GET、POST 等)。开发者可以通过设置请求的 URL、请求方法、请求头、请求参数等来定制请求。

  2. 发送请求:当开发者调用 Axios 提供的请求方法时,Axios 会创建一个 XMLHttpRequest 对象(在浏览器环境中)或使用 Node.js 的内置 http 模块(在 Node.js 环境中)。然后,Axios 将请求方法、URL、请求头、请求参数等信息传递给底层的 HTTP 客户端

  3. 处理响应:一旦底层的 HTTP 客户端接收到服务器的响应,Axios 会对响应进行处理。它会根据响应的状态码来判断请求是否成功,并将响应数据封装成一个 Promise 对象,以便开发者能够处理响应数据或进行错误处理。

  4. **拦截器:Axios 还提供了拦截器的功能,允许开发者在发送请求或处理响应之前,对请求或响应进行拦截和修改。**开发者可以注册请求拦截器和响应拦截器,从而实现在请求发送前或响应返回前对数据进行处理、添加认证信息等操作。

  5. 错误处理:如果请求过程中发生了错误,比如网络错误或服务器返回错误状态码,Axios 会将错误信息封装成一个错误对象,并将其传递给开发者进行处理。开发者可以通过 Promise 的 reject 方法或使用 try-catch 语句来捕获并处理这些错误。

二.React与Vue区别

Vue.js 和 React 是两个流行的前端框架,它们有一些区别,包括以下几个方面:

  1. 学习曲线Vue.js 相对来说更容易学习和上手,因为它提供了一种模板语法,使得编写模板和组件更加直观和简单。React 则使用了 JSX 语法,需要开发者熟悉 JavaScript 和 JSX 的语法规则。

  2. 组件化开发Vue.js 更加注重组件化开发,它将页面划分为多个可复用的组件,每个组件都包含自己的模板、样式和逻辑。React 也支持组件化开发,但它更加灵活,组件之间的通信需要开发者手动管理

  3. 响应式更新:Vue.js 使用了基于对象劫持的响应式系统,通过追踪数据的变化来自动更新 DOM。React 则使用了虚拟 DOM 和一种称为协调的机制,通过对比虚拟 DOM 的差异来更新真实 DOM

  4. 生态系统React 有更大和更活跃的生态系统,拥有丰富的第三方库和组件,同时也有更多的工具和支持。Vue.js 的生态系统也在不断发展壮大,但相对来说规模稍小。

  5. 社区和支持:由于 React 的广泛使用和活跃的社区,开发者可以更容易地找到解决问题的资源和支持。Vue.js 也有一个积极的社区,但相对来说可能需要花费一些额外的努力来获取支持。

三.vue有双向绑定,React为什么没有,如果要有的话,应该怎么写

React 没有内置的双向绑定的概念,这是因为 React 的设计思想是单向数据流(One-Way Data Flow)。单向数据流意味着数据的流动是单向的,从父组件向子组件传递数据,子组件通过 props 接收数据并进行展示或处理。当子组件需要修改数据时,它会通过回调函数的方式将数据的修改请求发送给父组件,由父组件来更新数据并再次传递给子组件。

这种单向数据流的设计有一些优点,比如数据流动清晰可追踪,易于调试和理解。同时,它也能避免一些潜在的问题,比如数据循环依赖和难以追踪的数据变更。

然而,如果你想在 React 中实现类似于双向绑定的效果,你可以通过手动编写代码来实现。下面是一个简单的示例:

import React, { useState } from 'react';function MyComponent() {const [value, setValue] = useState('');const handleChange = (event) => {setValue(event.target.value);};return (<div><input type="text" value={value} onChange={handleChange} /><p>{value}</p ></div>);
}

在这个示例中,我们使用了 React 的状态钩子 useState 来创建了一个名为 value 的状态变量,并使用 <input> 元素的 value 属性将其与输入框的值进行绑定。同时,我们还定义了一个名为 handleChange 的函数来处理输入框值的变化,并使用 onChange 事件将其与输入框关联起来。当输入框的值发生变化时,handleChange 函数会更新 value 的状态,并在 <p> 元素中展示最新的值。

四.ES6新特性

ES6(ECMAScript 2015)是 JavaScript 的第六个版本,引入了许多新的语言特性和改进,以提升开发者的开发体验和代码质量。以下是一些 ES6 的新特性:

  1. 块级作用域和常量(letconstletconst 关键字引入了块级作用域的概念,使得变量的作用范围更加清晰可控。let 声明的变量具有块级作用域,而 const 声明的变量是常量,不可被重新赋值。

  2. 箭头函数:箭头函数提供了一种更简洁的函数定义语法,并且自动绑定了函数的上下文。它们通常用于编写简短的匿名函数或在回调函数中使用。

  3. 默认函数参数:ES6 允许函数参数设置默认值,当调用函数时,如果没有传递该参数或传递的值为 undefined,则会使用默认值。

  4. 模板字符串:模板字符串是一种更强大和灵活的字符串表示方法,支持多行文本、内嵌变量和表达式,并使用反引号(`)进行包裹。

  5. 解构赋值:解构赋值允许通过模式匹配的方式从数组或对象中提取值,并将其赋给变量。这种方式简化了对数据的访问和赋值操作。

  6. 类和模块:ES6 引入了类的概念,使得面向对象编程更加直观和易用。同时,ES6 也支持了模块的导入和导出,使得代码的组织和复用更加方便。

  7. 迭代器和生成器:迭代器和生成器提供了一种更灵活的方式来遍历和生成数据。迭代器是一种对象,它实现了一个 next() 方法,可以按需产生序列中的下一个值。生成器则是一种函数,使用 function* 声明,能够以简洁的方式定义迭代器。

这些只是 ES6 中的一些重要特性,还有许多其他特性如模块化、箭头函数、Promise、新的数据结构(Map、Set)、扩展运算符等。这些特性的引入丰富了 JavaScript 的语法和功能,使得开发者能够更高效和舒适地编写现代的 JavaScript 代码。

五.箭头函数与普通函数的区别

  1. 语法简洁:箭头函数具有更简洁的语法,通常可以用更少的代码来定义函数。箭头函数使用箭头(=>)来替代普通函数的 function 关键字。

  2. 上下文绑定箭头函数会自动绑定函数体内部的 this 值,它会捕获函数声明时所在的上下文的 this,并在函数执行时保持不变。而普通函数的 this 值是在函数被调用时动态确定的

  3. 不绑定 arguments 对象:箭头函数没有自己的 arguments 对象,它会继承外部作用域的 arguments 对象。而普通函数则会创建自己的 arguments 对象。

  4. 不可作为构造函数箭头函数不能用作构造函数不能通过 new 关键字实例化一个箭头函数。普通函数可以用作构造函数来创建新的对象实例。

  5. 没有原型属性:箭头函数没有自己的 prototype 属性,因此不能使用 new 关键字来创建实例****。普通函数有自己的 prototype 属性,可以作为构造函数来创建对象。

  6. 没有绑定自己的 arguments, super, new.target:箭头函数没有绑定自己的 arguments 对象,也没有 super 关键字和 new.target 关键字。

总的来说,箭头函数适合于简短的函数表达式和回调函数,它们更简洁并且自动绑定上下文的 this 值。而普通函数则更灵活,适用于需要更多功能和更复杂逻辑的函数定义。选择使用哪种函数取决于具体的使用场景和需求。

六.Promise是什么?特点以及作用

Promise 是 JavaScript 中处理异步操作的一种机制,它代表了一个尚未完成但最终会完成或失败的操作。通过 Promise,可以更加优雅地编写和管理异步代码。

Promise 的特点包括

  1. 状态:Promise 有三种状态:pending(进行中)、fulfilled(已完成)和 rejected(已失败)。初始状态是 pending,当操作成功完成时,Promise 变为 fulfilled 状态;当操作失败时,Promise 变为 rejected 状态。一旦进入 fulfilled 或 rejected 状态,Promise 的状态就不可再变更。

  2. 异步操作:Promise 通常用于封装异步操作,如网络请求、文件读取等。在异步操作执行完毕后,Promise 可以被解析(resolve)或被拒绝(reject),并返回相应的结果或错误。

  3. 链式调用:Promise 支持链式调用,通过使用 then() 方法可以在 Promise 完成后执行相应的操作,并返回一个新的 Promise。这样可以方便地进行异步操作的串联和组合。

下面是一个简单的 Promise 示例:

const fetchData = () => {return new Promise((resolve, reject) => {// 异步操作,比如发送网络请求setTimeout(() => {const data = 'Some data'; // 假设这是从网络获取到的数据resolve(data); // 成功时将结果传递给 resolve// reject(new Error('Error message')); // 失败时将错误传递给 reject}, 2000);});
};fetchData().then((data) => {console.log('Data:', data);}).catch((error) => {console.error('Error:', error);});

在上面的示例中,fetchData 函数返回一个 Promise 对象,它模拟了一个异步操作(这里使用了 setTimeout 来模拟延迟)。通过调用 then() 方法,我们可以在 Promise 完成后处理返回的数据。如果 Promise 被解析,则会执行第一个回调函数并传递数据;如果 Promise 被拒绝,则会执行 catch() 方法指定的错误处理函数。

Promise 的优势在于提供了一种更清晰和结构化的方式来处理异步代码,避免了回调地狱的问题,并使代码更易读和维护。

七.HTTP1与HTTP2区别

HTTP/1.1(以下简称 HTTP/1)和 HTTP/2 是两个不同的 HTTP 协议版本,它们之间有一些重要的区别,主要包括以下几个方面:

  1. 多路复用HTTP/1 使用串行方式发送请求,即每个请求需要等待前一个请求的响应完成后才能发送。而 HTTP/2 支持多路复用,可以在一个 TCP 连接上同时发送多个请求和接收响应,提高了请求的并发性和性能。

  2. 头部压缩HTTP/1 在每个请求和响应中都携带完整的头部信息,导致了较大的数据传输量。而 HTTP/2 使用了头部压缩技术,通过在客户端和服务器之间维护一个头部表,减少了头部信息的重复传输,从而减小了数据传输的大小

  3. 二进制分帧HTTP/2 将数据分割为更小的二进制帧进行传输,每个帧都带有一个帧头,包含了必要的控制信息。这种分帧的方式使得服务器和客户端可以并行发送、接收和处理数据,提高了传输效率和响应速度。

  4. 服务器推送HTTP/2 支持服务器主动推送资源,即在客户端请求一个资源时,服务器可以主动推送其他相关资源给客户端,避免了客户端再次发送请求的延迟。

  5. 加密:虽然 HTTP/1 可以通过 HTTPS 进行加密通信,但这是可选的。而 HTTP/2 要求使用加密的传输层(TLS),即 HTTPS,以提供更安全的通信。

总体而言,HTTP/2 在性能和效率方面有显著的改进,特别是在多路复用、头部压缩和二进制分帧等方面。它可以更快地传输数据、减少延迟并提高网络性能。然而,需要注意的是,HTTP/2 的实际性能受到网络环境、服务器和客户端的支持程度等因素的影响。

八.不定长宽盒子垂直居中怎么实现

要实现不定长宽的盒子垂直居中,可以使用一些 CSS 技巧和布局属性。下面是几种常见的方法:

  1. 使用 Flexbox 布局

    .container {display: flex;align-items: center;justify-content: center;
    }
    

    在包含盒子的容器上应用 Flexbox 布局,通过设置 align-items: centerjustify-content: center,即可使盒子在垂直和水平方向上都居中。

  2. 使用绝对定位和 transform 属性

    .container {position: relative;
    }.box {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);
    }
    

    将容器设置为相对定位,然后在盒子上应用绝对定位。通过设置 top: 50%left: 50% 将盒子的左上角定位到容器的中心位置,然后使用 transform: translate(-50%, -50%) 进行微调,使盒子完全居中。

  3. 使用表格布局

    .container {display: table;width: 100%;height: 100%;
    }.cell {display: table-cell;vertical-align: middle;text-align: center;
    }
    

    将容器设置为表格布局,通过设置 display: tabledisplay: table-cell,并使用 vertical-align: middle 将盒子垂直居中。

这些方法都可以使不定长宽的盒子在垂直方向上居中。选择哪种方法取决于你的具体需求和布局结构。请根据自己的情况选择最适合的方法来实现垂直居中效果。

九.Redux Toolkit怎么使用与Redux区别

Redux Toolkit 是一个用于简化 Redux 开发的官方工具集,它提供了一些简化和标准化的 API,以及一些常见的开发模式和工具。下面是使用 Redux Toolkit 的基本步骤:

  1. 安装 Redux Toolkit:

    npm install @reduxjs/toolkit
    
  2. 创建 Redux Store:

    import { configureStore } from '@reduxjs/toolkit';
    import rootReducer from './reducers'; // 导入根 reducerconst store = configureStore({reducer: rootReducer,
    });export default store;
    

    使用 configureStore 函数创建 Redux store,并传入根 reducer。

  3. 创建 Reducer:

    import { createSlice } from '@reduxjs/toolkit';const counterSlice = createSlice({name: 'counter',initialState: 0,reducers: {increment: (state) => state + 1,decrement: (state) => state - 1,},
    });export const { increment, decrement } = counterSlice.actions;
    export default counterSlice.reducer;
    

    使用 createSlice 函数创建 reducer,并定义初始状态和相关的 action。createSlice 会自动生成相应的 action creators 和 reducer。

  4. 使用 Redux Store:

    import { useSelector, useDispatch } from 'react-redux';
    import { increment, decrement } from './counterSlice';function Counter() {const counter = useSelector((state) => state.counter);const dispatch = useDispatch();return (<div><span>{counter}</span><button onClick={() => dispatch(increment())}>+</button><button onClick={() => dispatch(decrement())}>-</button></div>);
    }
    

    使用 useSelector 来选择需要的 state,使用 useDispatch 来获取 dispatch 函数,从而可以触发相应的 action。

Redux Toolkit 相对于传统的 Redux,主要有以下区别和优势:

  1. 简化的 API:Redux Toolkit 提供了简化和标准化的 API,如 createSlice 自动创建 action creators 和 reducer,减少了样板代码的编写。

  2. 集成的常用工具:Redux Toolkit 集成了常用的 Redux 插件和工具,如 Redux DevTools Extension,使得开发和调试变得更加便捷。

  3. 内建的不可变更新Redux Toolkit 使用了 Immer 库来处理不可变更新,使得在 reducer 中可以直接修改 state,无需手动编写不可变的更新逻辑

  4. 默认配置的优化:Redux Toolkit 提供了合理的默认配置,如默认使用 configureStore 创建 store,并集成了常用的 Redux 中间件和优化选项。

总的来说,Redux Toolkit 简化了 Redux 的开发流程,提供了更简洁和直观的 API,并集成了一些常用的工具和优化。它是官方推荐的使用 Redux 的方式,适用于大多数 Redux 应用。

十.React其他状态管理工具

除了 Redux,React 还有其他一些常用的状态管理库和模式,可以根据具体项目的需求选择适合的状态管理方式。以下是一些常见的 React 状态管理解决方案:

  1. React Context:React Context 是 React 官方提供的一种状态管理机制,它允许在组件树中共享状态,而无需通过 props 逐层传递。使用 React Context,可以创建一个全局的状态容器,并在需要的组件中订阅和更新状态。

  2. MobX:MobX 是一个简单、可扩展的状态管理库,它使用观察者模式和响应式数据流来实现状态管理。MobX 提供了 observablecomputedaction 等装饰器来定义和观察状态,使得状态的变化能够自动地驱动相关组件的更新。

  3. Zustand:Zustand 是一个轻量级的状态管理库,它采用 Hook 和函数式编程的方式来管理状态。Zustand 提供了一个状态容器,使用类似于 Redux 的 reduceractions 的概念来定义和更新状态,并通过 useStore Hook 来访问状态和订阅状态的变化。

  4. Recoil:Recoil 是由 Facebook 开发的状态管理库,它专注于管理组件间的共享状态。Recoil 提供了 atomselectoruseRecoilState 等 API 来定义和使用状态,可以通过声明式地描述组件所需的状态依赖关系,并自动处理状态的变化和组件的更新。

  5. Apollo Client:如果你的应用需要与后端的 GraphQL 服务器进行交互,Apollo Client 是一个强大的状态管理解决方案。它提供了现代化的 GraphQL 客户端功能,包括数据获取、缓存管理和状态同步等,使得在 React 应用中处理 GraphQL 数据变得更加简单和高效。

这些状态管理库和模式在不同场景下具有各自的优势和适用性。选择适合项目需求和团队经验的状态管理方式,可以提高开发效率和代码的可维护性。

十一. Redux怎么实现的

Redux 是一个用于管理应用状态的 JavaScript 库,它遵循了单一数据源、状态不可变和纯函数的原则。下面是 Redux 的基本实现原理:

  1. **Store:Redux 的核心概念是 Store,它是应用状态的唯一数据源。**Store 包含了应用的状态树,并提供了一些方法来访问和修改状态。

  2. **Action:Action 是一个普通的 JavaScript 对象,它描述了发生的事件或用户操作。**Action 必须包含一个 type 属性来指示要执行的操作类型,以及可选的 payload 属性用于传递额外的数据。

  3. **Reducer:Reducer 是一个纯函数,它接收当前的状态和一个 Action,并根据 Action 的类型来返回一个新的状态。**Reducer 必须是一个纯函数,即对于相同的输入,始终返回相同的输出,而且不应该有副作用。

  4. **Dispatch:Dispatch 是一个用于触发 Action 的函数,它是通过调用 Store 的 dispatch 方法来执行的。**当调用 dispatch 方法时,Redux 会将 Action 传递给所有注册的 Reducer,每个 Reducer 可以根据 Action 的类型来处理相应的逻辑。

  5. **Subscribe:通过调用 Store 的 subscribe 方法,可以注册一个回调函数,用于监听状态的变化。**当状态发生改变时,会触发这个回调函数。

Redux 的基本工作流程如下:

  1. 初始化 Store,将根 Reducer 传入 createStore 方法,创建一个 Redux Store。
  2. 定义 Reducer 函数,根据不同的 Action 类型处理状态的更新逻辑。
  3. 在组件中通过 useDispatch 获取 dispatch 函数,用于触发 Action。
  4. 在组件中通过 useSelector 获取需要的状态值,以便渲染 UI。
  5. 当用户触发某个操作时,调用 dispatch 方法发送相应的 Action。
  6. Reducer 接收 Action,并根据 Action 类型更新状态。
  7. 当状态发生变化时,组件会重新渲染,获取最新的状态值并更新 UI。

Redux 的优势在于它提供了一种可预测的状态管理模式,使得状态变化更加可控和可追踪。它适用于中大型应用的状态管理,并且可以与各种 UI 框架和库结合使用。

十二.协商缓存强制缓存是什么?区别?

强制缓存和协商缓存是浏览器在处理缓存时使用的两种不同的机制。

**强制缓存是指浏览器直接从本地缓存中获取资源,而不向服务器发送请求。**当浏览器第一次请求资源时,服务器会返回资源的响应,并在响应头中设置缓存控制字段,如 Cache-ControlExpires。浏览器在后续请求该资源时,会首先检查本地缓存,并根据缓存控制字段的设置判断是否可使用缓存。如果可使用缓存,则直接从本地缓存中获取资源,而不发送请求到服务器。

协商缓存是指浏览器在请求资源时,先向服务器发送一个请求,服务器根据请求头中的条件判断,返回一个状态码表示资源是否改变。常用的条件判断字段是 If-Modified-SinceIf-None-Match。如果服务器返回的状态码表示资源未改变(如 304 Not Modified),则浏览器可以直接从本地缓存中获取资源。如果服务器返回的状态码表示资源已改变(如 200 OK),则浏览器会重新下载资源并更新缓存。

区别总结如下:

  • 强制缓存直接从本地缓存获取资源,不发送请求到服务器;协商缓存需要发送请求到服务器,根据服务器的响应决定是否使用缓存
  • 强制缓存使用的是缓存控制字段,如 Cache-ControlExpires协商缓存使用的是条件判断字段,如 If-Modified-SinceIf-None-Match
  • 强制缓存的优先级高于协商缓存,如果强制缓存生效,浏览器直接使用缓存而不进行协商;如果