前端百问面试题之鱼龙混杂(第二篇)

1.清除浮动的几种方式,各自的优缺点,推荐使用哪种?

  1. 使用清除浮动的空元素:

    • 通过在父容器的最后添加一个空的<div><br>元素,然后为该元素应用clear: both;样式,以强制父容器包含浮动元素。

    • 优点:简单易用,兼容性较好。

  • 缺点:会添加不必要的空元素到HTML中,语义不清晰。

2.使用overflow属性:可以通过设置父元素的overflow属性为auto或hidden来清除浮动。例如:

  • 优点:简单易行,不需要添加额外的元素。

  • 缺点:可能会隐藏溢出元素,造成内容被截断。

  1. 使用伪元素清除浮动

    • 为父容器添加一个伪元素,然后为伪元素应用clear: both;样式。

    • 优点:无需额外的HTML元素,语义更清晰。

    • 缺点:需要额外的CSS样式。

  2. 使用clearfix

    • 为父容器添加一个具有清除浮动样式的CSS类。

    • 优点:具有可重用性,语义清晰。

    • 缺点:需要在HTML中添加类。

不同的清除浮动方法适用于不同的情况。

如果你不希望引入额外的HTML元素,可以选择使用伪元素或overflow: hidden;

如果你需要清除浮动并且想保持语义清晰,可以使用伪元素或clearfix类。

如果不担心语义,可以使用清除浮动的空元素。

总的来说,推荐使用伪元素或clearfix类,它们既能清除浮动又保持了语义的清晰。

2.Sass、LESS区别是什么?大家为什么要使用他们?

Sass(Syntactically Awesome Style Sheets)和LESS(Leaner CSS)是两种流行的CSS预处理器,它们在原生CSS的基础上提供了一些额外的功能和语法。

以下是Sass和LESS之间的一些主要区别:

  1. 语法风格:Sass使用类似于Ruby的缩进式语法,而LESS使用类似于普通CSS的大括号和分号。

  2. 变量:Sass和LESS都支持变量,可以定义并重用一些常用的值。但是在Sass中,变量需要以$开头,而在LESS中,变量需要以@开头。

  3. 嵌套规则:Sass和LESS都允许嵌套CSS规则,可以更清晰地表示层级关系。不过,在Sass中,嵌套规则使用缩进,而在LESS中,嵌套规则使用大括号。

  4. 运算操作:Sass和LESS都支持一些基本的数学运算,如加法、减法等。然而,Sass更加强大,支持更多的运算和函数。

为什么要使用Sass和LESS呢?以下是一些使用它们的主要优势:

  1. 变量和嵌套:Sass和LESS允许使用变量和嵌套语法,可以简化CSS代码的编写和维护。通过变量,可以在整个样式表中共享和重用值,而嵌套语法可以更好地组织和表示层级关系。

  2. 混合(Mixins):Sass和LESS都支持混合,它们允许将一组CSS属性和值定义为一个可重用的片段,然后通过调用混合来使用这些定义。这样可以减少冗余的代码,并提高开发效率。

  3. 导入(Import):Sass和LESS可以通过导入其他样式文件来组织代码,这使得样式代码更易于管理和维护。

  4. 函数和运算:Sass比LESS提供了更强大的函数和运算功能,例如颜色操作、字符串处理等。这些功能可以帮助开发者更加灵活地处理样式。

总的来说,Sass和LESS都是非常流行的CSS预处理器,它们通过提供更强大和便捷的功能来改善CSS的编码过程,并提高代码的可维护性。选择使用哪个预处理器取决于个人偏好和项目要求。

3.说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?(2)

在React中,组件的生命周期可以分为三个主要的阶段:挂载(Mounting)、更新(Updating)和卸载(Unmounting)。以下是每个阶段对应的方法:

  1. 挂载阶段(Mounting Phase):

  • constructor:组件实例被创建时调用,用于初始化状态和绑定事件处理函数。

  • static getDerivedStateFromProps:在组件实例化和更新时都会调用,它接收新的属性和当前状态,并返回一个更新后的状态。

  • render:根据组件的状态和属性,返回JSX元素进行渲染,负责生成组件的UI结构。

  • componentDidMount:组件已经被渲染到DOM中后调用,此时可以进行网络请求、订阅事件等副作用操作。

  1. 更新阶段(Updating Phase):

  • static getDerivedStateFromProps:在组件更新时调用,接收新的属性和当前状态,并返回一个更新后的状态。

  • shouldComponentUpdate:在组件即将更新之前调用,返回一个布尔值,用于判断是否需要进行组件的重新渲染。

  • render:重新渲染组件的UI结构。

  • getSnapshotBeforeUpdate:获取更新前的快照

  • componentDidUpdate:组件已经重新渲染并且更新到DOM上后调用,此时可以进行状态更新、网络请求等副作用操作。

  1. 卸载阶段(Unmounting Phase):

  • componentWillUnmount:组件将要从DOM中移除时调用,可以进行清理工作,如取消订阅、清除定时器等。

注意:在React 16.3及之后的版本中,一些生命周期方法被标记为过时(deprecated),并有一些新的生命周期方法被引入。例如,推荐使用getDerivedStateFromProps替代componentWillReceiveProps,使用componentDidUpdate替代componentWillUpdate。此外,还有一些新的生命周期方法,如static getDerivedStateFromError(处理组件错误)和getSnapshotBeforeUpdate(获取更新前的快照)。

需要注意的是,React Hooks的引入也改变了组件的生命周期处理方式,可以使用useEffect来替代类组件中的生命周期方法。

4.说说React中setState执行机制?

  1. setState 调用:当你调用 this.setState(newState, callback) 来更新组件的状态时,React 将新的状态对象 newState 存储在队列中,但不会立即进行状态更新。

  2. 状态更新合并:React 会将多个 setState 调用中的状态更新合并成一个单一的状态更新操作,以提高性能。

  3. 触发调和过程:React会使用合并后的状态来触发“调和过程”(Reconciliation)。在调和过程中,React会比较新旧状态,计算出需要更新的部分。

  4. 生成虚拟DOM:React将新状态映射到虚拟DOM树(Virtual DOM),并与之前的虚拟DOM树进行比较,找出差异。

  5. 计算差异:React使用虚拟DOM比较算法来计算新旧虚拟DOM之间的差异。

  6. 更新实际DOM:React将差异应用于实际DOM,只更新需要更改的部分,从而减少浏览器的重排和重绘。

  7. 生命周期钩子:如果提供了 setState 的回调函数 callback,React会在状态更新后调用它。

5.如果需要手动写动画,你认为最小时间间隔是多久,为什么?

如果需要手动写动画,最小时间间隔取决于设备的刷新率。在大多数计算机和移动设备上,屏幕的默认刷新率通常是60Hz,也就是每秒刷新60次。因此,最小时间间隔可以取1秒除以刷新率(即1秒/60Hz),约为16.67毫秒。

选择最小时间间隔的原因是为了保证动画流畅性。如果动画的帧速率超过了设备的刷新率,会出现"撕裂"或"闪烁"的效果,给用户带来不好的视觉体验。因此,将动画的最小时间间隔设置为屏幕刷新率的倒数,可以确保每个动画帧都能在设备屏幕刷新之前完成绘制和渲染。

当然,并不是所有的动画都需要运行在最小时间间隔上。对于简单的动画或低要求的场景,较高的时间间隔可能已经足够流畅。而对于复杂的动画或高要求的场景,可以选择更小的时间间隔,以获得更精细的动画效果。

需要注意的是,在实际编码中,由于JavaScript的计时器并非完全准确,实际控制动画的时间间隔可能存在一定的误差。因此,建议在编写动画代码时,结合使用现代Web浏览器提供的动画API(如requestAnimationFrame)来实现更精准和高性能的动画效果。

6.React组件之间如何通信?(4)

Props传递:React中最基本的组件通信方式。通过将数据作为props传递给子组件来实现通信。父组件可以向子组件传递数据,然后子组件可以访问这些数据并进行渲染。

回调函数:父组件可以将函数作为props传递给子组件,以便子组件可以调用这些函数来通知父组件发生了某些事件。

上下文(Context):React的上下文机制允许你在组件层次结构中的祖先组件和后代组件之间共享数据,而不必手动传递props。这对于全局状态管理非常有用。

状态管理库:对于大型应用程序,你可以使用状态管理库(如Redux、Mobx、或React的useContextuseReducer)来管理应用的状态,以便多个组件可以访问和修改共享的状态。

事件总线:你可以创建一个事件总线对象,允许组件订阅和触发事件,以便它们可以相互通信。这在某些特定情况下很有用,但要小心不要滥用它,以避免代码变得难以维护。

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

在React中,受控组件(Controlled Components)和非受控组件(Uncontrolled Components)是两种处理表单元素和用户输入的不同方式。

**受控组件:

  1. 定义:受控组件是一种通过React的状态(state)来控制用户输入的组件。这意味着组件的值和状态受React的控制,通常通过value属性来绑定表单元素的值,以及通过事件处理程序来处理用户输入。

  2. 应用场景:受控组件适用于需要在React状态中进行数据处理、验证和响应的情况。

**非受控组件:

  1. 定义:非受控组件是一种不使用React状态(state)来控制用户输入的组件。它们允许表单元素的值直接由DOM自身管理,而不受React的干预。通常,你通过ref来访问表单元素的值。

  2. 应用场景:非受控组件适用于不需要对用户输入进行React状态管理的情况,或者需要与第三方库或既有代码集成的情况。它们可能更容易实现,但在处理复杂的表单逻辑时可能更难维护,因为你需要手动处理数据验证和转换,以及编写自定义逻辑来处理用户输入。

选择受控组件还是非受控组件取决于具体情况。一般来说,对于大多数React应用,受控组件是首选,因为它们提供了更好的数据流控制和React状态管理。然而,非受控组件可能在某些情况下更方便,尤其是当与第三方库或既有代码交互时。

