周考一理论总结

说说你对react的理解?有哪些特性?


React是一个用于构建用户界面的JavaScript库。它的核心思想是将用户界面划分为一个个可复用的组件,每个组件都有自己的状态,并通过数据的改变来更新UI。React采用了虚拟DOM的机制,通过比较差异来高效地更新DOM,从而提高了性能。

React具有以下特性:

1. 组件化:React将用户界面划分为若干组件,每个组件都是独立的、可复用的。这样可以使代码更加模块化,提高开发效率。

2. 声明式编程:通过使用JSX语法,可以以声明式的方式描述用户界面应该是什么样子的。React会根据数据的变化来自动更新界面,无需手动操作DOM。

3. 虚拟DOM:React使用虚拟DOM来表示用户界面,它是一个轻量级的JavaScript对象。当数据发生变化时,React会通过比较新旧虚拟DOM的差异来计算出最小的DOM操作,然后批量更新到真实的DOM上,减少了对DOM的直接操作,提高了性能。

4. 单向数据流:React中数据流是单向的,即数据从父组件流向子组件。这样可以使应用的数据流动更加可控,易于调试和排查问题。

5. 生命周期:React组件拥有生命周期方法,可以在组件的各个阶段进行操作。例如,组件挂载、更新、卸载时都会触发相应的生命周期方法,可以在这些方法中执行一些特定的逻辑。

6. 高效更新:由于使用虚拟DOM和差异比较算法,React能够高效地更新用户界面。它只会更新发生变化的部分,而不是整个界面,从而提升性能和用户体验。

总的来说,React是一个功能强大且灵活的库,它简化了构建复杂用户界面的过程,并提供了高效的更新机制。同时,React生态系统中也有丰富的第三方库和工具,可以进一步提升开发效率和开发体验。


说说Real DOM和Virtual DOM的区别?优缺点?

Real DOM 是浏览器提供的实际 DOM 结构,它直接映射到网页中的 HTML 元素,当网页中的元素发生变化时,Real DOM 会被更新,这种更新会触发浏览器进行页面重绘和重新布局,因为操作 Real DOM 需要直接对浏览器进行操作,所以性能相对较低。Virtual DOM 是以 JavaScript 对象的形式表示的一种抽象的 DOM 结构。它是对 Real DOM 的一种快照或者副本,通过对 Virtual DOM 的操作和对比,可以找出需要进行更新的部分,并只更新需要变化的部分。这样的更新是在 JavaScript 层面上进行的,减少了对 Real DOM 的直接操作,提高了性能。

优点:

Virtual DOM 可以减少与 Real DOM 的交互次数,提高页面渲染的性能。Virtual DOM 可以在不同平台(如浏览器、移动端等)上使用,保持一致的操作方式。Virtual DOM 可以提供更简洁的开发模式,使得前端开发更加高效和可维护。

缺点:

使用 Virtual DOM 需要对其原理有一定的了解和学习成本。引入 Virtual DOM 需要增加额外的代码和运行时开销。某些复杂的 DOM 操作可能不太容易转换为 Virtual DOM 的操作方式。


说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?
可以分为三个阶段:

挂载阶段

constructor:构造函数,在创建组件实例时调用,用于初始化状态和绑定事件处理程序

Static getDrivedStateFromProps:在组件实例化和接收新的props之前被调用,用于根据新的props更新state

Render:渲染函数

ComponentDidMount:在组件被挂载到DOM后立即调用

更新阶段

Static getDrivedStateFromProps:在组件实例化和接收新的props之前被调用,用于根据新的props更新state

shouldComponentUpdate:在组件接受到新的props或state被调用时,用于判断是否需要重新渲染组件

Render:渲染函数

getSnapshotBeforeUpdate:用于获取更新前的DOM快照

componentDidUpdate:组件更新完成后被调用

卸载阶段

ComponentWillUnmount:在组件被卸载和销毁之前调用

错误处理阶段

static getDerivedStateFromError:在子组件抛出错误后调用,用于渲染一个备用的UI界面。

componentDidCatch:在子组件抛出错误后调用,用于记录错误信息或进行错误上报。


说说React中setState执行机制?


在React中,setState是用于更新组件状态的方法。当我们调用setState时,React会进行以下步骤来执行状态更新:

1. 合并更新:React会首先将我们传递给setState的对象合并到当前的状态中。这意味着我们可以只更新状态的一部分,而不是整个状态对象。

2. 队列更新:React将新的状态放入一个更新队列中,而不是立即更新组件的状态。

3. 批量更新:React会在适当的时机,比如在事件处理函数和异步代码中,对状态更新进行批量处理。

