1. 说说你对react的理解?有哪些特性?
React是用于构建用户界面的javascript库,只提供了ui层面的解决方案。遵循组件设计模式,声明式编程范式和函数式编程概念,让前端应用程序变得更加高效灵活。使用虚拟dom来操作dom,遵循从高阶组件到低阶组件的单向数据流。将界面分成两个独立的小块,每个小块都是一个组件,这些组件可以进行组合,嵌套组合,嵌套并且构成了整体页面。react
类组件使用一个名为 render()
的方法或者函数组件return
,接收输入的数据并返回需要展示的内容
特性:Jsx语法、单项数据绑定、 虚拟dom 、声明式编程 ,component
2. 说说Real DOM和Virtual DOM的区别?优缺点?
(1)虚拟dom不会进行重绘与回流。真实dom会进行重绘与回流。
(2)虚拟dom的总消耗=虚拟dom增删改+真实dom差异增删改+排版与重绘。真实dom的总消耗=真实dom完全增删改+排版与重绘。
真实 DOM
的优势:易用
缺点:效率低,解析速度慢,内存占用量过高,性能差:频繁操作真实 DOM,易于导致重绘与回流
使用虚拟 DOM
的优势如下:
简单方便:如果使用手动操作真实 DOM
来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能
跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行
缺点:
在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢
3. 说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?
挂载阶段:
constructor:组件被创建时调用,用于初始化组件的状态和绑定事件处理函数。
render:根据组件的props和state返回一个React元素,用于构建组件的UI。
ComponentDidMount:组件被插入到DOM树中后调用,用于进行异步操作、订阅事件等。
更新阶段:
static getDerivedStateFromProps:在接收到新的props时调用,用于根据props更新组件的状态。
shouldComponentUpdate:在组件更新前调用,用于决定是否重新渲染组件。 render:根据组件的props和state返回一个React元素,用于构建组件的UI。 getSnapshotBeforeUpdate:在组件更新前调用,用于获取更新前的DOM状态。 componentDidUpdate:组件更新后调用,用于进行DOM操作、网络请求等。
销毁阶段:componentWillUnmount:组件被从DOM树中移除前调用,用于清理定时器、取消订阅等。
4. 说说React中setState执行机制?
-
异步执行:setState 方法是异步执行的,即调用 setState 后,并不会立即更新组件的状态。React 会将新的状态合并到一个队列中,等到适当的时机才会批量更新组件的状态。
-
队列机制:当调用 setState 方法时,React 会将新的状态添加到更新队列中,并标记组件为“待更新”。如果在同一个事件循环中多次调用 setState,React 会将这些更新合并成一个更新。
-
批量更新:React 会在合适的时机(比如当前事件循环结束、定时器触发、异步请求完成等)对队列中的更新进行批量处理,即批量更新组件的状态。这样可以避免频繁的 DOM 操作,提高性能和效率。
-
合并更新:在进行批量更新时,React 会对更新队列进行合并,只进行一次 DOM 操作。React 会根据组件的更新策略(比如 shouldComponentUpdate)判断是否需要更新组件的状态,并重新渲染组件。
5. 说说react的事件机制?
React的事件机制是基于合成事件的。React通过在DOM节点上注册少量的全局事件监听器,来处理所有的事件。当事件触发时,React会使用单一的事件监听器来捕获并分发事件。React的合成事件是对原生浏览器事件的跨浏览器封装。它提供了与原生事件相似的属性和方法,并且具有跨浏览器的兼容性。在React中,通过在组件元素上定义特定的事件属性,可以将事件处理函数与特定的事件关联起来。当事件触发时,React将创建一个合成事件对象,并将其传递给事件处理函数。事件处理函数可以访问合成事件对象,以获取有关事件的信息。
6. React组件之间如何通信?
父传子:在父组件中的子组件上挂载一个自定义属性,子组件通过props来接收父组件传递过来的值 子传父:在在父组件中的子组件上挂载一个自定义方法,子组件也是通过props接收函数并调用在上面挂载传递的参数,父组件上定义的方法第一个参数就是接收到的值 Context:在文件夹创建一个createContext空对象,在父组件中通过provider标签来包裹数据,在子组件中通过customer标签来接收,接收一个回调函数
7. 说说你对受控组件和非受控组件的理解?应用场景?
受控组件:就是受控制的组件。受setstate的控制,受控组件就是表单的回调,提交用户信息,多用于修改操作。
非受控组件指的是,表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。 在非受控组件中,可以使用一个ref来从DOM获得表单值。
8. 说说你对fiber架构的理解?解决了什么问题?
Fiber 架构是 React 在 16 版本中引入的一种新的协调机制,旨在解决 React 在处理大型应用中的性能问题和用户体验问题。
传统的 React 渲染过程是同步的,即一旦开始渲染,就会一直执行完整个渲染过程,期间无法中断。这种方式在处理大量计算密集型操作或者渲染大型组件树时,可能会导致主线程被长时间占用,造成页面卡顿,用户交互不流畅。
Fiber 架构的目标是将 React 的渲染过程分解为多个小任务,使得渲染过程可中断和恢复,以实现更好的用户体验。Fiber 架构的核心是虚拟的 Fiber 节点树,它以链表的形式表示组件树,通过优先级调度算法和协调器的配合,实现了任务的拆分、优先级调度和任务中断。
Fiber 架构解决了以下问题:
-
增量渲染:Fiber 架构将渲染过程拆分为多个小任务,每个任务可以在主线程空闲时执行,这样就可以实现增量渲染,将渲染过程分散到多个帧中,减少了单个渲染过程的时间,提升了用户体验。
-
任务优先级调度:Fiber 架构引入了任务优先级的概念,通过给不同任务赋予不同的优先级,可以根据任务的重要性和紧急程度来调度任务的执行顺序,保证用户交互的响应速度。
-
中断和恢复:由于 Fiber 架构的任务是可中断的,当有更高优先级的任务需要执行时,可以中断当前任务的执行,将控制权交还给浏览器,让用户交互能够得到及时响应。当浏览器空闲时,可以恢复中断的任务,继续执行。
-
错误边界:Fiber 架构引入了错误边界的概念,可以将错误隔离到组件树的更上层,避免整个应用崩溃。通过错误边界,可以优雅地处理组件内部的错误,提高应用的健壮性和可靠性。
9. 说说react diff的原理是什么?
-
双端比较:React会同时遍历新旧两棵树,逐个比较。
-
同层比较:React会逐层比较,如果当前层有变化,会继续比较下一层,如果当前层没有变化,则跳过该层的所有子级节点。
-
标识符提高比较准确性:通过key属性给元素标识,React可以更准确的比较元素,找到正确的变化位置。
-
不同类型元素替换渲染:如果新旧两棵树的某个位置对应的元素类型不同,React会销毁旧的子树并重新构造新的子树。
-
状态提高渲染效率:React会记录元素的状态,通过状态来判断是否需要重新渲染该元素。
-
事件监听保持不变:如果元素类型不同但有相同的key,React会复用原有元素的事件监听器。
10. 说说你对redux中间件的理解?常用的中间件有哪些?实现原理?
Redux中间件的理解:redux是介于应用系统和系统软件之间的一类软件,使用系统软件提供的基础服务,衔接网络上应用系统的各个部分或者不同应用,从而达到资源共享,功能共享的目的。
常用中间件:redux-thunk用于异步操作中间件,redux-logger用于日志记录。
实现原理:所有的中间件都放在到数组中嵌套执行,判断传递过来的数据类型,最后执行dispath中间件内的middleware api可以拿到getSate和dispath的方法。
11. 如何使用css实现一个三角形,写出两种以上方案得满分?
方案一:使用 border 属性
.triangle { width: 0; height: 0; border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 100px solid red; }
方案二:使用 transform 属性
.triangle { width: 0; height: 0; border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 100px solid red; transform: rotate(45deg); }
12. 什么是强缓存和协商缓存?
强缓存:浏览器不会向服务器发送请求,直接向本地存储中读取数据并返回state code 200 协商缓存:浏览器向服务器发送请求,服务器根据浏览器传递的请求判断是否命中协商缓存,如果命中,服务器会返回给浏览器一个reponse,浏览器去本地存储中读取文件
13. 说说React jsx转换成真实DOM的过程?
-
解析:React会使用Babel等工具将JSX代码解析为JavaScript对象。
-
创建元素:解析后的JSX代码会被转换为React元素对象。React元素是一个普通的JavaScript对象,包含有关组件类型、属性和子元素的信息。
-
调和:React会将元素与之前的渲染结果进行比较,找出需要更新的部分,然后生成更新的操作。
-
渲染:React将生成的更新操作应用于真实的DOM,以保持UI与组件状态的同步。React使用虚拟DOM(Virtual DOM)来提高渲染性能,避免不必要的DOM操作。
14. 说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?
react-redux 是的官方 React UI 绑定层,允许您的 React 组件从 Redux 存储中读取数据,并将操作分派到存储以更新状态。 @reduxjs/toolkit 是对 Redux 的二次封装,开箱即用可的一个高效的 Redux 开发工具集,使得创建store、更新store更加方便
15. React render方法的原理,在什么时候会触发?
render 方法的原理如下:
首次渲染:当一个组件被创建并插入到 DOM 中时,React 会调用该组件的 render 方法来生成组件的虚拟 DOM(Virtual DOM)树。然后,React 会将这个虚拟 DOM 树转换成真实的 DOM 树,并插入到页面的相应位置上。
更新渲染:当组件的状态(state)或属性(props)发生变化时,React 会自动调用组件的 render 方法重新生成新的虚拟 DOM 树。然后,React 会通过比较新旧虚拟 DOM 树的差异,将变化的部分更新到真实的 DOM 树上,从而更新页面的显示。
render 方法在以下情况下会触发:
-
组件首次渲染时,会调用组件的 render 方法生成初始的虚拟 DOM 树。
-
组件的状态(state)发生变化时,会重新调用组件的 render 方法生成新的虚拟 DOM 树,并通过比较差异更新页面显示。
-
组件的属性(props)发生变化时,会重新调用组件的 render 方法生成新的虚拟 DOM 树,并通过比较差异更新页面显示。
16. React性能优化的手段有哪些?
(1)使用纯组件
(2)路由懒加载
(3)避免使用内联样式属性
(4)优化react中的条件渲染
(5)列表渲染的时候加key
(6)类组件使用immutable 减少渲染次数
(7)React Fragments避免额外标签
(8)React.memo:缓存组件渲染,避免不必要的更新
(9)使用PureComponent或shouldComponentUpdate:PureComponent会实现一个浅对比的shouldComponentUpdate(),可以避免不必要的重新渲染。 对于复杂的state,可以手动实现shouldComponentUpdate(),进行深度对比,避免跳过必要的重新渲染,同时提高不必要重新渲染的判断精度。
17. 如何通过原生js实现一个节流函数和防抖函数,写出核心代码,不是简单的思路?
-
节流函数:
function throttle(func, delay) { let timer = null; return function() { if (!timer) { timer = setTimeout(() => { func.apply(this, arguments); timer = null; }, delay); } }; }
-
防抖函数:
function debounce(func, delay) { let timer = null; return function() { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, arguments); }, delay); }; }
18. 说说webpack中代码分割如何实现?
手动配置代码分割:通过在 webpack 配置文件中手动配置代码分割的规则。可以使用 entry
配置项指定入口文件,使用 output
配置项指定输出文件名,然后使用 optimization.splitChunks
配置项来指定代码分割的规则。
动态导入(Dynamic Import):在代码中使用动态导入语法来实现代码分割。动态导入可以通过 import()
函数来实现
使用第三方库:一些第三方库已经内置了代码分割的功能,例如 React 的 React.lazy()
和 React.Suspense
,Vue 的 Vue Router
等。通过使用这些库提供的功能,可以方便地实现代码分割。
19. 说说如何借助webpack来优化前端性能?
-
代码压缩和混淆:Webpack 可以使用 UglifyJSPlugin 或 TerserPlugin 来压缩和混淆代码,减小文件体积,提高加载速度。
-
代码分割:通过配置 Webpack 的 SplitChunksPlugin 或动态导入语法 (Dynamic Import) 来实现代码分割,将代码拆分成更小的块,按需加载,减少初始加载的文件大小。
-
懒加载和按需加载:使用动态导入语法 (Dynamic Import) 或第三方库(如 React.lazy 和 Vue Router)来实现懒加载和按需加载,只在需要时才加载相应的模块,减少初始加载的资源。
-
缓存优化:通过为生成的文件添加哈希值来解决浏览器缓存问题,确保文件内容发生变化时,浏览器能够正确地获取到最新的文件。
-
Tree Shaking:使用 Webpack 的 Tree Shaking 功能,可以剔除未使用的代码,减少文件体积。
-
图片优化:使用 file-loader 或 url-loader 配合 image-webpack-loader 对图片进行压缩和优化,减小图片文件大小,提高加载速度。
-
编译缓存:使用 Webpack 的缓存功能,通过配置 cache-loader 或 hard-source-webpack-plugin,可以缓存编译过的模块,提高构建速度。
-
并行构建:使用 Webpack 的 parallel-webpack 或 happypack 插件,可以实现多线程并行构建,加快构建速度。
20. 说说javascript内存泄漏的几种情况?
-
无意的全局变量:在全局作用域中定义的变量会一直存在于内存中,直到页面关闭。如果不小心声明了一个全局变量,并且忘记在后续代码中清除它,就会造成内存泄漏。
-
被遗忘的定时器或回调函数:定时器(setTimeout、setInterval)和回调函数(addEventListener、Promise)在不再需要时,需要手动清除。如果忘记清除定时器或回调函数,它们将继续存在于内存中,导致内存泄漏。
-
闭包:闭包是指一个函数访问了其外部函数的变量,而这个函数又被保留在外部作用域中。如果闭包中引用了大量的变量或者引用了 DOM 元素,但不再需要使用它们,就会导致内存泄漏。
-
DOM 引用:如果在 JavaScript 中保留了对 DOM 元素的引用,而这些元素在后续的操作中被删除或替换,但是引用没有被清除,就会导致内存泄漏。例如,通过事件监听器或全局变量持有对 DOM 元素的引用,如果没有及时移除监听器或清除全局变量,就会引起内存泄漏。
-
循环引用:当两个或多个对象之间形成循环引用时,即使它们没有被其他部分引用,也无法被垃圾回收机制回收。这种情况下,需要手动打破循环引用,以便垃圾回收机制可以正确释放内存。