8.说说你对fiber架构的理解?解决了什么问题?(2)

  1. 渲染中断和调度优先级:传统的React调度算法是递归的,当组件树很深或组件数量很多时,可能导致渲染操作阻塞主线程,引起页面卡顿。React Fiber引入了可中断的渲染,将渲染过程分解为多个小任务,每个任务都有一个优先级。这样可以更好地控制渲染的优先级,允许浏览器在渲染期间响应用户交互。

  2. 增量更新:传统的React在更新时会进行完整的虚拟DOM比较,然后更新整个组件树。React Fiber引入了增量更新的能力,使得React可以只更新发生变化的部分,从而减少不必要的工作,提高性能。

  3. 并行处理:React Fiber引入了并行处理的概念,允许React在多个任务之间切换,从而更充分地利用多核CPU。这可以加速渲染操作,特别是在处理大型应用程序或复杂组件树时。

  4. 可终止和可恢复的渲染:Fiber允许React在渲染任务中间被终止,而不会破坏整个渲染过程。这对于处理大型列表或复杂UI场景非常有用。一旦浏览器有时间,React可以继续之前被中断的渲染任务。

  5. 更好的动画支持:React Fiber提供了更好的动画支持,可以更精确地控制动画和布局过程。这有助于创建流畅的用户体验。

  6. 自定义渲染器:Fiber的架构允许开发者创建自定义渲染器,以适应不同的平台和环境,如React Native、VR应用等。

总的来说,React Fiber的目标是改进React的渲染性能,使其更适用于大型和复杂的应用程序,提供更好的用户体验,同时提高了React的可扩展性,以适应各种不同的应用场景。这一架构的引入使得React更灵活、高效和强大。

9.说说react diff的原理是什么?(2)

  1. 触发更新:当应用程序状态发生变化,React会触发重新渲染过程。

  2. 构建Virtual DOM:React会创建一个虚拟DOM树,它是真实DOM树的轻量级副本。

  3. 进行Diff比较:React会将新的虚拟DOM树与先前的虚拟DOM树进行比较,这个过程叫做Diff算法。

  4. 计算差异:在Diff比较过程中,React会计算出需要进行的DOM更新操作,如添加、删除或更新DOM元素。

  5. 批量更新DOM:一旦差异计算完成,React会将所有需要的DOM更新操作批量执行,从而尽量减少对真实DOM的直接访问。

这个Diff算法的原理是为了最小化DOM操作,从而提高性能。React尽可能地减少了对真实DOM的访问,将多个DOM操作合并成单个操作,并使用各种优化策略来提高渲染效率。这使得React在更新视图时更加高效,而不需要重新渲染整个DOM树。

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

Redux中间件是一种用于扩展Redux的功能的机制,它允许你在Redux的dispatch过程中添加自定义的逻辑。通过中间件,你可以在action被派发到reducer之前或之后执行一些额外的操作,以满足不同的需求。

Redux中间件的主要原理是基于函数的洋葱模型(Onion Model),它允许你在Redux流程的不同阶段添加自定义逻辑,这些逻辑会按顺序执行。Redux中间件通常由一个函数构成,该函数返回一个函数,这个返回的函数接受store.dispatchstore.getState等参数,然后返回一个新的dispatch函数。这个新的dispatch函数可以拦截、修改、延迟或增强action派发的过程。

常用的Redux中间件有:

  1. redux-thunk:当你需要执行异步操作时,可以返回一个函数,该函数接收dispatchgetState作为参数,然后可以在异步操作完成后再次派发action。

  2. redux-saga:用于处理复杂的异步操作、副作用和并发流。允许你以声明性的方式管理和监视action的流,处理各种复杂的异步任务。

  3. redux-logger:用于在控制台中记录Redux操作和状态的中间件,用于开发和调试过程。

  4. redux-persist:用于将Redux状态持久化到本地存储,以便应用程序在重新加载时保持状态。

  5. redux-observable:用于处理异步操作和副作用。它允许你使用RxJS的强大功能来管理和监视action流。

Redux中间件的实现原理是基于函数的装饰器模式,它通过包装store.dispatch方法,来拦截和处理action。

11.路由原理 history 和 hash 两种路由方式的特点?

  • 在前端Web开发中,路由(Routing)是一种管理浏览器历史记录和URL的机制,用于实现单页应用(SPA)的导航和页面切换。常见的路由方式包括基于浏览器的historyhash两种。

    1. 基于history的路由

      • 特点

        • 使用HTML5的history API,使得URL可以更加友好和语义化。

        • 不会在URL中出现#字符,URL更加美观。

        • 可以使用真实的URL路径,例如/products/123

        • 支持服务端渲染(Server-Side Rendering,SSR)。

        • 可以处理前进和后退按钮的导航。

    2. 基于hash的路由

      • 特点

        • 使用URL中的#字符来表示不同的路由,例如http://example.com/#/products/123

        • 兼容性较好,因为不涉及HTML5的history API。

        • 可以用于构建单页应用,但通常不适用于服务端渲染。

12.什么是强缓存和协商缓存?(2)

强缓存(强制缓存)和协商缓存是Web开发中用于提高性能的两种重要缓存策略。

  1. 强缓存(Strong Cache):

    • 强缓存是一种缓存策略,它依赖于HTTP响应头中的缓存控制信息,用于在浏览器缓存中存储并直接使用缓存的资源,而不需要发送请求到服务器。

    • 当浏览器发送请求时,服务器会在响应头中包含一些缓存控制信息,例如Cache-ControlExpires。这些信息告诉浏览器可以在一段时间内(过期时间)内直接使用缓存,而不需要重新请求服务器。如果缓存仍然有效(未过期),浏览器将使用缓存,而不发送请求。

  2. 协商缓存(Conditional Cache):

  • 协商缓存是一种缓存策略,它依赖于服务器和浏览器之间的通信,用于检查资源是否已经发生了变化。

  • 当浏览器发送请求时,服务器会检查请求头中的一些条件,,这些条件是之前请求资源时服务器生成的标识。如果服务器发现资源没有发生变化,它会返回一个`304 的响应,而不返回实际资源数据。浏览器可以使用本地缓存,而无需重新下载资源。

13.iframe有那些缺点?

虽然iframe是一种非常有用的Web开发技术,但它也有一些缺点:

  1. 页面性能问题:使用iframe会导致页面加载速度变慢,因为每个iframe都需要额外的HTTP请求和资源下载。对于大量的iframe,这些额外的网络请求可能会对页面性能产生显著影响。

  2. SEO问题:搜索引擎爬虫无法识别iframe中的内容。如果网站依赖于SEO流量,使用iframe可能会降低其可见性和排名。

  3. 安全性问题:由于iframe可以嵌入来自其他域的内容,所以它可能会带来安全风险,如跨站点脚本攻击(XSS)。在使用iframe时,应该谨慎考虑安全问题,并尽可能限制来自其他域的内容。

  4. 维护问题:在使用iframe时,需要注意内部和外部文档之间的通信问题。如果内部文档需要与外部文档进行通信,需要考虑一些复杂的维护问题,如跨域通信、事件处理等。

  5. 移动设备适配问题:移动设备上的浏览器对iframe的支持不如桌面浏览器。在移动端使用iframe容易出现布局问题和兼容性问题。

综上所述,虽然iframe在某些情况下非常有用,但在使用时需要谨慎考虑其性能、SEO、安全性、维护和移动设备适配等问题,并尽可能采用其他替代方案来避免这些问题。

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

@reduxjs/toolkit是一个Redux官方推荐的工具集,它的主要目的是简化和加速Redux开发流程,提供了一组工具和约定,以减少样板代码和提高开发效率。下面是我对@reduxjs/toolkit的理解以及它与react-redux之间的区别:

区别

  • @reduxjs/toolkit是Redux的辅助工具集,用于简化和加速Redux开发,但它仍然是Redux的一部分。它并不是独立于Redux的状态管理库。

  • react-redux是React应用程序中与Redux集成的库,它提供了React组件和Redux store之间的连接机制,允许你将Redux store中的状态传递给React组件,以及将Redux action派发给Redux store。react-redux是与React紧密集成的,而@reduxjs/toolkit与Redux本身更相关。

  • @reduxjs/toolkit通常用于简化Redux的配置和开发过程,而react-redux用于在React应用中连接React组件与Redux store。这两者通常一起使用,但它们有不同的目的和关注点。

15.window.onload和$(document).ready?

window.onload

  • window.onload 是原生JavaScript事件,当整个页面及其所有资源(如图像、CSS文件等)都完全加载和渲染时触发。这意味着在window.onload触发之前,页面上的所有元素和资源都已准备就绪。

  • window.onload 只能添加一个事件处理程序,意味着如果多个脚本尝试使用window.onload,只有最后一个事件处理程序会生效。

  • 用法示例:

  • window.onload = function() { // 在整个页面和资源都加载完毕后执行的代码 };

  • $(document).ready()

    • $(document).ready() 是jQuery库提供的一种事件,它在DOM树构建完毕并且可以安全地访问和操作DOM元素时触发,而无需等待所有资源的加载完成。

    • $(document).ready() 可以添加多个事件处理程序,这使得多个脚本可以安全地并行运行,而不需要担心执行顺序。

    • 用法示例:

    • $(document).ready(function() { // 在DOM准备就绪后执行的代码 });

    • $(document).ready()window.onload是两种不同的事件,你可以根据需求来选择使用哪一个。在现代Web开发中,$(document).ready()` 更常见,因为它更灵活,并且通常能满足大多数情况下的需求。

16.React性能优化的手段有哪些?(3)

  1. 使用合成事件:React的合成事件系统能够提高性能,因为它使用事件委托,将事件监听器挂载在顶层容器上,而不是每个DOM元素上。减少了事件监听器的数量,从而减小了内存和性能开销。

  2. 使用组件级别的shouldComponentUpdate或React.memo:通过shouldComponentUpdate方法或React.memo函数,可以控制组件的更新过程。这允许你避免不必要的重新渲染,只有在状态或属性发生实际变化时才重新渲染组件。

  3. 使用React的虚拟DOM: React使用虚拟DOM来比较前后两个状态树,从而最小化对实际DOM的操作。减少了DOM操作的次数,提高了性能。

  4. 使用列表和键(Keys):在渲染列表时,使用唯一的键来标识每个列表项,以帮助React识别添加、删除或重新排序的操作。有助于React更有效地管理列表的更新。

  5. 避免深层次的嵌套:过多的嵌套组件可能会导致性能问题。尽量保持组件层次扁平,只嵌套必要的组件。

  6. 使用React的Context API: Context API允许跨组件层次传递数据,而不必一层层地通过属性传递。有助于避免不必要的属性传递,提高了代码的可维护性。

  7. 使用懒加载和代码拆分:将应用程序代码拆分成小块,按需加载,以减小初始加载时间。

  8. 使用生命周期方法或副作用钩子:合理使用componentDidMountcomponentDidUpdatecomponentWillUnmount等生命周期方法,以及useEffect等副作用钩子,以执行异步操作、订阅和取消订阅等操作。

  9. 使用生产模式:在生产环境中,确保使用React的生产模式版本,以去除开发模式下的额外检查和警告,提高性能。

  10. 性能监测和分析工具:使用性能监测工具,来识别和解决性能问题。this.props、this.state、this.forceUpdate、this.refs等方法,会引起组件的重渲染。