4. 计算差异:当React准备更新组件时,它会使用虚拟DOM进行新旧状态的比较,以确定需要更新的部分。

5. 更新组件:根据计算得出的差异,React会执行最小化的DOM操作,将变化应用到真实的DOM上。

6. 触发生命周期方法:状态更新完成后,React会触发相应的生命周期方法,比如componentDidUpdate(),我们可以在这些方法中执行额外的操作。

需要注意的是,由于React对状态更新进行了批量处理,因此在同一个事件处理函数或同一块异步代码中的多次setState调用,可能会被合并为一次更新。这样可以提高性能,并减少不必要的渲染。

另外,setState的更新是异步的,这意味着我们不能立刻获取到更新后的状态。如果我们需要在更新后执行某些操作,可以使用回调函数作为setState的第二个参数,这个回调函数会在更新完成后被调用。
 


说说react的事件机制?


React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、时间的合成、事件冒泡、事件派发等,在React中这套事件机制被称之为合成事件。

React事件和原生事件的执行顺序:

react所有事件都挂载在document对象上,当真实的DOM元素触发事件,会冒泡到document对象后,在处理React事件,所以会先执行原生事件,在处理React事件,最后真正执行document上挂载的事件

总结:

1.React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的
DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
2.React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
3.React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
4.React 有一套自己的合成事件 SyntheticEvent
 


React组件之间如何通信?


在React中,组件之间可以通过以下几种方式进行通信:

1. Props传递:父组件可以通过props向子组件传递数据或回调函数。子组件可以通过props接收父组件传递的数据,并根据需要进行处理或渲染。

2. 上下文(Context):Context允许数据在组件树中传递而无需通过中间组件传递props。通过创建一个Context对象并将其提供给组件树,我们可以在组件树中的任何地方访问该Context的值。

3. 组件引用(Refs):通过使用ref,我们可以在父组件中访问子组件的实例。这样就可以直接调用子组件的方法或访问其属性。

4. 自定义事件:可以在应用中实现自定义事件系统,通过订阅和发布事件的方式进行组件之间的通信。这可以使用第三方库,如eventemitter3或Redux的发布/订阅机制来实现。

5. 状态管理库:使用第三方的状态管理库(如Redux、MobX)可以让组件之间共享状态并进行通信。这些库提供了一个中央存储,它可以在应用的任何地方被访问和修改。

需要根据具体的场景和需求选择适合的通信方式。通常情况下,使用props传递数据是React中最常用的方式,而使用上下文、组件引用或状态管理库则用于更复杂的应用场景。
 


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


它们都是React中用于处理表单元素的两种方式

受控组件是指由React控制并管理器状态的组件。受控组件可以通过setState方法实时的更新并反应用户的输入

应用场景:当需要对用户输入进行验证或处理时,受控组件通常在事件处理函数中进行验证或处理逻辑,可以即时的提供反馈和用户输入;可以根据一个输入框的值动态改变另一个表单元素的选项

非受控组件是指不受React控制并直接访问DOM节点的组件。在非受控组件中,表单元素的值由DOM节点自身管理,而不需要通过组件的状态来控制,通常使用ref来获取表单元素的值

应用场景:

当需要对用户输入进行简单操作时,而不需要渲染复杂的验证逻辑或状态管理时,可以使用非受控组件。它可以提供一种简洁的方式来处理表单输入

当处理大量表单元素时,使用非受控组件可能会更加高效,因为不需要在组件中维护大量的状态
 


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


Fiber 架构是一种新的协调机制,它对 React 的渲染和调度进行了重新设计和实现。Fiber 架构的核心目标是实现更高效的渲染,并提高用户界面的流畅度和响应性。

在传统的栈帧调用树中,React 在进行组件更新时是采用递归的方式遍历整个组件树,这样的处理方式会导致长时间的 JavaScript 运算,使得 UI 的渲染和用户交互的响应能力受到限制。Fiber 架构通过引入一个新的数据结构——Fiber 节点链表,来实现可中断和可恢复的渲染过程。

解决的问题:

Fiber 架构将渲染任务切分成多个小任务,可以根据优先级进行调度,使得高优先级任务可以插队执行,提高应用的响应性。

Fiber 架构允许 React 在渲染过程中中断和恢复,这样可以在浏览器空闲时执行渲染任务,减少对用户交互的阻塞。

Fiber 架构通过划分不同优先级的任务,并使用任务队列管理,可以快速地响应用户交互和处理高优先级的任务,提升应用的性能和用户体验。

Fiber 架构引入了错误边界的概念,允许组件捕获和处理自身及其子组件的错误,防止错误的扩散并提供更好的用户反馈。
 


