TCP协议
- TCP 和 UDP 的区别?
- TCP 三次握手的过程?
- 为什么是三次而不是两次、四次?
- 三次握手过程中可以携带数据么?
- 说说 TCP 四次挥手的过程
- 为什么是四次挥手而不是三次?
- 半连接队列和 SYN Flood 攻击的关系
- 如何应对 SYN Flood 攻击?
- 介绍一下 TCP 报文头部的字段
- TCP 快速打开的原理(TFO)
- 说说TCP报文中时间戳的作用?
- TCP 的超时重传时间是如何计算的?
- TCP 的流量控制
- TCP 的拥塞控制
- 说说 Nagle 算法和延迟确认?
- 如何理解 TCP 的 keep-alive?
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
浏览器篇
- 浏览器缓存?
- 说一说浏览器的本地存储?各自优劣如何?
- 说一说从输入URL到页面呈现发生了什么?
- 谈谈你对重绘和回流的理解
- XSS攻击
- CSRF攻击
- HTTPS为什么让数据传输更安全?
- 实现事件的防抖和节流?
- 实现图片懒加载?
3.state 参数传递:使用 history.push
或 Link
组件进行路由跳转时,还可以通过 state
属性传递参数,location.state
14. Js中浅拷贝和深拷贝有什么区别,如何实现?(3次)
在 JavaScript 中,浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是两种常见的数据拷贝方式,它们的区别在于拷贝的程度。
- 浅拷贝:浅拷贝仅拷贝对象或数组的引用,而不是拷贝其内部的值。这意味着原始对象和拷贝后的对象会共享同一个内存地址,当其中一个对象修改了属性或元素时,另一个对象也会受到影响。
- 深拷贝:深拷贝会创建一个完全独立的对象或数组,并递归地拷贝其所有的属性或元素。这意味着原始对象和拷贝后的对象不共享内存地址,修改其中一个对象不会对另一个对象产生影响。
下面是一些常见的实现方法:
浅拷贝的实现方法:
- 使用
Object.assign()
方法进行浅拷贝: - 使用展开运算符进行浅拷贝:
深拷贝的实现方法:
- 使用
JSON.parse(JSON.stringify())
进行深拷贝,但注意该方法有一些限制和注意事项(比如无法拷贝函数、循环引用会导致错误等): - 使用递归实现自定义的深拷贝函数:
15. 为什么不能在循环、条件或嵌套函数中调用 Hooks?(2次)
在 React 中,Hooks 是一种用于在函数组件中添加状态和其他 React 特性的方式。Hooks 通过使用特定的钩子函数(如 useState
、useEffect
等)来管理组件的状态和副作用。
根据 React 的规范,Hooks 只能在函数的顶层调用,也就是说不能在循环、条件或嵌套函数中调用 Hooks。这是因为 React 依赖于 Hooks 的调用顺序来正确地追踪组件的状态。如果在循环、条件或嵌套函数中调用 Hooks,会导致 React 无法正常追踪和管理组件的状态,可能引发一些错误或不一致的行为。
16. React有哪些性能优化的方法?(3次)
1.使用虚拟列表和虚拟表格:对于大量数据的情况下,使用虚拟化技术来优化列表和表格的性能。
2.批量更新状态:使用setState的函数式形式(setState(updater)),将多个状态更新合并为一个,从而减少React重复渲染的次数。
3.懒加载组件:使用React.lazy和Suspense进行组件懒加载,在需要时再动态加载组件,提高初始加载性能。
4.使用css过渡和动画:使用CSS过渡和动画来优化组件的过渡效果和用户交互体验,从而提高用户满意度。
5.避免在render方法中调用函数和组件方法:例如在render方法中调用this.props、this.state、this.forceUpdate、this.refs等方法, 会引起组件的重渲染。
17. Webpack有哪些核心概念?
Webpack 是一个现代的 JavaScript 应用程序静态模块打包工具,它的核心概念有以下几个:
- 入口(Entry):指示 Webpack 从哪个文件开始构建依赖关系图。入口可以是一个或多个文件,形成一个依赖图的根节点。
- 输出(Output):指示 Webpack 将打包后的文件输出到哪里,以及如何命名这些文件。输出通常是一个或多个打包后的文件,可以是 JavaScript、CSS 等类型。
- 加载器(Loaders):Webpack 可以通过加载器处理非 JavaScript 文件。加载器允许你在导入文件时进行预处理转换,例如将 Sass 编译为 CSS、将 ES6/TypeScript 转译为 ES5 等。
- 插件(Plugins):插件用于扩展 Webpack 的功能。它们可以执行范围更广的任务,从而实现优化、资源管理、注入环境变量等功能。例如,添加 HtmlWebpackPlugin 插件可以为生成的 HTML 文件添加自动引用打包后的脚本。
- 模式(Mode):模式指定了 Webpack 的构建环境,可以是开发模式(development)、生产模式(production)或其他自定义环境。不同的模式会触发不同的内置优化策略,例如压缩代码、启用浏览器调试工具等。
- 代码分割(Code Splitting):Webpack 支持将代码分割成多个块,以实现按需加载和优化加载性能。通过代码分割,可以将常用的第三方库或业务代码拆分成不同的文件,按需加载,减少初始加载时间。
- 缓存(Caching):Webpack 通过使用哈希值作为文件名的一部分,可以根据文件内容的更改来更新缓存。这样,在文件内容不变的情况下,可以利用浏览器缓存,减少重新下载的开销。
- DevServer:Webpack DevServer 是一个轻量级的开发服务器,提供了热更新、代理转发、自动刷新等开发时常用的功能。
这些都是 Webpack 的核心概念,了解并熟练使用它们可以帮助你更好地使用 Webpack 进行模块打包和构建前端应用程序。
18. 常用的hooks都有哪些,说出他们的作用,最少列出来5个?(2次)
以下是常用的 React Hooks,并列出了它们的作用:
- useState:用于在函数组件中添加状态管理。它返回一个包含当前状态和更新状态的数组,可以通过解构赋值来使用。
- useEffect:用于在函数组件中执行副作用操作,比如订阅外部数据、操作 DOM 等。它接收一个回调函数和依赖数组,并在每次渲染后执行回调函数。
- useContext:用于从 React 的 Context 中获取当前的上下文值。它接收一个 Context 对象,并返回该 Context 的当前值。
- useReducer:类似于 Redux 中的 reducer,用于管理复杂的状态逻辑。它返回当前状态和一个 dispatch 函数,用于派发状态更新的动作。
- useCallback:用于性能优化,避免不必要的函数重新创建。它接收一个回调函数和依赖数组,并返回一个记忆化的版本,只在依赖数组变化时才会重新创建。
- useMemo:用于性能优化,缓存计算结果。它接收一个回调函数和依赖数组,并返回计算结果。只有当依赖数组发生变化时,才会重新计算结果。
- useRef:用于在函数组件中创建可变的 ref 对象。它返回一个包含
current
属性的对象,该属性可以保存任意可变值,并在组件重新渲染时保持不变。 - useLayoutEffect:类似于
useEffect
,但它在 DOM 更新之后同步执行。在大多数情况下,可以使用useEffect
替代,但在需要获取真实 DOM 属性时,可能需要使用useLayoutEffect
。 - useImperativeHandle:用于自定义暴露给父组件的实例值。它接收一个 ref 对象和一个工厂函数,在父组件中可以通过 ref 访问工厂函数返回的值。
- useDebugValue:用于在 React 开发者工具中显示自定义的钩子值,方便调试和跟踪。它接收一个值和一个格式化函数,用于显示在工具中的自定义标签。
这些是常用的 React Hooks,每个 Hook 都有特定的作用,能够方便地处理组件的状态管理、副作用操作、上下文等功能。使用 Hooks 可以使函数组件更易于编写、理解和维护。
19。Typescript中泛型是什么?(2次)
泛型是 TypeScript 中一个重要的特性,它允许在函数、类、接口等类型声明中使用类型参数,从而实现更加抽象和灵活的类型定义。
通过使用泛型,可以编写具有通用性的代码,不受特定类型的限制。例如,一个数组排序函数不需要关心数组元素的具体类型,只需要确保数组元素是可比较的,就可以实现一个通用的排序方法。
在 TypeScript 中泛型可以针对以下几个部分进行声明:
-
函数泛型:通过在函数参数列表前声明类型参数,可以使函数参数和返回值的类型更加灵活。
-
类泛型:在声明类时添加泛型类型参数,可以使类的属性和方法可以使用泛型类型参数。
-
接口泛型:可以使用泛型定义接口的属性类型和方法参数类型。
20umijs.lugin-access插件如果使用?
umijs/plugin-access 是 Umi.js 的一个插件,它提供了一种方便的方式来管理和控制路由权限。通过配置路由权限规则,可以根据用户的身份、权限等信息来限制用户对某些路由的访问。
要使用 umijs/plugin-access 插件,可以按照以下步骤进行操作:
\1. 安装插件:
shellCopy Code
npm install @umijs/plugin-access --save-dev
\2. 在 .umirc.ts 或者 config/config.ts 文件中配置插件:
javascriptCopy Code
export default {
plugins: ['@umijs/plugin-access'],
access: {
**// 配置权限规则**
**// 可以是一个函数,也可以是一个包含多个函数的数组**
**// 函数的返回值为 boolean 表示是否有权限访问该路由**
canAccessRoute: (route) => {
**// 在这里根据用户的身份、权限等信息判断是否有权限访问该路由**
**// 返回 true 表示有权限,返回 false 表示没有权限**
},
},
};
\3. 在路由配置文件 config/routes.ts 中,可以使用 access 属性来指定每个路由的权限要求:
javascriptCopy Code
const routes = [
{
path: '/public',
component: 'PublicPage',
},
{
path: '/private',
component: 'PrivatePage',
access: 'user', **// 这个路由需要用户登录才能访问**
},
**// ...**
];
\4. 在页面组件中,可以通过 access 对象访问当前路由的权限信息:
javascriptCopy Code
import { useAccess } from 'umi';
export default function PrivatePage() {
const access = useAccess();
return (
<div>
{access.canAccessRoute('/private') ? (
• <h1>Welcome to private page!</h1>
) : (
• <h1>Sorry, you don't have access to this page.</h1>
)}
</div>
);
}
21.数组【1,2,3,4,5,3,2,2,4,2,2,3,1,3,5】中出现次数最多的数,并统计出现多 少次,编写一个函数?
function findMostFrequentNumber(arr) {
const counter = arr.reduce((acc, num) => {
if (num in acc) {
acc[num]++;
} else {
acc[num] = 1;
}
return acc;
}, {});
let maxNum = null;
let maxCount = 0;
for (const num in counter) {
if (counter[num] > maxCount) {
maxNum = num;
maxCount = counter[num];
}
}
return { number: maxNum, count: maxCount };
}
**// 调用示例**
const arr = [1,2,3,4,5,3,2,2,4,2,2,3,1,3,5];
const result = findMostFrequentNumber(arr);
console.log(`出现次数最多的数字是 ${result.number},出现了 ${result.count} 次`);
22大文件如何做断点续传?(2)
\1. 将大文件分割为小块:将大文件分割成多个固定大小的小块,例如每个小块的大小可以设置为 1MB。这样可以方便进行分片上传和管理。
\2. 记录上传进度:在客户端和服务端都需要记录上传进度。客户端可以通过记录已成功上传的分片信息来跟踪上传进度,服务端可以通过记录已接收的分片信息来跟踪接收进度。
\3. 分片上传:将分割后的小块逐个上传到服务器,并记录已成功上传的分片信息。可以使用 HTTP 或其他协议来进行分片上传。
\4. 上传失败处理:如果某个分片上传失败,需要记录下该分片的状态,以便后续重新上传。可以保存上传失败的分片信息,并将其标记为未完成。
\5. 断点续传:在下次上传时,检查之前的上传进度和已完成的分片信息。根据记录的信息,跳过已成功上传的分片,只上传未完成的分片。确保已上传的分片不会重复上传。
\6. 合并分片:当所有分片都上传完成后,在服务端将这些分片按顺序合并为完整的文件。
23.元素js如何实现上拉加载下拉刷新?(2)
24说说设备像素、css像素、设备独立像素、dpr、ppi之间的区别?(2)
\1. 设备像素 设备像素指物理屏幕上的像素点数量,通常用“px”表示。屏幕上的每个px将有一个相对应的硬件像素来对应显示。设备像素决定了屏幕的绝对分辨率。
\2. CSS 像素 CSS 像素在设计中用来表示页面中图形和文本等元素尺寸的虚拟单位,通常用“px”表示。CSS 像素并不是“真实”的像素,它是相对于屏幕分辨率独立的。
\3. 设备独立像素 设备独立像素(DP,Density-Independent Pixel),也称为逻辑像素,是一种基于屏幕密度的抽象概念,通常用“dp”或“dip”表示,Android系统中也将其称为“sp”。设备独立像素是与屏幕像素密度有关的像素单位,屏幕像素密度越高,同样大小的物体所占设备独立像素就越高。
\4. DPR(设备像素比) DPR指的是设备像素和CSS像素的比例关系,即“设备像素 / CSS像素” 。它用来描述物理像素与逻辑像素之间的关系。常见的 DPR 值包括 1、2、3 等。
\5. PPI(每英寸像素数) PPI指的是屏幕上每英寸显示的像素点数量,通常用来表示屏幕密度以及屏幕显示清晰程度。PPI 值可以通过计算设备像素和屏幕尺寸之间的比例得到。
25说说TCP为什么需要三次握手和四次握手?(2)
\1. 三次握手 初始化双方 seq。 确认双方信道可以实现最低限度的全双工三次握手是指在建立 TCP 连接时,客户端与服务器之间需要进行三次通信以确认连接的建立。其流程如下:
· 第一次握手:客户端向服务器发送 SYN 报文,并随机生成一个初始序号(ISN)。
· 第二次握手:服务器收到SYN报文后,回复一个SYN+ACK报文作为确认,其也会随机生成一个ISN值。
· 第三次握手:客户端再次向服务器回复一个ACK报文,确认连接已建立。
\2. 四次握手 TCP 要支持半关闭连接四次握手是指在释放 TCP 连接时,客户端与服务器之间需要通过四次通信来确认连接已经关闭。其流程如下:
· 第一次握手:客户端向服务器发送FIN报文,请求关闭连接。
· 第二次握手:服务器收到FIN报文后,回复一个ACK报文作为确认,但此时仍可向客户端发送数据。
· 第三次握手:服务器完成数据的发送后,则向客户端发送一个FIN报文,表示服务器的数据发送完毕,请求关闭连接。
第四次握手:客户端收到服务器的FIN报文后,向服务器回复一个ACK报文作为确认,进入 TIME_WAIT 状态,等待 2MSL 后断开连接。
26.react之间如何通信(3)
1.props:通过向子组件传递 props,可以实现父子组件之间的通信。这是 React 中最基本的通信方式。父组件可以将需要传递给子组 件的数据作为 props 传递给子组件,子组件可以通过 this.props
访问这些数据。
2.Context:Context 是 React 中的一种全局数据管理方式。它可以让子组件在不通过 props 传递的情况下,直接访问父组件或者祖先 组件中的数据。使用 Context 需要先创建一个 Context 对象,然后在祖先组件中通过 Provider 提供数据,在子孙组件中通 过 Consumer 访问数据。
3.Refs:Refs 允许我们访问在组件中创建的 DOM 或者其他组件实例。通过 Refs,组件可以在不通过 props 或者 context 的情况下, 直接修改子组件或者 DOM 元素的属性。
4.Event Bus:Event Bus 是一种跨组件通信方式,它可以让任何两个组件之间都可以进行通信。
27.什么是闭包,应用场景是什么(2)
闭包是指在函数内部创建的函数,并且该函数能够访问到其外部函数的作用域。
闭包有以下特点:
- 内部函数可以访问外部函数中的变量和参数。
- 外部函数的执行上下文被保留在内存中,即使外部函数执行完成后,内部函数仍然可以访问外部函数的作用域。
- 多个内部函数可以共享同一个父级作用域,形成一个闭包函数族。
闭包的应用场景包括但不限于:
- 保护变量:通过使用闭包,可以隐藏变量,只提供对外部函数公开的接口。这样,可以确保变量不会被外部直接修改,增加了数据的安全性。
- 计数器和累加器:通过闭包,可以在函数外部保存一个内部状态,并在每次调用内部函数时修改该状态。这一特性可用于实现计数器、累加器等功能。
- 延迟执行和回调函数:将函数作为返回值,可以实现延迟执行或者在特定条件满足时回调执行。
28.JS数组之间进行合并,写出三种合并方式?
concat() 方法:
concat()
方法可以合并两个或多个数组,并返回一个新数组。它会将所有输入数组中的元素添加到一个新的数组中。
spread 运算符(扩展运算符):
ES6 中引入的 spread 运算符可以很方便地合并数组。它可以将一个数组转换为独立的参数列表,从而可以很容易地合并多个数组。
push() 方法:
push()
方法可以将一个或多个元素添加到数组的末尾,并返回修改后的数组长度。因此,可以通过循环或迭代来将一个数组中的元素添加到另一个数组中。
29.为什么 useState 返回的是数组而不是对象?
在 React 中,useState
是一个用于在函数组件中声明状态的钩子函数。它的返回值是一个长度固定为 2 的数组,而不是一个对象,这是由设计选择所决定的。
使用数组来表示状态,是因为它具有以下优势:
- 简单直观:将状态以数组的形式进行管理,可以更容易地理解和使用。
- 顺序保持一致:使用
useState
声明多个状态时,它们的顺序和声明的顺序是完全一致的。你可以根据索引访问和更新每个状态,而不需要命名或记住状态的特定名称。 - 无需每次指定键:当更新状态时,不需要像使用对象那样指定键。只要调用
useState
返回的第二个元素(通常命名为setXXX
)即可。
30.React 的生命周期?每个生命周期都做了什么?
在React中,组件的生命周期可以分为三个主要的阶段:挂载(Mounting)、更新(Updating)和卸载(Unmounting)。以下是每个阶段对应的方法:
- 挂载阶段(Mounting Phase):
- constructor:组件实例被创建时调用,用于初始化状态和绑定事件处理函数。
- static getDerivedStateFromProps:在组件实例化和更新时都会调用,它接收新的属性和当前状态,并返回一个更新后的状态。
- render:根据组件的状态和属性,返回JSX元素进行渲染,负责生成组件的UI结构。
- componentDidMount:组件已经被渲染到DOM中后调用,此时可以进行网络请求、订阅事件等副作用操作。
- 更新阶段(Updating Phase):
- static getDerivedStateFromProps:在组件更新时调用,接收新的属性和当前状态,并返回一个更新后的状态。
- shouldComponentUpdate:在组件即将更新之前调用,返回一个布尔值,用于判断是否需要进行组件的重新渲染。
- render:重新渲染组件的UI结构。
- componentDidUpdate:组件已经重新渲染并且更新到DOM上后调用,此时可以进行状态更新、网络请求等副作用操作。
- 卸载阶段(Unmounting Phase):
- componentWillUnmount:组件将要从DOM中移除时调用,可以进行清理工作,如取消订阅、清除定时器等。
32.Js中深拷贝和浅拷贝的定义以及具体实现都有哪些方法?
浅拷贝是指创建一个新对象或数组,该对象或数组的值是原始对象或数组中的引用。换句话说,浅拷贝只复制对象或数组的第一层,而不会递归复制嵌套的对象或数组。
常见的实现浅拷贝的方法有:
- 扩展运算符(Spread Operator)
javascriptCopy Codeconst originalArray = [1, 2, 3];
const newArray = [...originalArray];
javascriptCopy Codeconst originalObject = { name: "John", age: 25 };
const newObject = { ...originalObject };
Object.assign()
javascriptCopy Codeconst originalObject = { name: "John", age: 25 };
const newObject = Object.assign({}, originalObject);
注意:Object.assign()
在浅拷贝对象时是深拷贝属性值。
3. Array.slice()
javascriptCopy Codeconst originalArray = [1, 2, 3];
const newArray = originalArray.slice();
深拷贝是指创建一个全新的对象或数组,并递归地复制原始对象或数组的所有嵌套对象或数组。
常见的实现深拷贝的方法有:
- 使用 JSON 序列化和反序列化
javascriptCopy Codeconst originalObject = { name: "John", age: 25 };
const newObject = JSON.parse(JSON.stringify(originalObject));
这种方法可以处理大多数情况,但是对于包含函数、正则表达式等特殊对象的深拷贝可能会有问题。
2. 递归实现深拷贝
javascriptCopy Codefunction deepCopy(obj) {
if (typeof obj === "object" && obj !== null) {
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
return copy;
}
return obj;
}
const originalObject = { name: "John", age: 25 };
const newObject = deepCopy(originalObject);
33.Umi中如何实现路由权限,实现按钮权限?
在 Umi 中实现路由权限和按钮权限可以通过以下步骤:
\1. 路由权限:
· 在路由配置文件中,为每个需要进行权限控制的路由添加 authority 属性,值为权限标识,例如角色名或权限码。
在全局的路由守卫(比如 layouts 中的页面组件)中,判断当前用户是否有权限访问该路由。可以使用 Access 组件配合 authority 属性来进行权限控制。
\2. 按钮权限:
· 在页面组件里,在需要进行权限控制的按钮上添加 authority 属性,值为权限标识,例如角色名或权限码。
自定义一个 Button 组件,根据当前权限判断是否显示按钮。
然后,在页面组件中使用 AccessibleButton 来替代 antd 的 Button 组件,传递相应的 authority 属性。
32.props和state相同点和不同点?render方法在哪些情况下会执行?
Props(属性)和 State(状态)是 React 组件中用于管理数据的两个概念。
相同点:
- 它们都是用于存储和管理组件数据的。
- 当它们的值发生变化时,都可以触发组件重新渲染。
不同点:
- Source(数据来源):Props 是从父组件传递给子组件的,而 State 则是在组件内部定义和管理的。
- Mutability(可变性):Props 是只读的,无法在子组件内部直接修改,只能通过父组件重新传递新的 Props。而 State 可以在组件内部进行修改和更新。
- 更新方式:当 Props 发生变化时,React 会自动进行组件的重新渲染。而 State 的更新需要调用组件的
setState
方法来触发重新渲染。
关于 render
方法的执行情况:
- 初始渲染:组件首次被渲染时,
render
方法会被调用,并生成对应的虚拟 DOM 树。 - 数据变化:当组件的 Props 或 State 发生变化时,React 会重新调用
render
方法来更新虚拟 DOM,并与之前的虚拟 DOM 进行对比。 - 强制更新:通过调用组件的
forceUpdate
方法可以强制触发render
方法的执行,即使 Props 和 State 没有发生变化。 - 父组件更新:如果父组件进行重新渲染,子组件的
render
方法也会被调用。
需要注意的是,只有在 render
方法中返回的虚拟 DOM 与之前的虚拟 DOM 不同,React 才会重新渲染真实 DOM,并更新到页面上。
33.说说你对Object.defineProperty()的理解?
Object.defineProperty() 是 JavaScript 中用于定义或修改对象属性的方法。通过这个方法,可以精确地控制属性的特性,例如可枚举性、可写性、可配置性以及属性的值。
这个方法接受三个参数:要定义属性的对象、属性名和属性描述符对象。属性描述符对象可以包含以下属性:
- value:属性的值。
- writable:属性是否可写。
- enumerable:属性是否可枚举。
- configurable:属性是否可配置。
使用 Object.defineProperty() 方法可以实现对属性的定制化控制,例如可以定义一个不可枚举、不可写、但可配置的属性,从而实现对属性的严格保护或隐藏。
34.说说React中的虚拟dom?在虚拟dom计算的时候diff和key之间有什么关系?
虚拟 DOM(Virtual DOM)是 React 中的一种机制,通过在内存中构建一棵轻量级的虚拟 DOM 树来代替操作浏览器 DOM,从而提高组件的渲染性能和用户体验。
在 React 中,当组件的 Props 或 State 发生变化时,React 会根据最新的数据重新生成一棵虚拟 DOM 树,并与之前的虚拟 DOM 树进行对比。在对比的过程中,React 会找到两棵树中不同的节点,并将它们对应的真实 DOM 节点进行修改、删除或添加,最终将更新后的 DOM 渲染到页面上。
虚拟 DOM 的 diff 算法是 React 优化渲染性能的核心。在 diff 算法中,每个节点都有一个唯一的标识符,称为 key。当新旧虚拟 DOM 树进行对比时,React 会通过 key 来判断两个节点是否表示相同的内容。在判断过程中,React 会先以新节点为基准,在旧节点中查找对应的节点。如果找到了相同的节点,则进行更新;否则,将新节点插入到旧节点中或从旧节点中删除。
在使用 React 进行开发时,我们应该尽量避免使用索引作为 key,因为索引本身并没有表示唯一性,容易造成错误的判断结果和性能问题。相反,我们应该在数据中为每个元素提供一个唯一的标识符,例如数据库中的 ID 或者全局唯一的 UUID。
需要注意的是,虽然虚拟 DOM 可以有效地降低浏览器对真实 DOM 的操作次数,但也会带来一些额外的开销。例如,在生成和比较虚拟 DOM 树时,需要进行大量的计算和判断,可能会影响应用的整体性能。因此,在实际开发中,我们需要根据具体情况,权衡使用虚拟 DOM 的益处和代价,选择最适合自己应用的方案。
35.react新出来两个钩子函数是什么?和删掉的will系列有什么区别?(2)
react新出的哪两个钩子函数? 新出的getDerivedStateFromProps 与 getSnapshotBeforeUpdate 两个钩子。 少了componentWillMount,componentWillReceiveProps与componentWillUpdate三个都带有will的钩子。 1、getDerivedStateFromProps 这个钩子的作用其实就是从props获取衍生的state。getDerivedStateFromProps中返回一个对象用于更新当前组件的state,而不是直接取代。 2、getSnapshotBeforeUpdate 这个钩子的意思其实就是再组件更新前获取快照,此方法一般是结合componentDidUpdate使用,getSnapshotBeforeUpdate中返回的值将作为第三参数传递给componentDidUpdate。 为什么删掉will? 三个钩子函数是被废弃,但是不是直接不能用了,而是官方会给出警告并推荐我们在这三个钩子前添加UNSAFE_前缀。 为什么被废弃呢,因为使用率并不太高,这三个钩子很容易被误解和滥用,这几个钩子不稳定。
36.React的props.children使用map函数来遍历会收到异常显示,为什么?应该 如何遍历?
使用 map
函数遍历 React 组件的 props.children
时,可能会遇到异常显示的问题。这是因为 props.children
在不同情况下的类 型不同,导致 map
函数操作的方式也不同。
解决方法:
1.首先检查 props.children
的类型。
2.如果它是一个数组,那么可以直接使用 map
函数进行遍历。
3.如果它只有一个子元素,需要先将它转换为数组,然后再进行遍历
37.ts中抽象类的理解?
在 TypeScript 中,抽象类是一种特殊的类,用于作为其他类的基类或父类,并且不能直接实例化。抽象类主要用于定义一组通用的属性和方法,然后让其他类继承并实现这些抽象类中定义的方法。
抽象类通过使用 abstract
关键字进行声明。抽象类可以包含抽象方法、普通方法和属性。
抽象类的主要特点包括:
- 不能直接实例化,只能被继承。
- 可以包含抽象方法、普通方法和属性。
- 抽象方法必须在派生类中实现。
- 子类可以覆盖或扩展抽象类中的方法和属性。
38.redux本来是同步的,为什么它能执行异步代码?实现原理是什么?中间件的实现原理是什么?
Redux 是一个使用单一数据源和不可变数据的状态管理库,它本身是同步的,但通过中间件可以实现异步操作。Redux 中最常用的异步中间件是 Redux Thunk、Redux Saga 和 Redux Observable。
具体来说,当我们需要在 Redux 中执行异步代码时,通常会使用如下流程:
- 在 View 中触发一个异步 Action。
- Action 发送到 Reducer,Reducer 更新 Store 中的状态。
- 如果需要执行异步操作,Middleware 捕获到这个 Action,并执行异步代码。
- 异步代码完成后, Middleware 发送新的 Action 到 Reducer,Reducer 再次更新 Store 中的状态。
实现原理是中间件利用了 Redux 提供的 action 和 reducer 的单向数据流机制,使得 action 可以被拦截并且异步处理后再次派发出去。中间件本质上是对 dispatch 函数的重写,并且它可以执行某些操作,例如异步处理、日志记录、错误报告等。
中间件的原理是 Redux 通过在 dispatch 中间注入中间件的执行代码,在 action 到达 reducer 之前对 action 进行了修改或者是对应的 side effect 操作。具体来说,每个中间件都是一个函数,它接收 store 的 dispatch 和 getState 函数作为参数,返回一个新的函数,这个函数被用来包装 action creator,在 dispatch 前后进行操作。这种方式支持链式调用多个中间件,以便进行不同操作,例如数据处理、异步调用、日志记录、错误报告等。
39.redux中同步action与异步action最大的区别是什么?
1.同步 Action: 同步 action 是指在触发后立即执行并完成的 action。具体表现为,在 Redux 中通过 dispatch 触发同步 action 后,它 会立即被发送到 reducer 进行状态更新
2.异步 Action: 异步 action 是指在触发后需要一定时间进行处理的 action,通常用于处理异步操作,比如网络请求、定时器等。异步 action 通常会触发多个相关的同步 action,以表示异步操作的不同阶段
40.redux-saga和redux-thunk的区别与使用场景?
Redux Saga 和 Redux Thunk 都是用于处理异步操作的 Redux 中间件,它们在实现和使用上有一些区别,适用于不同的场景。
- 区别:
- 实现方式:Redux Thunk 是一个函数,允许我们在 Action Creator 中返回一个函数,这个函数可以进行异步操作并手动调用 dispatch。而 Redux Saga 则基于 ES6 的 Generator 函数,通过使用特定的语法来处理异步操作。
- 控制流程:Redux Thunk 使用简单的回调函数来处理异步操作,通常是通过链式调用多个 Action,从而实现异步流程控制。而 Redux Saga 则使用生成器来定义和控制异步操作的流程,通过监听和响应不同的 Action 来执行相应的操作。
- 异常处理:Redux Thunk 需要手动处理错误,通过 try-catch 捕获异常,并在回调函数中 dispatch 错误信息。而 Redux Saga 具备异常捕获和处理的能力,在 Generator 函数内部可以使用 try-catch 捕获异常,并且可以派发不同的 Action 处理异常情况。
- 使用场景:
- Redux Thunk 适用于简单的异步场景,例如发起一个 AJAX 请求并在请求成功后更新状态。它的学习曲线比较低,容易上手,适合于对异步处理需求不复杂的项目。
- Redux Saga 适用于复杂的异步场景,例如需要处理多个连续的异步操作、任务取消、并发请求等。它提供了更强大和灵活的异步处理能力,并且通过 Generator 函数的形式使得异步流程易于阅读和维护。但是相对复杂性也较高,需要掌握 Generator 函数和 Saga 相关的代码结构。
总而言之,Redux Thunk 适用于简单的异步操作,学习曲线较低;而 Redux Saga 适用于复杂的异步场景,提供了更强大和灵活的异步处理能力。选择哪个中间件取决于项目的具体需求和开发团队的技术背景。
41.在使用redux过程中,如何防止定义的action-type的常量重复?(2)
在 Redux 中,为了避免定义的 action type 常量重复,可以采用以下几种方式:
- 使用字符串常量:定义 action type 时使用字符串常量,在不同的模块或文件中使用不同的命名空间来确保唯一性。
- 使用枚举类型:使用 TypeScript 的枚举类型来定义 action type,枚举成员的名称是唯一的
- 使用工具库:可以使用一些辅助工具库来帮助管理和生成唯一的 action type,例如
redux-act
、redux-actions
等。
42.CDN的特点及意义?
CDN(Content Delivery Network,内容分发网络)是一种分布式的网络架构,其目标是通过将内容缓存在离用户较近的边缘节点上,提供更快速、稳定的内容分发服务。CDN的特点和意义如下:
- 高效快速:CDN利用分布在全球各地的边缘节点,将内容缓存到离用户最近的节点上,当用户请求内容时,可以从最近的节点获取,减少了传输距离和网络拥堵,提供了更快的响应速度和较低的延迟。
- 负载均衡:CDN通过智能路由和负载均衡技术,将用户请求分配到最优的节点,避免了单一服务器的过载,提高了整体的系统吞吐量和可用性。
- 提升用户体验:由于CDN提供了更快的响应速度和较低的延迟,用户可以更快速地访问和加载网页、图片、视频等内容,提升了用户的体验感受,降低了用户的等待时间。
- 减轻源站压力:通过将静态内容缓存到CDN节点上,可以减轻源站的负载压力,提高源站的可扩展性和稳定性,同时减少了源站与用户之间的直接流量,降低了网络成本。
- 抵御网络攻击:CDN可以通过分布式的架构和安全防护机制,提供一定程度的网络攻击防护,如分布式拒绝服务攻击(DDoS)的缓解。
- 全球覆盖:CDN网络分布在全球各地的节点,可以实现全球范围内的内容分发,无论用户身处何地,都可以享受到较快速的网络访问。
综上所述,CDN具有高效快速、负载均衡、提升用户体验、减轻源站压力、抵御网络攻击和全球覆盖等特点。在大规模的网络应用中,使用CDN能够显著提升网站性能、可靠性和安全性,为用户提供更好的体验,并降低运营成本。
43.为什么for循环比forEach性能高
在 JavaScript 中,常用的循环有 for 循环和 forEach 循环。虽然两者都可以遍历数组,但它们的实现方式不同,因此性能也有所不同。
for 循环是一种基于索引值(或下标)的循环方式,通过数组的下标索引来访问数组元素。而 forEach 循环则是一种迭代器,对数组中的每个元素都执行一次回调函数。
因此,for 循环相对于 forEach 循环具有以下优势:
- for 循环不需要编写额外的函数,可以直接对数组进行操作,因此其执行过程相对更加高效。
- 在 for 循环中,可以通过定义一个本地变量(如
var len = arr.length
)将数组长度缓存起来,避免多次访问arr.length
属性导致的性能损失。 - 在需要对数组进行修改时,for 循环比 forEach 循环更为方便且高效。因为在 for 循环中,可以通过获取数组的下标索引来修改数组元素,而在 forEach 循环中无法直接修改数组元素。
总体而言,在大多数情况下,for 循环比 forEach 循环更具有优势。但是,对于需要进行异步操作的情况,forEach 循环可能更为适用,因为它可以支持 async/await
操作,而 for 循环不支持。
需要注意的是,虽然 for 循环比 forEach 循环更快,但在实际应用中,在性能方面的差别通常不会太大。因此,选择使用哪种循环方式应该根据实际情况而定,以符合代码的可读性、可维护性和执行效率等方面的要求。
44.redux实现原理?
Redux 是一个 JavaScript 状态管理库,它可以用于管理应用程序中的状态。Redux 的实现原理可以简单概括为以下几个步骤:
创建 store:创建一个全局的存储对象作为状态管理的中心。Store 由 Redux 提供,它包含应用程序的当前状态,还包括实现更新状态的 reducer。
创建 reducer:reducer 是纯函数,它接收旧的 state 和 action,返回新的 state。Reducer 的主要作用是根据 action 的类型来更新 state,同时保证 state 的不可变性。
创建 action:action 是一个普通的 JavaScript 对象,它描述了要发生的操作。Action 包含一个 type 属性,表示要执行的行动类型,还可以包含其他数据。
分发 action:通过调用 store.dispatch() 方法将 action 分发到 reducer,这个过程会触发 state 的更新。
更新 state:reducer 根据接收的 action 类型更新 store 中的 state,同时返回一个新的 state。
订阅 state 的变化:可以通过 store.subscribe() 方法监听 state 的变化,在 state 更新时执行相应的操作。
45.React render方法的原理,在什么时候会触发?(2)
render()
方法的原理:
根据组件的状态和属性(props)来生成对应的虚拟 DOM 树。React 使用虚拟 DOM 来表示组件的界面结构,并通过比较更新前后 的虚拟 DOM 树找出差异,然后将差异部分进行高效地更新到真实的 DOM 上,从而实现页面的局部更新
render()
方法会在以下情况下触发:
1.组件首次挂载:当组件第一次被渲染到真实的 DOM 上时,render()
方法会被调用
2.组件的状态或属性发生变化:当组件的状态(通过 setState()
方法更新)或属性发生变化时,React 会自动重新调用 render()
方法,生成新的虚拟 DOM,并进行比较和更新
3.父组件重新渲染:如果父组件的 render()
方法被调用,那么其中包含的子组件的 render()
方法也会被触发
46.谈谈你是如何做移动端适配的?
对于移动端适配,一种常用的方法是响应式布局(Responsive Layout)和媒体查询(Media Queries)。以下是一些常见的移动端适配策略:
- 使用Viewport meta标签:在HTML文档的头部添加Viewport meta标签,通过设置
width=device-width
,可以告诉浏览器使用设备的宽度作为页面的宽度。另外,还可以设置initial-scale
、maximum-scale
和minimum-scale
等属性来控制缩放行为。 - 使用相对单位:在样式表中使用相对单位(如百分比、em、rem)来定义元素的尺寸和布局,相对单位可以根据屏幕尺寸进行适配。
- 弹性布局:使用弹性盒模型(Flexbox)或网格布局(Grid)等弹性布局方式,使得元素能够根据可用空间自动调整大小和位置。
- 媒体查询:利用CSS3中的媒体查询功能,根据不同的屏幕尺寸和设备特性,为不同的屏幕宽度范围应用不同的样式规则。可以根据需要调整字体大小、布局结构、隐藏或显示某些元素等。
- 图片适配:通过设置图片的最大宽度为100%来保证图片在不同尺寸的屏幕上自适应。同时,使用高分辨率的图片(如Retina屏幕),以提供更清晰的图像。
- 测试和调试:在开发过程中,使用模拟器或真实设备进行测试,并通过调试工具(如Chrome开发者工具)来检查和优化页面在不同屏幕大小下的样式和布局。
综合使用以上方法,可以使网页在不同尺寸的移动设备上呈现出良好的用户体验,适应不同的屏幕大小和设备特性。
当然,对于较为复杂的移动端适配需求,可以考虑使用CSS预处理器(如Sass、Less)或CSS框架(如Bootstrap、Foundation)等工具来简化和优化适配过程。
47.移动端1像素的解决方案?
在移动端开发中,由于不同设备的像素密度差异,1像素问题成为了一个常见的难题。如果我们不对这个问题进行针对性的解决,那么会导致页面显示效果不美观,甚至影响用户体验。
以下是一些解决方案:
- 使用css3的scale属性:将要渲染的元素放大一倍,然后通过scale缩小回去。例如,将一个1像素的边框放大到2像素,再通过scale(0.5)恢复原来大小。这种方法可以使边框看起来更加清晰,但是可能会影响元素的布局和性能。
- 通过伪元素实现:使用伪元素before或after,并设置其content属性为空,然后通过border设置为1像素粗细的边框。例如:
cssCopy Code.box::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border: 1px solid #ddd;
-webkit-box-sizing: border-box;
box-sizing: border-box;
z-index: -1;
}
这种方法可以避免影响元素布局,但是可能会增加HTML代码量和CSS复杂度。
3. 通过JavaScript动态设置viewport缩放比例: 使用JavaScript获取设备物理像素和设备独立像素的比例,然后动态设置viewport的缩放比例,从而实现1像素问题的解决。例如:
javascriptCopy Codevar scale = 1 / window.devicePixelRatio;
### 自学几个月前端,为什么感觉什么都没学到??
----------------------------------------------------------------------------------
这种现象在很多的初学者和自学前端的同学中是比较的常见的。
因为自学走的弯路是比较的多的,会踩很多的坑,学习的过程中是比较的迷茫的。
最重要的是,在学习的过程中,不知道每个部分该学哪些知识点,学到什么程度才算好,学了能做什么。
很多自学的朋友往往都是自己去找资料学习的,资料上有的或许就学到了,资料上没有的或许就没有学到。
这就会给人一个错误的信息就是,我把资料上的学完了,估计也-就差不多的了。
但是真的是这样的吗?非也,因为很多人找的资料就是很基础的。学完了也就是掌握一点基础的东西。分享给你一份前端分析路线,你可以参考。
**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**
![](https://img-blog.csdnimg.cn/img_convert/15be8206a9f6e5bd9e8e930303b613ee.png)
还有很多的同学在学习的过程中一味的追求学的速度,很快速的刷视频,写了后面忘了前面,最后什么都没有学到,什么都知道,但是什么都不懂,要具体说,也说不出个所以然。
所以学习编程一定要注重实践操作,练习敲代码的时间一定要多余看视频的时间。
d #ddd;
-webkit-box-sizing: border-box;
box-sizing: border-box;
z-index: -1;
}
这种方法可以避免影响元素布局,但是可能会增加HTML代码量和CSS复杂度。
3. 通过JavaScript动态设置viewport缩放比例: 使用JavaScript获取设备物理像素和设备独立像素的比例,然后动态设置viewport的缩放比例,从而实现1像素问题的解决。例如:
javascriptCopy Codevar scale = 1 / window.devicePixelRatio;
### 自学几个月前端,为什么感觉什么都没学到??
----------------------------------------------------------------------------------
这种现象在很多的初学者和自学前端的同学中是比较的常见的。
因为自学走的弯路是比较的多的,会踩很多的坑,学习的过程中是比较的迷茫的。
最重要的是,在学习的过程中,不知道每个部分该学哪些知识点,学到什么程度才算好,学了能做什么。
很多自学的朋友往往都是自己去找资料学习的,资料上有的或许就学到了,资料上没有的或许就没有学到。
这就会给人一个错误的信息就是,我把资料上的学完了,估计也-就差不多的了。
但是真的是这样的吗?非也,因为很多人找的资料就是很基础的。学完了也就是掌握一点基础的东西。分享给你一份前端分析路线,你可以参考。
**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**
![](https://img-blog.csdnimg.cn/img_convert/15be8206a9f6e5bd9e8e930303b613ee.png)
还有很多的同学在学习的过程中一味的追求学的速度,很快速的刷视频,写了后面忘了前面,最后什么都没有学到,什么都知道,但是什么都不懂,要具体说,也说不出个所以然。
所以学习编程一定要注重实践操作,练习敲代码的时间一定要多余看视频的时间。