17.如何通过原生js实现一个节流函数和防抖函数?

1. 节流函数(Throttle)

节流函数限制一个函数在一定时间间隔内只能执行一次。以下是一个原生JavaScript的节流函数的实现示例:

 

function throttle(func, delay) {
  let lastTime = 0;
  return function() {
    const now = Date.now();
    if (now - lastTime >= delay) {
      func.apply(this, arguments);
      lastTime = now;
    }
  };
}

使用方法:

 

function handleThrottle() {
  console.log("Throttle function is called");
}
const throttledFunc = throttle(handleThrottle, 1000);
// 在需要的地方调用 throttledFunc

2. 防抖函数(Debounce)

防抖函数会等待一段时间后,执行一次函数。如果在等待时间内再次触发函数,等待时间会重新计时。以下是一个原生JavaScript的防抖函数的实现示例:

 

 
function debounce(func, delay) {
  let timer;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, arguments);
    }, delay);
  };
}
使用方法:
function handleDebounce() {
  console.log("Debounce function is called");
}
const debouncedFunc = debounce(handleDebounce, 1000);
// 在需要的地方调用 debouncedFunc

这两个函数可以根据需要在事件处理程序、滚动事件、输入框输入等情况下使用,以减少频繁触发函数的次数,提高性能。根据实际需求,你可以调整节流和防抖的时间间隔来满足特定的场景要求。

18.说说webpack中常见的loader?解决了什么问题?(2)

  1. 在Webpack中,Loader是用于处理各种文件类型的模块加载器,它们用于对文件进行转换、处理和加载。常见的Loader解决了以下问题:

    1. 处理 JavaScript 文件:Babel Loader用于将最新的JavaScript语法转译为浏览器兼容的版本,以解决不同浏览器之间的兼容性问题。它还可用于将TypeScript、Flow等其他JavaScript超集转译成标准JavaScript。

    2. 处理样式文件:CSS Loader和Style Loader用于加载和处理CSS文件,使其能够被应用到页面中,同时可以进行模块化和处理Sass、Less等预处理器。PostCSS Loader可用于自动添加浏览器前缀,优化CSS。

    3. 处理图片和媒体文件:File Loader和url-loader用于加载并处理图像、字体、音频、视频等文件,将它们复制到构建目录或将小文件编码为Data URI,以减少HTTP请求次数,提高性能。

    4. 处理HTML文件:HTML Loader用于加载和处理HTML文件,允许在HTML中引用资源,同时可以进行压缩和优化。

    5. 处理数据文件:CSV Loader、JSON Loader和 XML Loader用于加载和处理数据文件,以便在代码中引入数据。

    6. 处理模板文件:Handlebars Loader、EJS Loader等用于加载和处理模板文件,将它们编译为可在应用中使用的函数,以实现动态生成HTML。

    7. 处理字体文件:Font Awesome Loader等用于加载和处理字体文件,以便在应用中使用自定义图标。

    8. 处理静态资源引用:Copy Webpack Plugin用于复制静态文件,Html Webpack Plugin用于自动将JavaScript文件引用插入HTML文件。

    9. 处理ES6+模块:imports-loader和exports-loader用于处理CommonJS、AMD和其他模块系统,以便在Webpack中引入和使用。

    10. 代码分析和Linting:ESLint Loader用于集成ESLint代码质量检查,提供代码规范检查和自动修复功能。

    这些Loader解决了Webpack构建过程中不同类型文件的处理和转换问题,使得开发人员可以更轻松地处理文件加载、转译、压缩、模块化等任务,同时提高应用的性能和可维护性。根据项目需求,你可以根据需要配置和使用适当的Loader。

19.说说如何借助webpack来优化前端性能?(2)

  1. 代码分割:使用Webpack的代码分割功能,将应用程序代码拆分成多个块(chunks)。这可以减小初始加载时间,因为浏览器只需要下载当前页面所需的代码块,而不是整个应用。

  2. 懒加载:结合代码分割,使用动态导入(Dynamic Imports)来按需加载模块。这可以减少初始页面加载时需要下载的代码量,从而提高加载速度。

  3. 压缩和混淆:Webpack可以配置不仅压缩JavaScript、CSS和HTML文件,还可以混淆变量名,以减小文件大小。使用插件如TerserPlugincss-minimizer-webpack-plugin来实现这些优化。

  4. 文件哈希和缓存:为生成的文件添加哈希值,以便在文件内容变化时强制浏览器重新下载文件。这有助于充分利用浏览器缓存,减少不必要的网络请求。

  5. 图片优化:Webpack可以集成图片压缩工具,如image-webpack-loader,以减小图片文件的大小。另外,使用适当的图片格式(如WebP)以及响应式图片技术,可以提高性能。

  6. Tree Shaking:使用Webpack的Tree Shaking功能,消除未使用的JavaScript代码,以减小文件大小。确保使用ES6模块(import/export)以便Webpack能够正确进行Tree Shaking。

  7. 分离样式:将CSS从JavaScript分离出来,以允许浏览器并行下载样式和脚本,从而提高加载性能。使用mini-css-extract-plugin插件来实现。

  8. 使用缓存:借助Webpack的持久缓存特性,确保每个生成文件都有唯一的哈希值,以便浏览器可以缓存它们并在需要时更新。

  9. HTTP/2和多入口点:如果你的服务器支持HTTP/2,可以利用多入口点的Webpack配置来并行加载多个资源,以提高加载速度。

  10. 服务端渲染(SSR):在某些情况下,使用服务端渲染可以显著提高性能,因为它可以减少客户端渲染的工作负担。

  11. CDN和静态资源托管:使用CDN(内容分发网络)来加速静态资源的传递。将静态资源托管在高性能的CDN上,可以减少加载时间。

  12. 监控和性能分析:使用Webpack插件和工具,如webpack-bundle-analyzer,来分析和监控构建结果,以查找潜在的性能问题。

20.重绘和回流(重排)是什么,如何避免?

重绘和回流是与网页性能和渲染相关的两个概念

重绘指的是当元素样式改变,但不影响其布局的情况下,浏览器重新绘制该元素的过程。例如,改变元素的背景颜色或字体颜色时会触发重绘。

回流指的是当页面布局发生变化导致需要重新计算元素的几何属性(如位置、大小等)并重新布局时的过程。例如,改变元素的宽度、高度、边距等会触发回流。

重绘和回流是耗费性能的操作,因为它们涉及到重新计算元素的样式和布局。大量的重绘和回流会降低网页的性能,导致页面卡顿和加载缓慢。

为了避免频繁的重绘和回流,可以采取以下一些优化措施:

  1. 使用 CSS3 的 transform 和 opacity 属性:这些属性可以使用 GPU 加速,减少重绘和回流的开销。

  2. 避免频繁地改变样式:如果需要对多个样式进行修改,尽量通过添加或删除 CSS 类来实现,而不是直接修改元素的样式。

  3. 在 DOM 元素之外进行操作:将要插入或修改的元素先脱离文档流,进行处理后再插入回文档流,这样可以减少回流次数。

  4. 批量更新样式:如果需要对多个元素进行样式修改,可以将这些修改操作放在一个 JavaScript 函数中,将多次修改合并为一次,减少重绘和回流的次数。

  5. 使用 documentFragment 进行批量 DOM 操作:将需要插入到页面中的元素先添加到 documentFragment 中,然后再一次性插入到页面中,避免频繁的回流。

  6. 使用 CSS3 动画代替 JavaScript 操作:CSS3 动画可以使用 GPU 加速,比使用 JavaScript 实现的动画效果更高效。

总的来说,避免频繁的修改样式和布局,尽量进行批量处理和合并操作,可以有效减少重绘和回流带来的性能问题,提升页面的渲染性能。

21.React 和 Vue 在技术层面有哪些区别?

React 和 Vue 是两个非常流行的前端框架,它们在技术层面有以下几点区别:

  1. 数据驱动方式不同:React 的数据驱动是单向的,即从父组件向子组件传递数据,子组件不能直接修改父组件的数据。Vue 的数据驱动则是双向的,即可以从父组件向子组件传递数据,也可以从子组件向父组件传递数据。

  2. 组件化实现方式不同:React 使用 JSX 语法编写组件,将 HTML 和 JavaScript 在代码层面融合在一起,组件之间的通信通过 props 和回调函数实现。Vue 则使用模板语法编写组件,将 HTML 和 JavaScript 分离开来,组件之间的通信通过 prop 和自定义事件实现。

  3. 运行时机制不同:React 使用虚拟 DOM 技术,在数据变化时生成新的虚拟 DOM 树与旧的虚拟 DOM 树进行比较,找出发生变化的节点进行更新。Vue 使用响应式系统,在数据变化时自动触发视图更新。

  4. 扩展性不同:React 提供了强大的扩展能力,可以结合其他库和框架进行使用,如 Redux、MobX 等。Vue 相对而言扩展性不太好,需要对整个框架进行定制化开发。

  5. 社区和生态环境不同:React 的社区比较大,拥有相对完善的生态环境,可以很容易地找到开源组件和工具。Vue 的社区虽然不如 React 大,但是也在不断发展壮大,并且 Vue 本身提供了许多常用的功能和组件。

这些是 React 和 Vue 在技术层面上的一些主要区别,但两者都是优秀的前端框架,都有自己的优点和适用场景。选择框架时,应根据实际需求和团队技术栈进行选择。

22.React 中的 ref 有什么用,如果获取子组件中的方法?

ref的作用:

React 的 ref 属性用于获取组件或 DOM 节点的引用,通过它可以在组件外部访问组件的属性和方法,或者直接操作 DOM 节点。

获取子组件中的方法可以这么做:

1.父组件中创建一个 childRef 对象,并将其作为 ref 属性传递给 ChildComponent