说说react diff的原理是什么?


它是用于比较两棵虚拟DOM树的差异,并确定最小的更新操作,以最大程度地减少对真实DOM的操作,提高性能。React会逐层比较新旧两棵虚拟DOM树的节点,判断他们是否相同类型的组件或DOM元素。对于列表类型的组件或DOM元素,React会采用key属性来进行唯一的标识,以提高渲染性能。React通过同时从新旧两个子树的开头和结尾进行比较,从而提高效率。在比较中,如果类型不同的话,它会完全替代旧节点,如果两个节点类型相同的时候,但属性不同,则会更新属性。如果React发现某一节点在两棵树中是相同的,则会保持该节点的真实DOM对象不变


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


在Redux中,中间件是一个可以处理Redux action的函数。它位于发起一个action和达到reducer之间,允许你在action到达reducer之前进行一些额外的处理。

中间件可以用于实现诸如日志记录、异步调用、错误处理等功能。它们可以拦截action并在需要的时候对其进行修改,也可以在触发action后执行额外的操作。

常用的Redux中间件包括:
1. redux-thunk:允许action创建函数返回一个函数,进行异步操作。
2. redux-saga:基于Generators的解决方案,用于处理复杂的异步流程和副作用。
3. redux-logger:用于在控制台中记录Redux action和状态的变化。
4. redux-promise:允许action创建函数返回一个Promise,处理异步操作。

实现原理:
中间件的实现原理是基于Redux的洋葱模型(Onion Architecture)。当一个action被派发时,它会首先经过第一个中间件,然后依次经过后续的中间件,最后到达reducer进行状态的更新。

每个中间件可以通过调用next(action)来将控制权传递给下一个中间件,或者终止请求链并阻止该action到达reducer。这使得中间件能够在处理前后执行一些自定义逻辑。

Redux中间件的基本结构是一个可以接收store的函数,它返回另一个函数,该函数接收next(用于调用下一个中间件)和action(当前的action)。这种结构可以使用柯里化技术来实现。

总的来说,Redux中间件允许开发者在Redux流程的不同阶段进行拦截和处理,以实现各种额外的功能和复杂的业务逻辑。
 


什么是强缓存和协商缓存?


强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network选项中可以看到该请求返回200的状态码;

协商缓存:向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源。


说说React jsx转换成真实DOM的过程?


JSX编写: 在React开发中,使用JSX语法编写组件的UI结构,例如<div>Hello, World!</div>。

Babel编译: JSX代码通过Babel等工具进行编译,将JSX转换为对应的React.createElement()函数调用。

创建虚拟DOM: 转换后的JavaScript代码会创建一个描述UI结构的JavaScript对象,称为虚拟DOM节点。该节点包含了标签、属性和子节点等信息。

虚拟DOM diff: 当组件需要重新渲染时,React会将新生成的虚拟DOM与之前渲染的虚拟DOM进行比较,找出需要更新的部分。

生成真实DOM: React根据比较结果,将需要更新的部分使用对应的原生DOM操作生成真实的DOM元素。

更新页面: 最后,React将生成的真实DOM插入到页面中相应的位置,完成页面的视图更新。


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


功能范围不同: @reduxjs/toolkit关注于简化Redux本身的使用,提供一些工具和API。而react-redux则关注于Redux在React中的使用,提供与React组件的连接和交互。

提供的功能不同: @reduxjs/toolkit提供了快速创建Redux slice、配置Redux store、处理异步操作等功能。react-redux提供了Provider组件和connect函数,用于将Redux store和React应用程序连接起来,并在组件中使用Redux的状态和行为。

属于不同的软件包: @reduxjs/toolkit和react-redux是两个独立的软件包,可以单独使用。


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


React的render方法是用于将组件的输出渲染到DOM中的方法。它根据组件的状态和属性生成相应的UI,然后更新DOM以反映这些更改。

Render方法在以下情况下会被触发:

1. 组件初始渲染:当组件第一次被创建并且需要在页面上显示时,render方法会被调用。

2. 组件的状态或属性发生变化:当组件的状态(state)或属性(props)发生变化时,React会自动重新调用render方法来更新组件的UI并重新渲染到DOM中。这是React的核心特性之一,称为"声明式编程",通过数据的变化来驱动界面的更新。

3. 父组件重新渲染:如果一个组件的父组件发生了重新渲染,那么子组件的render方法也会被调用。

需要注意的是,React并不会每次状态或属性变化都立即重新渲染组件,而是通过一种称为"调和"的机制来进行批量更新,以提高性能。React会比较前后两次的状态或属性差异,并尽可能地进行最小化的更新。