2.父组件中的 handleButtonClick 方法会调用 ChildComponent 中的 changeText 方法

3.将 数据 作为参数传递过去,从而改变 ChildComponent 的显示文本

23.单页应用如何提高加载速度?

提高单页应用(Single Page Application,SPA)的加载速度可以从以下几个方面入手:

  1. 代码优化:压缩和精简 JavaScript、CSS 和 HTML 代码,减少文件大小,通过使用工具如Webpack对代码进行打包和优化。同时,删除未使用的代码和依赖库,以减少加载和执行时间。

  2. 懒加载:将页面划分为多个模块或组件,只在需要时才进行加载和渲染。这样可以减少初始加载时间,并在用户需要时进行按需加载。

  3. 路由懒加载:对于 SPA 的路由,可以实现按需加载,即在用户导航到相关页面时再加载该页面的资源,而不是一次性加载所有页面的资源。

  4. 图片优化:缩小图片尺寸、压缩图片文件大小,采用适当的图片格式(如WebP),使用懒加载技术加载图片,或者使用 CSS Sprites 技术将多个图片合并成一个请求。

  5. 缓存策略:合理设置缓存策略,利用浏览器缓存来减少网络请求。可以使用 HTTP 头中的 Cache-Control 和 ETag 等机制来控制缓存行为。

  6. 代码分割:将代码分割成多个小块,按需加载。可以使用动态导入(dynamic import)、Webpack 的代码分割功能或者使用第三方库(如Loadable Components)来实现。

    以上是一些常见的优化方式,根据具体情况可以选择适合自己项目的优化策略。需要注意的是,在进行性能优化时,最好结合性能测试工具进行测试和评估,以确定优化效果和影响。

24.说说javascript内存泄漏的几种情况?(2)

  1. JavaScript内存泄漏是指应用程序中的内存不再被使用,但没有被正确释放,导致内存占用不断增加,最终可能导致应用程序性能下降或崩溃。

    1. 未被释放的引用:当一个对象仍然存在对其他对象的引用,即使你不再需要这个对象,它也不会被垃圾回收。这种情况通常发生在事件处理程序、闭包或全局变量中。

    2. 定时器未清理:如果你创建了定时器(setTimeoutsetInterval)但没有清理它们,它们会一直运行,即使不再需要。这会导致函数和相关数据一直存在于内存中。

    3. DOM引用未清理:在JavaScript中,如果你保留对DOM元素的引用,而这些元素被删除或替换,那么对这些引用的引用仍然存在于内存中,导致内存泄漏。

    4. 循环引用:循环引用是指两个或多个对象相互引用,而没有其他引用指向它们,垃圾回收器无法检测到它们不再被使用。这通常发生在对象之间的互相引用,比如父子关系或闭包。

    5. 内存泄漏工具不足:有时,内存泄漏可能是由于工具或框架本身的问题,例如浏览器或第三方库的内存泄漏。

    解决JavaScript内存泄漏的方法包括及时释放不再需要的引用、手动清理定时器和事件处理程序、避免循环引用、使用内存泄漏检测工具和定期检查和分析内存占用。

25.怎么实现图片懒加载,要求写一下思路?

实现图片懒加载可以按照以下思路进行:

  1. 将默认的图片占位符(placeholder)设置给 <img> 标签的 src 属性,这个占位符图片应该是一个很小的、可快速加载的图像。例如,可以使用一个灰色的小方块或者加载中的动画图标。

  2. 将要延迟加载的图片地址保存到自定义的属性中,比如 data-src

  3. 监听页面滚动事件,当用户滚动到需要加载图片的位置时触发加载。

  4. 在滚动事件触发时,遍历所有具有自定义 data-src 属性的 <img> 标签,检查它们是否出现在可视区域内。

  5. 如果图片出现在可视区域内,将 data-src 属性的值赋给 src 属性,触发图片的加载。并且移除 data-src 属性,以免重复加载。

  6. 结合防抖和节流操作,减少滚动事件频繁触发,优化性能。例如,使用 setTimeoutrequestAnimationFrame 来控制滚动事件的触发频率。

26.原生js如何实现上拉加载下拉刷新?

要实现上拉加载和下拉刷新的功能,可以使用原生 JavaScript 结合浏览器的滚动事件和触摸事件来实现。下面是一个简单的示例代码:

javascriptCopy Code// 上拉加载
function handleLoadMore() {
  // 在此处编写处理上拉加载的逻辑
  // 当页面滚动到底部时,触发加载更多数据的操作
}
// 下拉刷新
function handleRefresh() {
  // 在此处编写处理下拉刷新的逻辑
  // 当用户下拉到一定程度时,触发刷新数据的操作
}
// 监听滚动事件
window.addEventListener('scroll', () => {
  const scrollHeight = document.documentElement.scrollHeight; // 页面总高度
  const scrollTop = document.documentElement.scrollTop; // 滚动条距离顶部的高度
  const clientHeight = document.documentElement.clientHeight; // 可视区域高度
  if (scrollTop + clientHeight >= scrollHeight) {
    handleLoadMore(); // 到达页面底部,执行上拉加载
  }
});
// 监听触摸事件
let startY; // 记录触摸起始位置
window.addEventListener('touchstart', (e) => {
  startY = e.touches[0].pageY; // 记录触摸开始位置
});
window.addEventListener('touchmove', (e) => {
  const moveY = e.touches[0].pageY; // 当前触摸位置与起始位置的偏移量
  const distance = moveY - startY; // 移动距离
  if (distance > 0 && document.documentElement.scrollTop === 0) {
    e.preventDefault(); // 阻止页面滚动
    handleRefresh(); // 下拉到一定程度,执行下拉刷新
  }
});

27.说说你对 useReducer 的理解?

理解:

1.useReducer 是 React 提供的一个 Hook,用于实现状态管理和状态更新,它的作用类似于类组件中的 this.setState 方法。通过 useReducer,可以将组件的状态逻辑抽离出来,以更清晰、可维护的方式管理组件的状态。

2.使用 useReducer 需要提供两个参数:reducer 函数和初始状态。其中,reducer 函数接收当前状态和动作(action),并返回新的 状态。

3.useReducer 返回的数组包含当前状态和 dispatch 方法,通过解构赋值可以分别获取它们

useReducer的好处:

1.当状态逻辑较复杂时,可以将其拆分为多个 case,每个 case 只关注自己的逻辑,使代码更易于理解和维护。

2.useReducer 还可以结合 Context API 实现全局状态管理,或者与其他 Hook 如 useEffectuseContext 等搭配使用,实现更强大的功能。

28.谈谈你对BFC的理解?

  1. BFC,或者块级格式上下文(Block Formatting Context),是CSS中的一个重要概念,用于控制和规范块级元素在布局中的表现。理解BFC对于解决布局问题和处理元素之间的相互影响非常有帮助。

    BFC是一种重要的CSS布局概念,用于控制块级元素的布局和相互关系。了解BFC如何创建以及它的特性和作用可以帮助开发者更好地控制页面布局,解决一些常见的布局问题。

    1. 创建条件:BFC是在以下情况下创建的:

      • 根元素(HTML根元素)。

      • 浮动元素(float属性不为none)。

      • 绝对定位元素(position属性为absolutefixed)。

      • 行内块元素(display属性为inline-block)。

      • 表格单元格元素(display属性为table-cell)。

      • overflow属性不为visible的元素。

    2. 特性和作用:BFC具有以下特性和作用:

      • 内部的块级盒子会在垂直方向上一个接一个地排列。

      • BFC内部的元素不会与浮动元素重叠。

      • BFC可以包含浮动元素,将浮动元素的边界框包含在内。

      • BFC内部的元素会忽略浮动元素,不会与浮动元素重叠。

      • BFC内部的元素不会溢出其容器,它们会在容器内重新布局,不会影响外部元素的位置。

    3. 应用场景:BFC的理解对于解决以下一些常见的布局问题非常有用:

      • 清除浮动:将包含浮动元素的父元素创建为BFC,以清除浮动。

      • 防止外边距合并:在两个垂直外边距发生合并时,将一个元素包含在BFC中,以防止合并。

      • 自适应两栏布局:实现两栏布局时,将左侧栏创建为BFC,以防止右侧栏覆盖。

      • 防止元素溢出:将包含溢出元素的容器创建为BFC,以确保不会影响其他元素的布局。

29.我们应该在什么场景下使用 useMemo 和 useCallback?

useMemouseCallback 都是 React 提供的 Hook,它们都可以用于优化函数组件的性能。

useMemo 的作用是对函数的计算结果进行缓存,只有在依赖项发生变化时才会重新计算

当一个组件中渲染内容需要根据一些复杂的计算结果得出,这个计算过程可能会非常耗时,就可以考虑使用 useMemo 来缓存计算结果,避免不必要的重复计算,并且能够有效地提高渲染性能。

useCallback 的作用是对函数进行缓存,只有依赖项发生变化时才会创建新的函数。

当一个组件中传递给子组件的回调函数需要大量计算,或者是传递给子组件的回调函数会引起子组件的重复渲染时,就可以使用 useCallback 来缓存回调函数。

30.为什么 useState 返回的是数组而不是对象?

在 React 中,useState 是一个用于在函数组件中声明状态的钩子函数。它的返回值是一个长度固定为 2 的数组,而不是一个对象,这是由设计选择所决定的。

使用数组来表示状态,是因为它具有以下优势:

  1. 简单直观:将状态以数组的形式进行管理,可以更容易地理解和使用。

  2. 顺序保持一致:使用 useState 声明多个状态时,它们的顺序和声明的顺序是完全一致的。你可以根据索引访问和更新每个状态,而不需要命名或记住状态的特定名称。

  3. 无需每次指定键:当更新状态时,不需要像使用对象那样指定键。只要调用 useState 返回的第二个元素(通常命名为 setXXX)即可。

31.虚拟DOM一定更快吗?

虚拟 DOM 本身并不一定比直接操作真实 DOM 更快。它的性能优势主要体现在以下几个方面:

  1. 高效的批量更新:虚拟 DOM 可以将多次状态变更批量处理,并通过算法进行优化,最终只更新真实 DOM 中需要变更的部分。这种批量更新的方式可以有效减少对真实 DOM 的频繁操作,提高性能。

  2. 减少重绘和回流:虚拟 DOM 会通过 diff 算法,比较新旧虚拟 DOM 树的差异,并最小化对真实 DOM 的修改。通过最小化修改操作,可以减少浏览器的重绘(repaint)和回流(reflow)操作,从而提升性能。

  3. 跨平台能力:虚拟 DOM 是一个抽象层,可以使得 React 这样的框架同时运行在多个平台上,如浏览器、移动端等。它可以将用户界面渲染为虚拟 DOM,然后再根据具体平台的特性将其转化为真实的 UI。

尽管虚拟 DOM 可以提供性能上的优势,但并不意味着它一定比直接操作真实 DOM 更快。在某些简单场景下,直接操作真实 DOM 可能更加高效。而且虚拟 DOM 本身也会引入一定的开销,比如虚拟 DOM 的生成和 diff 算法的计算等。

32.React中,能否直接将 props 的值复制给 state?

在 React 中,可以把 props 直接赋值给 state,但这样不一定是一个好的做法,因为 propsstate 有不同的含义和用途。

props 表示从父组件传递给子组件的属性,主要用于组件之间传递数据。而 state 则表示组件自身的状态,主要用于保存交互状态或控制组件行为。

虽然 propsstate 的数据形式相同,但它们存在本质的区别:

  1. props 是从父组件传递下来的,子组件不能修改。而 state 是组件自己管理的,可以通过 setState() 方法进行修改。

  2. props 可以用于组件之间传递数据,而 state 主要用于保存组件内部的交互状态。

因此,直接将 props 复制给 state 忽略了组件自身状态的更新和控制,容易导致数据不一致或不可控的情况。在一些特殊场景下,可以将 props 赋值给 state,但需要认真考虑并仔细分析其影响和后果。

33.什么是高阶组件?

高阶组件(Higher-Order Component,简称 HOC)是一个函数,它接收一个组件作为参数,并返回一个新的组件。这个新的组件可以对原组件进行扩展或增强,例如添加一些通用的逻辑、状态、行为等。

高阶组件本质上是一个装饰器模式,通过把通用逻辑从组件中抽离出来,实现代码复用和组件的解耦。高阶组件可以用于很多场景,例如添加权限控制、添加数据逻辑、动态渲染等等。

在 React 中,高阶组件主要通过以下方式实现:

  1. 属性代理:通过 props 将新的属性传递给被包裹的组件。

  2. 反向继承:通过继承被包裹的组件,可以覆盖或增加它的生命周期方法和渲染函数。

使用高阶组件可以有效避免代码冗余和逻辑耦合,提高代码的可维护性和重用性。但需要注意,过度使用高阶组件可能会导致组件层级过深、代码难以理解等问题,因此需要根据具体场景进行评估和使用。

34. React.PureComponent 和 React.Component 有什么区别?

1.React.PureComponentReact.Component会浅比较组件的 props 和 state 的变化,如果相同则不会重新渲染组件。而 React.Component 每次更新都会重新渲染组件。

2.React.PureComponent 会帮助我们优化组件性能,避免不必要的组件重渲染,从而提升应用的性能和用户体验

3.React.PureComponent 的浅比较只检查对象引用是否相等,不会检查对象内部属性的变化,如果使用不当可能会导致错误或不可预期的结果。

4.React.PureComponent,因为每次渲染都会生成新的对象,无法享受到性能优化的好处。

目录

1.清除浮动的几种方式,各自的优缺点,推荐使用哪种?

2.Sass、LESS区别是什么?大家为什么要使用他们?

3.说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?(2)

4.说说React中setState执行机制?

5.如果需要手动写动画,你认为最小时间间隔是多久,为什么?

6.React组件之间如何通信?(4)

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

8.说说你对fiber架构的理解?解决了什么问题?(2)

9.说说react diff的原理是什么?(2)

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

11.路由原理 history 和 hash 两种路由方式的特点?

12.什么是强缓存和协商缓存?(2)

13.iframe有那些缺点?

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

15.window.onload和$(document).ready?

16.React性能优化的手段有哪些?(3)

17.如何通过原生js实现一个节流函数和防抖函数?

18.说说webpack中常见的loader?解决了什么问题?(2)

19.说说如何借助webpack来优化前端性能?(2)

20.重绘和回流(重排)是什么,如何避免?

21.React 和 Vue 在技术层面有哪些区别?

22.React 中的 ref 有什么用,如果获取子组件中的方法?

23.单页应用如何提高加载速度?

24.说说javascript内存泄漏的几种情况?(2)

25.怎么实现图片懒加载,要求写一下思路?

26.原生js如何实现上拉加载下拉刷新?

27.说说你对 useReducer 的理解?

28.谈谈你对BFC的理解?

29.我们应该在什么场景下使用 useMemo 和 useCallback?

32.为什么 useState 返回的是数组而不是对象?

33.虚拟DOM一定更快吗?

34.React中,能否直接将 props 的值复制给 state?

35.什么是高阶组件?

36 React.PureComponent 和 React.Component 有什么区别?

39.props和state相同点和不同点?render方法在哪些情况下会执行?

40.shouldComponentUpdate有什么作用?

41.说说React中的虚拟dom?在虚拟dom计算的时候diff和key之间有什么关系?

42.react新出来两个钩子函数是什么?和删掉的will系列有什么区别?

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

44.谈谈你对immutable.js的理解?

45.redux本来是同步的,为什么它能执行异步代码?实现原理是什么?中间件的实现原理是什么?

46.redux中同步action与异步action最大的区别是什么?

47.redux-saga和redux-thunk的区别与使用场景?

48.在使用redux过程中,如何防止定义的action-type的常量重复?

49.CDN的特点及意义?

50.为什么for循环比forEach性能高?

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

52.![] == ![],![] == [],结果是什么?为什么?

53.什么是闭包,应用场景是什么?

54.谈谈你是如何做移动端适配的?

55.移动端1像素的解决方案?

56.弹性盒中的缩放机制是怎样的?

57.说说你对useEffect的理解,可以模拟哪些生命周期?

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

59.说说React中setState和replaceState的区别?

60.说说react中onClick绑定后的工作原理?

61.Redux的实现原理,写出其核心实现代码?

62.什么是垂直外边距合并?说说合并后的几种情况?

63.useEffect的依赖为引用类型如何处理?

64.知道react里面的createPortal么,说说其使用场景?

65.Provider和connect的底层原理实现,写出其核心代码?


35.props和state相同点和不同点?render方法在哪些情况下会执行?

Props(属性)和 State(状态)是 React 组件中用于管理数据的两个概念。

相同点:

  1. 它们都是用于存储和管理组件数据的。

  2. 当它们的值发生变化时,都可以触发组件重新渲染。

不同点:

  1. Source(数据来源):Props 是从父组件传递给子组件的,而 State 则是在组件内部定义和管理的。

  2. Mutability(可变性):Props 是只读的,无法在子组件内部直接修改,只能通过父组件重新传递新的 Props。而 State 可以在组件内部进行修改和更新。

  3. 更新方式:当 Props 发生变化时,React 会自动进行组件的重新渲染。而 State 的更新需要调用组件的 setState 方法来触发重新渲染。

关于 render 方法的执行情况:

  1. 初始渲染:组件首次被渲染时,render 方法会被调用,并生成对应的虚拟 DOM 树。

  2. 数据变化:当组件的 Props 或 State 发生变化时,React 会重新调用 render 方法来更新虚拟 DOM,并与之前的虚拟 DOM 进行对比。

  3. 强制更新:通过调用组件的 forceUpdate 方法可以强制触发 render 方法的执行,即使 Props 和 State 没有发生变化。

  4. 父组件更新:如果父组件进行重新渲染,子组件的 render 方法也会被调用。

需要注意的是,只有在 render 方法中返回的虚拟 DOM 与之前的虚拟 DOM 不同,React 才会重新渲染真实 DOM,并更新到页面上。

36.shouldComponentUpdate有什么作用?

shouldComponentUpdate 是 React 组件的生命周期函数之一,用于控制组件是否需要重新渲染。

当组件的 Props 或 State 发生变化时,React 会自动触发组件的重新渲染,但是在渲染之前,React 会调用 shouldComponentUpdate 方法来判断是否需要重新渲染。如果 shouldComponentUpdate 返回 true,则组件将继续进行重新渲染;如果返回 false,则组件将停止重新渲染,直接使用之前的结果。

shouldComponentUpdate 的作用主要有以下两个方面:

  1. 性能优化:在某些情况下,组件可能会因为不必要的重新渲染而浪费大量性能。比如,在组件的某个 Prop 值没有发生变化时,我们可以通过重写 shouldComponentUpdate 方法来告诉 React 直接复用上次的渲染结果,从而避免不必要的计算和渲染,提升应用性能。

  2. 控制组件渲染的粒度:有些组件可能包含多个子组件,当 Props 或 State 发生变化时,所有子组件都会被重新渲染,即使对于某些子组件来说,数据并没有发生变化。此时,我们可以通过在父组件中重写 shouldComponentUpdate 来手动控制子组件的重新渲染,从而优化渲染性能和用户体验。

需要注意的是,shouldComponentUpdate 每次在重新渲染之前都会被调用,因此返回 false 可以有效地防止组件重复渲染。

37.说说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 的益处和代价,选择最适合自己应用的方案。

38.react新出来两个钩子函数是什么?和删掉的will系列有什么区别?

React 新增的两个钩子函数是 useEffectuseLayoutEffect

useEffect 用于处理组件中的副作用,它在渲染结束后执行一次,并且可以通过参数控制何时重新执行。

useEffect 接收两个参数:第一个参数是回调函数,在组件渲染后执行副作用逻辑;第二个参数是一个数组,用于指定依赖项,当依赖项变化时才会重新执行副作用逻辑。

useEffectcomponentDidMountcomponentDidUpdatecomponentWillUnmount 等方法类似,但是与这些生命周期函数不同的是,useEffect 中的回调函数是异步执行的,也就是说,它可能会在页面渲染完成之后执行。因此,我们需要注意在回调函数中访问组件的 Props、State 或 Ref 等变量时,要确保它们的值是最新的。

useLayoutEffect 的作用和 useEffect 类似,但是它的执行时机不同。useLayoutEffect 在渲染后、浏览器执行绘制之前执行,因此可以在这个回调函数中访问布局信息,并修改元素的样式等。需要注意的是,由于 useLayoutEffect 是同步执行的,因此如果执行的逻辑比较耗时,可能会影响页面的性能,应该谨慎使用。