总结起来,render方法是React用于将组件的UI渲染到DOM中的重要方法,在组件初始渲染、组件状态或属性变化以及父组件重新渲染时被触发。
 


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


使用React.memo或PureComponent:通过使用React.memo高阶组件或继承自React.PureComponent的类组件,可以避免不必要的组件重新渲染,只在props发生变化时重新渲染。

使用shouldComponentUpdate:自定义组件时,通过重写shouldComponentUpdate方法,手动比较props和state的变化,决定是否重新渲染组件。

使用Key属性:在使用使用map渲染列表项时,为每个列表项提供唯一的key属性。这有助于React更准确地追踪组件的变化,提高列表项的更新性能。

懒加载:对于一些页面中不是首次展示的组件或模块,可以使用React.lazy和Suspense来进行懒加载,将组件的加载推迟到真正需要的时候,提高了初始渲染的速度。

分割大型组件:将大型的复杂组件拆分为更小的可复用组件,使用组合的方式来构建页面。这样可以提高组件的可维护性、复用性,并减少不必要的渲染开销。

使用高效的列表渲染:在需要渲染大量列表项的情况下,可以使用虚拟化技术来优化性能,只渲染当前可见的列表项,节省内存和渲染时间。

使用memoize函数:对于一些计算成本较高的函数,可以使用memoize函数进行记忆化处理,缓存函数的结果,避免重复计算。

使用React Profiler:利用React DevTools中的Profiler工具,可以分析组件的渲染性能,发现性能瓶颈,并进行相应的优化调整。


说说如何借助webpack来优化前端性能?


1. 压缩代码:删除多余的代码、注释、简化代码的写法等等⽅式。可以利⽤webpack的 UglifyJsPlugin 和 ParallelUglifyPlugin 来压缩JS⽂件, 利⽤ cssnano (css-loader?minimize)来压缩css

2. 利⽤CDN加速: 在构建过程中,将引⽤的静态资源路径修改为CDN上对应的路径。可以利⽤webpack对于 output 参数和各loader的 publicPath 参数来修改资源路径

3. Tree Shaking: 将代码中永远不会⾛到的⽚段删除掉。可以通过在启动webpack时追加参数 --optimize-minimize 来实现

4. Code Splitting: 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利⽤浏览器缓存

5. 提取公共第三⽅库: SplitChunksPlugin插件来进⾏公共模块抽取,利⽤浏览器缓存可以⻓期缓存这些⽆需频繁变动的公共代码
 


 

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


1. 对象没有被正确地销毁或释放

在JavaScript中,如果一个对象没有被正确地销毁或释放,它将一直占用内存。这种情况通常发生在使用全局变量或闭包时,因为这些变量或闭包会一直存在于内存中,直到程序结束。

2. 循环引用

循环引用是指两个或多个对象之间相互引用,导致它们无法被垃圾回收器自动清除。这种情况通常发生在对象之间的相互引用,例如在JavaScript中的事件处理程序中。

3. DOM元素未被正确地移除

如果DOM元素没有被正确地从页面中移除,它将一直存在于内存中。这种情况通常发生在使用jQuery或其他DOM操作库时,因为这些库可能会缓存DOM元素并延迟它们的删除。

4. 定时器未被正确地清除

如果定时器没有被正确地清除,它将一直存在于内存中,导致内存泄漏。这种情况通常发生在使用setTimeout()和setInterval()函数时,因为这些函数会在后台运行并持续触发回调函数。

5. 全局变量未被正确地声明或初始化

如果全局变量没有被正确地声明或初始化,它们将一直存在于内存中,导致内存泄漏。这种情况通常发生在使用全局变量时,因为这些变量会一直存在于内存中,直到程序结束。

6.闭包未正确使用

在JavaScript中,闭包可以让函数访问其定义时的作用域,但如果未正确使用闭包,也可能导致内存泄漏。在使用闭包时,请确保只保留必要的引用,并在不需要时删除它们。

7.事件未正确解绑

在JavaScript中,如果注册了事件监听器却没有正确解绑,就会导致内存泄漏。例如,当一个DOM元素被删除时,它仍然会保留对事件监听器的引用,如果没有解绑,事件监听器将无法被垃圾回收。

8.大量数据未及时清理

在处理大量数据时,如果不及时清理无用的数据,就会导致内存泄漏。

9.使用了第三方库或框架

在使用第三方库或框架时,需要确保它们没有内存泄漏问题。如果使用了存在内存泄漏问题的库或框架,就会导致整个应用程序出现内存泄漏问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值