相比于被删除的 will 系列生命周期函数,useEffectuseLayoutEffect 是基于钩子函数的,而不是基于类的生命周期函数。它们是 React Hooks 的一部分,旨在帮助开发者更加方便地编写组件逻辑和处理副作用。与以前的生命周期函数相比,Hooks 更加经济高效,使代码更加简洁易懂,并且更容易实现复用。

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

如果你的props.children包含单个React元素或组件,map函数将无法直接使用,因为它期望一个数组来进行映射操作。如果你只有一个子元素,你可以将其包装在一个数组中,然后使用map函数遍历

另一种常见情况是props.children包含多个子元素,每个子元素都有自己的类型。在这种情况下,你可以使用React.Children.map来处理props.children,它会为你处理各种类型的子元素

使用React.Children.map可以避免一些潜在的异常,因为它会处理各种类型的子元素,包括字符串、数字和React元素。如果props.children包含非React元素,直接使用map函数可能会导致异常。

40.谈谈你对immutable.js的理解?

Immutable.js 是 Facebook 开发的一个 JavaScript 库,用于处理不可变(Immutable)数据。它提供了一组用于创建、操作和使用不可变数据结构的 API。不可变数据是指一旦创建就无法被修改的数据,任何对不可变数据的修改都会返回一个新的不可变数据结构,而原始数据结构不会受到影响。

下面是我对 Immutable.js 的理解:

  1. 不可变性:Immutable.js 的核心思想是通过不可变性来管理数据。当我们对不可变数据进行修改时,实际上是创建了一个全新的数据结构,而不是修改原始数据。这样做的好处是可以避免副作用,使代码更加可靠和可预测。

  2. 持久性:Immutable.js 中的数据结构是持久化的,这意味着即使进行了修改,原始数据依然保持不变。这对于性能优化非常重要,因为在大规模数据集上进行修改时,可以复用原始数据的共享部分,而不需要完全复制整个数据结构。

  3. API:Immutable.js 提供了一组强大的 API,用于创建和操作不可变数据。这些 API 包括 List、Map、Set 等各种数据结构,以及一些函数式编程的方法(如 map、filter、reduce 等),用于对数据进行转换和处理。

  4. 并发安全:由于不可变数据是无法修改的,所以多个线程或并发操作可以安全地共享和访问不可变数据。这使得在并发环境下编写可靠和高效的代码变得更加容易。

  5. 性能考虑:尽管创建不可变数据结构可能会产生一些额外的开销,但 Immutable.js 通过使用持久化数据结构、结构共享和结构共享的引用透明性等技术来优化性能,并提供了一些针对特定场景的优化方法。

总的来说,Immutable.js 提供了一种管理不可变数据的方式,通过避免数据的变异和副作用,使代码更加可靠和可维护。它在大规模数据处理、函数式编程和并发操作等方面具有很大的优势。

需要注意的是,使用 Immutable.js 需要权衡数据的复杂度和性能要求,不是所有情况都适合使用不可变数据。在某些简单的场景下,使用原生 JavaScript 的数据结构也可以达到类似的效果。

41.redux本来是同步的,为什么它能执行异步代码?实现原理是什么?中间件的实现原理是什么?

Redux 是一个使用单一数据源和不可变数据的状态管理库,它本身是同步的,但通过中间件可以实现异步操作。Redux 中最常用的异步中间件是 Redux Thunk、Redux Saga 和 Redux Observable。

具体来说,当我们需要在 Redux 中执行异步代码时,通常会使用如下流程:

  1. 在 View 中触发一个异步 Action。

  2. Action 发送到 Reducer,Reducer 更新 Store 中的状态。

  3. 如果需要执行异步操作,Middleware 捕获到这个 Action,并执行异步代码。

  4. 异步代码完成后, Middleware 发送新的 Action 到 Reducer,Reducer 再次更新 Store 中的状态。

实现原理是中间件利用了 Redux 提供的 action 和 reducer 的单向数据流机制,使得 action 可以被拦截并且异步处理后再次派发出去。中间件本质上是对 dispatch 函数的重写,并且它可以执行某些操作,例如异步处理、日志记录、错误报告等。

中间件的原理是 Redux 通过在 dispatch 中间注入中间件的执行代码,在 action 到达 reducer 之前对 action 进行了修改或者是对应的 side effect 操作。具体来说,每个中间件都是一个函数,它接收 store 的 dispatch 和 getState 函数作为参数,返回一个新的函数,这个函数被用来包装 action creator,在 dispatch 前后进行操作。这种方式支持链式调用多个中间件,以便进行不同操作,例如数据处理、异步调用、日志记录、错误报告等。

42.redux中同步action与异步action最大的区别是什么?

Redux中的同步action和异步action的最大区别在于它们处理动作(actions)的方式和触发时间:

  1. 同步Action

    • 同步action是指立即执行的操作,不涉及任何异步操作。

    • 同步action的动作创建函数会立即返回一个包含动作类型和数据的action对象,通常使用dispatch函数触发。

    • 同步action用于处理立即执行的操作,例如用户点击按钮、输入框变化等。

    • 由于同步action是立即执行的,所以它们通常用于处理简单的状态变更,不需要等待异步操作的结果。

  2. 异步Action

    • 异步action是指涉及异步操作的动作,需要等待异步操作的结果,例如网络请求或定时任务。

    • 异步action的动作创建函数通常会返回一个函数,而不是一个包含动作对象的函数。

    • 异步action用于处理需要等待异步操作结果的情况

总之,同步action用于立即触发状态变更,而异步action用于处理需要等待异步操作结果的情况。

43.redux-saga和redux-thunk的区别与使用场景?

Redux Saga 和 Redux Thunk 都是用于处理异步操作的 Redux 中间件,它们在实现和使用上有一些区别,适用于不同的场景。

  1. 区别:

  • 实现方式: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 处理异常情况。

  1. 使用场景:

  • Redux Thunk 适用于简单的异步场景,例如发起一个 AJAX 请求并在请求成功后更新状态。它的学习曲线比较低,容易上手,适合于对异步处理需求不复杂的项目。

  • Redux Saga 适用于复杂的异步场景,例如需要处理多个连续的异步操作、任务取消、并发请求等。它提供了更强大和灵活的异步处理能力,并且通过 Generator 函数的形式使得异步流程易于阅读和维护。但是相对复杂性也较高,需要掌握 Generator 函数和 Saga 相关的代码结构。

总而言之,Redux Thunk 适用于简单的异步操作,学习曲线较低;而 Redux Saga 适用于复杂的异步场景,提供了更强大和灵活的异步处理能力。选择哪个中间件取决于项目的具体需求和开发团队的技术背景。

44.在使用redux过程中,如何防止定义的action-type的常量重复?

在 Redux 中,确保定义的 action type 常量不重复可以通过以下几种方式来实现:

  1. 使用单独的文件来存放 action type 常量:可以创建一个名为 actionTypes.jsconstants.js 的文件,在其中定义并导出所有的 action type 常量。这样做可以集中管理常量,减少重复定义的可能性。

 

 
javascriptCopy Code// actionTypes.js
export const ACTION_TYPE_1 = 'ACTION_TYPE_1';
export const ACTION_TYPE_2 = 'ACTION_TYPE_2';
// ...
  1. 使用命名空间或前缀来限定 action type:在定义 action type 常量时,给它们添加命名空间或前缀,以确保全局唯一性。

 

 
javascriptCopy Codeexport const NAMESPACE_ACTION_TYPE_1 = 'NAMESPACE/ACTION_TYPE_1';
export const NAMESPACE_ACTION_TYPE_2 = 'NAMESPACE/ACTION_TYPE_2';
// ...
  1. 使用工具库来生成唯一的 action type:可以使用第三方工具库(如uuid)来生成唯一的字符串作为 action type。这样可以防止手动定义的常量重复。

 

 
javascriptCopy Codeimport { v4 as uuidv4 } from 'uuid';
export const UNIQUE_ACTION_TYPE = uuidv4();

无论选择哪一种方式,重要的是在整个项目中保持一致性,避免重复定义相同的 action type 常量。这样做可以避免潜在的错误,并提高代码的可维护性。

45.CDN的特点及意义?

CDN(Content Delivery Network,内容分发网络)是一种分布式的网络架构,其目标是通过将内容缓存在离用户较近的边缘节点上,提供更快速、稳定的内容分发服务。CDN的特点和意义如下:

  1. 高效快速:CDN利用分布在全球各地的边缘节点,将内容缓存到离用户最近的节点上,当用户请求内容时,可以从最近的节点获取,减少了传输距离和网络拥堵,提供了更快的响应速度和较低的延迟。

  2. 负载均衡:CDN通过智能路由和负载均衡技术,将用户请求分配到最优的节点,避免了单一服务器的过载,提高了整体的系统吞吐量和可用性。

  3. 提升用户体验:由于CDN提供了更快的响应速度和较低的延迟,用户可以更快速地访问和加载网页、图片、视频等内容,提升了用户的体验感受,降低了用户的等待时间。

  4. 减轻源站压力:通过将静态内容缓存到CDN节点上,可以减轻源站的负载压力,提高源站的可扩展性和稳定性,同时减少了源站与用户之间的直接流量,降低了网络成本。

  5. 抵御网络攻击:CDN可以通过分布式的架构和安全防护机制,提供一定程度的网络攻击防护,如分布式拒绝服务攻击(DDoS)的缓解。

  6. 全球覆盖:CDN网络分布在全球各地的节点,可以实现全球范围内的内容分发,无论用户身处何地,都可以享受到较快速的网络访问。

综上所述,CDN具有高效快速、负载均衡、提升用户体验、减轻源站压力、抵御网络攻击和全球覆盖等特点。在大规模的网络应用中,使用CDN能够显著提升网站性能、可靠性和安全性,为用户提供更好的体验,并降低运营成本。

46.为什么for循环比forEach性能高?

在 JavaScript 中,常用的循环有 for 循环和 forEach 循环。虽然两者都可以遍历数组,但它们的实现方式不同,因此性能也有所不同。

for 循环是一种基于索引值(或下标)的循环方式,通过数组的下标索引来访问数组元素。而 forEach 循环则是一种迭代器,对数组中的每个元素都执行一次回调函数。

因此,for 循环相对于 forEach 循环具有以下优势:

  1. for 循环不需要编写额外的函数,可以直接对数组进行操作,因此其执行过程相对更加高效。

  2. 在 for 循环中,可以通过定义一个本地变量(如var len = arr.length)将数组长度缓存起来,避免多次访问 arr.length 属性导致的性能损失。

  3. 在需要对数组进行修改时,for 循环比 forEach 循环更为方便且高效。因为在 for 循环中,可以通过获取数组的下标索引来修改数组元素,而在 forEach 循环中无法直接修改数组元素。

总体而言,在大多数情况下,for 循环比 forEach 循环更具有优势。但是,对于需要进行异步操作的情况,forEach 循环可能更为适用,因为它可以支持 async/await 操作,而 for 循环不支持。

需要注意的是,虽然 for 循环比 forEach 循环更快,但在实际应用中,在性能方面的差别通常不会太大。因此,选择使用哪种循环方式应该根据实际情况而定,以符合代码的可读性、可维护性和执行效率等方面的要求。

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

render() 方法的原理:

根据组件的状态和属性(props)来生成对应的虚拟 DOM 树。React 使用虚拟 DOM 来表示组件的界面结构,并通过比较更新前后 的虚拟 DOM 树找出差异,然后将差异部分进行高效地更新到真实的 DOM 上,从而实现页面的局部更新

render() 方法会在以下情况下触发:

1.组件首次挂载:当组件第一次被渲染到真实的 DOM 上时,render() 方法会被调用

2.组件的状态或属性发生变化:当组件的状态(通过 setState() 方法更新)或属性发生变化时,React 会自动重新调用 render() 方法,生成新的虚拟 DOM,并进行比较和更新

3.父组件重新渲染:如果父组件的 render() 方法被调用,那么其中包含的子组件的 render() 方法也会被触发

48.![] == ![],![] == [],结果是什么?为什么?

在 JavaScript 中,比较操作符 "==" 用于比较两个值是否相等。对于给定的表达式:

  1. ![] == ![]:表示将两个空数组 [] 进行逻辑非运算,得到的结果都是 false。因此,该表达式相当于 false == false,即比较两个布尔值 false 是否相等。根据 JavaScript 的规则,两个布尔值相等仅在它们的值都为 false 或都为 true 时成立。所以,该表达式的结果是 true

  2. ①、根据运算符优先级 ,! 的优先级是大于 == 的,所以先会执行 ![]

    !可将变量转换成boolean类型,null、undefined、NaN以及空字符串(’’)取反都为true,其余都为false。

    所以 ! [] 运算后的结果就是 false

    也就是 [] == ! [] 相当于 [] == false

    ②、根据上面提到的规则(如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1),则需要把 false 转成 0

    也就是 [] == ! [] 相当于 [] == false 相当于 [] == 0

    ③、根据上面提到的规则(如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较,如果对象没有valueOf()方法,则调用 toString())

    而对于空数组,[].toString() -> ‘’ (返回的是空字符串)

    也就是 [] == 0 相当于 ‘’ == 0

    ④、根据上面提到的规则(如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值)

    Number(’’) -> 返回的是 0

    相当于 0 == 0 自然就返回 true了

综上所述,![] == ![] 的结果是 true![] == [] 的结果是 true

这里需要注意的是,在 JavaScript 中,比较操作符 "==" 的规则相对复杂,并且涉及到类型转换。为了避免出现意外的比较结果,推荐使用严格相等操作符 "===",它会在比较时同时考虑值和类型是否相等,更加准确和可靠。

49.什么是闭包,应用场景是什么?

闭包是指在函数内部创建的函数,并且该函数能够访问到其外部函数的作用域。换句话说,闭包是一个函数以及在该函数被创建时所处的词法环境的组合。

闭包有以下特点:

  1. 内部函数可以访问外部函数中的变量和参数。

  2. 外部函数的执行上下文被保留在内存中,即使外部函数执行完成后,内部函数仍然可以访问外部函数的作用域。

  3. 多个内部函数可以共享同一个父级作用域,形成一个闭包函数族。

闭包的应用场景包括但不限于:

  1. 保护变量:通过使用闭包,可以隐藏变量,只提供对外部函数公开的接口。这样,可以确保变量不会被外部直接修改,增加了数据的安全性。

  2. 计数器和累加器:通过闭包,可以在函数外部保存一个内部状态,并在每次调用内部函数时修改该状态。这一特性可用于实现计数器、累加器等功能。

  3. 延迟执行和回调函数:将函数作为返回值,可以实现延迟执行或者在特定条件满足时回调执行。

50.谈谈你是如何做移动端适配的?

对于移动端适配,一种常用的方法是响应式布局(Responsive Layout)和媒体查询(Media Queries)。以下是一些常见的移动端适配策略:

  1. 使用Viewport meta标签:在HTML文档的头部添加Viewport meta标签,通过设置width=device-width,可以告诉浏览器使用设备的宽度作为页面的宽度。另外,还可以设置initial-scalemaximum-scaleminimum-scale等属性来控制缩放行为。

  2. 使用相对单位:在样式表中使用相对单位(如百分比、em、rem)来定义元素的尺寸和布局,相对单位可以根据屏幕尺寸进行适配。

  3. 弹性布局:使用弹性盒模型(Flexbox)或网格布局(Grid)等弹性布局方式,使得元素能够根据可用空间自动调整大小和位置。

  4. 媒体查询:利用CSS3中的媒体查询功能,根据不同的屏幕尺寸和设备特性,为不同的屏幕宽度范围应用不同的样式规则。可以根据需要调整字体大小、布局结构、隐藏或显示某些元素等。

  5. 图片适配:通过设置图片的最大宽度为100%来保证图片在不同尺寸的屏幕上自适应。同时,使用高分辨率的图片(如Retina屏幕),以提供更清晰的图像。

  6. 测试和调试:在开发过程中,使用模拟器或真实设备进行测试,并通过调试工具(如Chrome开发者工具)来检查和优化页面在不同屏幕大小下的样式和布局。

综合使用以上方法,可以使网页在不同尺寸的移动设备上呈现出良好的用户体验,适应不同的屏幕大小和设备特性。

当然,对于较为复杂的移动端适配需求,可以考虑使用CSS预处理器(如Sass、Less)或CSS框架(如Bootstrap、Foundation)等工具来简化和优化适配过程。

51.移动端1像素的解决方案?

在移动端开发中,由于不同设备的像素密度差异,1像素问题成为了一个常见的难题。如果我们不对这个问题进行针对性的解决,那么会导致页面显示效果不美观,甚至影响用户体验。

以下是一些解决方案:

  1. 使用css3的scale属性:将要渲染的元素放大一倍,然后通过scale缩小回去。例如,将一个1像素的边框放大到2像素,再通过scale(0.5)恢复原来大小。这种方法可以使边框看起来更加清晰,但是可能会影响元素的布局和性能。

  2. 通过伪元素实现:使用伪元素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;
    document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=device-width,initial-scale=' + scale + ',maximum-scale=' + scale + ',minimum-scale=' + scale + ',user-scalable=no');

    这种方法可以根据设备分辨率进行动态适配,但是可能会对页面布局和性能产生影响。

  4. 使用第三方库:有一些开源的第三方库可以帮助我们解决1像素问题,例如border.csspostcss-1px等。这些库可以通过CSS预处理器或者PostCSS等工具使用。

52.弹性盒中的缩放机制是怎样的?

在弹性盒(Flexbox)布局中,存在一种缩放机制,可以通过调整弹性盒子的基准尺寸和剩余空间的分配方式来实现元素的缩放。这个机制称为「Flex的缩放」。

Flex缩放是通过flex-growflex-shrinkflex-basis这三个属性来控制的:

  1. flex-grow属性:确定弹性盒子在剩余空间中分配的比例,它决定了元素在主轴方向上的扩展能力。默认值为0,即不进行扩展。如果多个弹性盒子都设置了flex-grow,它们将按照数值比例分配剩余空间。

  2. flex-shrink属性:定义了元素在空间不足时的收缩能力,即在元素溢出容器时的缩放比例。默认值为1,表示会收缩。如果多个弹性盒子都设置了flex-shrink,它们将按照数值比例进行收缩。

  3. flex-basis属性:指定了弹性盒子在分配多余空间之前的初始尺寸。可以用像素(px)、百分比(%)或者auto(根据内容大小自动计算)来设置。默认值为auto

下面是一个示例代码,演示了Flex缩放的效果:

 

 
htmlCopy Code<div class="container">
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>
cssCopy Code.container {
  display: flex;
}
.box {
  flex-grow: 1; /* 元素扩展比例为1 */
  flex-shrink: 1; /* 元素收缩比例为1 */
  flex-basis: 100px; /* 初始尺寸为100px */
  background-color: gray;
  margin: 10px;
}

在上述示例中,.box元素设置了flex-grow: 1;,表示在剩余空间中按比例进行扩展。当容器宽度增加时,.box元素会根据比例分配到的空间进行水平扩展。当容器宽度减小时,.box元素会根据flex-shrink属性进行水平收缩。

需要注意的是,Flex缩放只会发生在弹性盒子的主轴方向上。在交叉轴方向上,元素的尺寸由align-self属性来控制,默认情况下不进行缩放。

53.说说你对useEffect的理解,可以模拟哪些生命周期?

(一)理解:

1)useEffect 是 React Hook 中的一个函数,用于处理副作用操作。副作用操作是指在组件渲染过程中需要进行的除了更新 UI 以外的 其他操作,例如发送网络请求、订阅事件、操作 DOM 等。

2)useEffect 接收两个参数:一个是副作用函数,另一个是依赖项数组。副作用函数会在每次组件渲染完成后执行,而依赖项数组用 于控制副作用函数的执行时机。

(二)可以模拟以下类似的生命周期:

 

 
    1)componentDidMount: 在组件初始渲染完毕后执行副作用函数,可以使用空的依赖项数组来模拟。

2)componentDidUpdate: 在组件更新后执行副作用函数,可以使用有依赖项的数组来模拟。

3)componentWillUnmount: 在组件即将销毁前执行清理操作,可以在副作用函数中返回一个清理函数来模拟。

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

Real DOM(真实DOM)

  1. 优点:Real DOM 是浏览器的底层实现,具有广泛的浏览器支持,并能够直接与硬件和操作系统交互,提供原生性能。

Virtual DOM(虚拟DOM)

  1. 优点

    • 提高性能:减少了对Real DOM的直接操作,从而减少了浏览器的回流(reflow)和重绘(repaint)次数。

    • 更容易进行跨平台开发

    • 更容易实现开发者工具和调试

  2. 缺点

    • 需要额外的内存:虚拟DOM需要在内存中维护一份DOM树的拷贝,占用了一些额外的内存。

    • 学习曲线:使用虚拟DOM需要开发者理解和掌握相应的概念,可能需要一定的学习成本。

Virtual DOM 的优点在于其性能优化和跨平台能力,使得前端开发更加高效。然而,它并不是适用于所有场景的银弹,因为在一些简单的应用中,Real DOM 可能更加直观和高效。在选择使用哪种方式时,开发者需要综合考虑应用的复杂性和性能需求。

55.说说React中setState和replaceState的区别?

在 React 中,setState()和 replaceState()是用于更新组件状态的两个方法。它们之间有一些区别。

1)setState(newState):setState() 方法用于更新组件的状态。它接收一个新状态对象作为参数,并将新状态与当前状态合并。React 会合并状态更新并自动触发组件的重新渲染。这意味着 setState() 是基于当前状态的增量更新方式。例如:

2)replaceState(newState):replaceState()方法也用于更新组件状态,但它不会自动合并新旧状态。相反,它会完全替换掉当前状 态,使用提供的新状态对象来更新组件。这意味着调用 replaceState() 会完全重置组件状态,并强制触发重新渲染。

3)在 React v16.3 之后的版本中,官方不再推荐使用 replaceState(),并且将其作为不稳定的方法。相反,推荐使用函数形式的 setState(),可以更好地处理并发更新的情况,并且可以确保基于最新状态计算的正确结果。

56.说说react中onClick绑定后的工作原理?

在 React 中,onClick是用于绑定点击事件的属性。当元素被点击时,绑定的 onClick事件处理函数会被触发执行。

具体工作原理如下:

1)绑定事件处理函数:在 JSX 中,你可以将 onClick 设置为一个函数

2)渲染并呈现元素:React 会将 JSX 渲染为虚拟 DOM,并将其呈现在浏览器中。在渲染过程中,React 会为每个元素添加事件监听器。

3) 监听点击事件:当元素被点击时,浏览器会触发相应的点击事件。

4)执行事件处理函数:当点击事件被触发时,绑定的事件处理函数会被执行。例如,当点击上述按钮时,handleClick 函数将被调用。

5)需要注意的是,在 React 中,所有事件处理函数都会被自动绑定到组件实例的上下文中。这意味着你不需要手动绑定函数的上下文或使用箭头函数来避免上下文问题。React 会自动确保事件处理函数中的 this指向正确的组件实例。

57.Redux的实现原理,写出其核心实现代码?

下面是 Redux 核心实现的简化代码示例:

```javascript

// 定义初始状态

const initialState = {

count: 0

};

// 定义 reducer 函数

const reducer = (state = initialState, action) => {

switch (action.type) {

case 'INCREMENT':

return {

...state,

count: state.count + 1

};

case 'DECREMENT':

return {

...state,

count: state.count - 1

};

default:

return state;

}

};

// 创建 Redux store

const store = createStore(reducer);

// 监听状态变化

store.subscribe(() => {

console.log(store.getState());

});

// 派发动作更新状态

store.dispatch({ type: 'INCREMENT' });

store.dispatch({ type: 'DECREMENT' });

58.什么是垂直外边距合并?说说合并后的几种情况?

垂直外边距合并(Vertical Margin Collapse)是 CSS 盒模型中的一个特性,指的是两个或多个垂直相邻的元素的上下外边距重叠在一起,合并成一个外边距的现象。

垂直外边距合并可能会导致一些不直观的结果,下面是合并后的几种情况:

  1. 兄弟元素的合并:当两个相邻的兄弟元素没有边框、内边距、块格式化上下文分隔或清除浮动的元素,它们的垂直外边距会合并成一个外边距。合并后的外边距大小为两个外边距中的较大值。

  1. 嵌套元素的合并:如果一个元素内部没有发生块格式化上下文的分隔,它的上外边距会和第一个子元素的上外边距以及最后一个子元素的下外边距合并。合并后的外边距大小仍然是两个外边距中的较大值。

  1. 空块元素的合并:如果一个没有边框、内边距、行内内容和高度的块级元素,它的上下外边距会合并成一个外边距。合并后的外边距大小由元素自身的外边距决定。

  1. 父元素与第一个/最后一个子元素的外边距合并:在某些情况下,父元素的上外边距可能会和第一个子元素的上外边距或者父元素的下外边距会和最后一个子元素的下外边距合并。这种情况下,合并后的外边距大小由外层的边界决定。

59.useEffect的依赖为引用类型如何处理?

在 React 的 useEffect 钩子中,依赖项(dependencies)用于确定何时执行 effect 函数。默认情况下,React 使用浅比较(shallow comparison)来比较依赖项的值,以确定是否触发 effect。

当依赖项是引用类型(如数组或对象)时,可能会遇到一些问题。因为浅比较只会比较引用值而不会递归比较对象或数组的值,所以当引用类型的值发生改变时,浅比较无法捕获到这些变化。

如果你希望在引用类型的值发生改变时触发 effect,有几种处理方式:

  1. 使用深比较(Deep Comparison):手动在 effect 中进行深比较,比较引用类型的值的具体内容,然后根据需要执行相应的操作。可以使用工具库(如 lodash 的深比较函数 isEqual)来简化深比较的过程。

  1. 使用唯一标识符:给引用类型的值添加一个唯一的标识符,并将该标识符作为依赖项。当引用类型的值发生改变时,其标识符也会改变,从而触发 effect。

  1. 使用 useRef 进行比较:使用 useRef 钩子创建一个可持久化的变量,保存上一次的依赖项值,并在 effect 内部进行比较。

60.知道react里面的createPortal么,说说其使用场景?

createPortal 是 React 提供的一个 API,它可以让你将子组件渲染到 DOM 结构中的不同位置(不仅仅是组件所在的容器)。

createPortal 的使用场景包括但不限于以下几种情况:

  1. 在组件外部创建容器:有时候,你可能需要在组件的外部创建一个容器,然后将某个子组件渲染到该容器中。createPortal 可以帮助你做到这一点,例如在模态框(Modal)组件中,你可以使用 createPortal 将模态框的内容渲染到根节点之外的某个容器中,以防止样式和事件冲突。

  1. 在布局中插入子组件:有时候,你可能希望将某个子组件插入到组件树的特定位置,而不是组件所在的容器中。createPortal 可以让你灵活地管理组件的渲染位置,例如在一个有固定布局的导航栏组件中,你可以使用 createPortal 将弹出菜单组件渲染到根节点之外的导航栏容器中。

  1. 与第三方库的集成:有时候,你可能需要将 React 组件嵌入到已有的非 React 代码中(如 jQuery 或其他第三方库)。createPortal 可以帮助你将 React 组件渲染到这些外部代码所控制的 DOM 容器中。

  1. 处理 z-index 排序:在某些情况下,你可能需要控制组件在层叠布局中的显示顺序。使用 createPortal 可以将组件渲染到其他 DOM 节点上,通过控制它们的位置来改变层叠顺序。

61.Provider和connect的底层原理实现,写出其核心代码?

Provider 和 connect 是 React Redux 中常用的两个组件,它们共同提供了状态管理的能力。下面是它们的底层实现的简化版本核心代码:

1)Provider 的核心代码:

```javascript

import React from 'react';

import PropTypes from 'prop-types';

import { createStore } from 'redux';

class Provider extends React.Component {

static propTypes = {

store: PropTypes.object.isRequired,

children: PropTypes.element.isRequired

};

static childContextTypes = {

store: PropTypes.object.isRequired

};

getChildContext() {

return {

store: this.props.store

};

}

render() {

return React.Children.only(this.props.children);

}

}

export default Provider;

```

2)connect 的核心代码:

```javascript

import React from 'react';

import PropTypes from 'prop-types';

export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {

class Connect extends React.Component {

static contextTypes = {

store: PropTypes.object.isRequired

};

componentDidMount() {

this.unsubscribe = this.context.store.subscribe(this.handleChange);

}

componentWillUnmount() {

this.unsubscribe();

}

handleChange = () => {

this.forceUpdate();

};

render() {

const { store } = this.context;

const stateProps = mapStateToProps(store.getState());

const dispatchProps = mapDispatchToProps(store.dispatch);

return <WrappedComponent {...this.props} {...stateProps} {...dispatchProps} />;

}

}

return Connect;

};

```

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前端Vue常见的面试题中,有以下几个常见问题: 1. v-show和v-if的区别是什么? v-show和v-if都是Vue中用于控制元素显示与隐藏的指令,但它们的工作原理有所不同。 v-show是通过CSS的display属性来控制元素的显示与隐藏,当条件为真时,元素会显示,条件为假时,元素会隐藏。v-show适用于频繁切换元素显示与隐藏的情况。 v-if是根据条件动态地渲染或销毁元素,当条件为真时,元素会渲染到DOM中,条件为假时,元素会从DOM中移除。v-if适用于在显示逻辑复杂、切换频率较低的情况下使用。 2. Vue中如何监听数据变化? Vue提供了多种方式来监听数据的变化,其中包括: - 使用watch属性来监听特定数据的变化。 - 使用computed属性来计算派生数据,当依赖的数据发生变化时,计算属性会重新计算。 - 使用Vue提供的$watch方法来手动监听数据的变化。 3. Vue中如何编写可复用的组件? 在Vue中,可以通过编写可复用的组件来提高代码的重用性和可维护性。编写可复用的组件需要遵循以下几个步骤: - 将组件的逻辑和样式封装在一个.vue文件中。 - 使用props属性来接收父组件传递的数据。 - 使用emit方法触发自定义事件,让父组件可以监听子组件的状态变化。 - 使用slot插槽来实现组件的灵活性,让父组件可以自定义子组件的内容。 这些是前端Vue常见的面试题,希望对你有所帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [前端vue经典面试题78道(重点详细简洁)](https://blog.csdn.net/weixin_59519449/article/details/123636668)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值