第一部分:函数组件和类组件
01.React组件之间如何通讯?
Props(属性):父组件可以通过属性将数据传递给子组件,并且子组件可以通过props来获取这些数据。这是React中最常用的一种通讯方式。
Callbacks(回调函数):父组件可以将自己的函数作为props传递给子组件,在子组件内部调用该函数,从而实现与父组件的通讯。
Context(上下文):Context提供了一种在组件树中共享数据的方法,可以允许组件在不通过props层层传递的情况下访问共享的数据。
共享状态管理库(如Redux、Mobx等):这些库提供了一种集中式的方式来管理应用程序的状态,并且可以在组件之间共享和更新状态。
发布-订阅模式或事件总线:可以使用第三方库(如EventEmitter)或自己实现一个事件总线,允许多个组件通过订阅事件和发布事件的方式进行通讯。
02.JSX本质是什么?
JSX(JavaScript XML)是React中的语法扩展,它使用类似XML的语法来描述UI组件的结构,可以将HTML、CSS和JavaScript代码组合在一起。实质上,JSX是一种语法糖,它是React.createElement()的简化语法,用于创建React元素。当我们在JSX中编写组件时,它最终会被转换成JavaScript代码并运行在浏览器中。因此,JSX本质上是一种用于描述React组件结构的JavaScript语法扩展。
03.Context是什么,如何应用?
Context是指在程序中执行的当前环境,包括当前的状态、配置、数据等。它可以提供给代码访问系统资源和服务的权限,允许代码与其他组件交互并执行操作
在Android开发中,Context是一个非常重要的概念。Activity、Service、Application等都是Context的子类,它们都提供了一些与平台交互的API,如访问资源、启动Activity等。在开发过程中,我们经常需要使用Context来访问系统服务、启动Activity、发送Broadcast Intent等操作
04.说一下shouldComponentUpdate的用途?
shouldComponentUpdate是React生命周期方法中的一个,用于在组件更新前判断是否需要重新渲染。其用途主要是优化性能,因为在React中,每次组件更新都会重新渲染DOM,会造成不必要的开销。如果组件的状态或属性没有变化,则可以避免不必要的渲染,从而提高性能。
05.说一下redux单向数据流的机制?
Redux的单向数据流机制是一种基于Flux架构的数据流管理模式,它通过一个单一的store来管理应用程序的状态,并通过action/dispatch/reducer的机制来实现数据的更新。具体来说,Redux的单向数据流机制可以通过以下几个步骤实现:
-
Store:应用程序的状态存储在一个单一的store中,即一个JavaScript对象,它通过reducer函数来更新和管理状态。
-
Action:定义一个描述状态变化的JavaScript对象,包含一个type属性和一些数据。Action是唯一可以操作store中数据的方式,它通过dispatch方法派发给store。
-
Dispatch:通过store.dispatch(action)方法派发一个action来更新store中的状态。dispatch方法会调用store中的reducer函数,并将当前的state和action传入reducer函数,根据action的type属性来更新state中的数据。
-
Reducer:reducer是一个纯函数,它接收当前的state和一个action作为参数,返回新的state。reducer通过使用当前的state和action来保持应用程序的状态更新。
-
View:Redux中的View层是React组件,它通过connect方法将store中的state映射到组件的props属性上,从而访问store中的数据。当view层中的组件需要修改state时,它会触发一个action并通过dispatch方法将这个action传递给store中的reducer,从而更新应用程序的状态。
在Redux的单向数据流机制中,所有的数据变化都是通过Action/dispatch/reducer的方式来进行的,这保证了数据的一致性和可预测性,并且有效地防止了应用程序中的状态混乱。同时,Redux的机制也使得应用程序的状态变更易于追踪和调试,提高了应用程序的可维护性。
06.React类组件的setState是同步操作还是异步操作?
React类组件的setState操作是异步操作,也就是说在调用setState之后,不会立即更新state,而是先存放在一个队列里,等到适当的时机(比如React的事件循环结束)再去执行更新。这样可以提高应用性能,避免频繁的重渲染。但是如果需要立即获取更新后的state值,可以在setState中传入一个回调函数作为第二个参数,该回调函数会在state更新之后立即执行。
07.什么是纯函数?
纯函数是指在相同的输入情况下,总是返回相同的输出,并且没有任何副作用的函数。
08.介绍React组件生命周期
-
constructor(props)
:构造函数,通常用来初始化state和bind方法。 -
getDerivedStateFromProps(nextProps, prevState)
:在组件实例化和更新时被调用,用来检查props变化是否需要更新state(如果需要就返回一个对象用来更新state,否则就返回null)。 -
shouldComponentUpdate(nextProps, nextState)
:在组件更新之前被调用,用来判断组件是否需要重新渲染(如果需要返回true,否则返回false)。 -
render()
:渲染组件的UI。 -
getSnapshotBeforeUpdate(prevProps, prevState)
:在组件更新前被调用,用来获取更新前的DOM状态。 -
componentDidUpdate(prevProps, prevState, snapshot)
:在组件更新后被调用,用来操作更新后的DOM节点。 -
componentDidMount()
:在组件挂载后被调用,用来执行需要在组件挂载完成后才能执行的操作。 -
componentWillUnmount()
:在组件卸载前被调用,用来执行组件销毁前的清理工作。
09.React发起ajax应该在哪个生命周期
React发起Ajax请求应该在React组件的生命周期函数中的componentDidMount()
中发起。
10.渲染列表,为何使用key?
在渲染列表时,使用 key 属性是非常重要的,因为它有助于 React 更好地跟踪哪些元素被添加、删除或修改。
11.函数组件和class组件的区别
函数组件:
-
语法简单,只需要返回一个 JSX 元素即可。
-
没有状态,不能使用 state 和生命周期等 React 特性。
-
可以使用 hooks,来处理组件局部的状态和处理副作用等操作。
-
性能更高,因为没有实例化组件类的过程。
-
不能使用 ref 属性来引用组件的实例,也不能使用 shouldComponentUpdate 等生命周期方法。
Class 组件:
-
语法相对复杂,需要继承 React.Component 。
-
有状态,可以使用 state 和生命周期等 React 特性。
-
可以通过类方法来定义事件处理程序、生命周期函数等。
-
可以使用 ref 属性来引用组件的实例。
-
性能较低,因为实例化类需要更多的计算资源。
12.什么是受控组件、什么是非受控组件?
受控组件是由 React 组件完全控制的组件。它的值和行为由组件的 state 和 props 决定。通过在 state 中存储组件的值,并在 onChange 事件中更新 state,组件可以响应用户输入并更新自身状态。例如,一个表单输入框的值会被存储在 state 中,当用户输入时,onChange 事件会触发更新 state 的操作。
非受控组件则是由 DOM 元素自己控制的组件。它的值和行为由 DOM 元素自己决定。可以通过 ref 属性访问 DOM 元素的值。非受控组件通常用于需要快速获取用户输入值的场景,例如表单提交
13.何时使用异步组件?
-
组件较大且可分为更小的子组件时,可以将该组件设置为异步,以便子组件在需要时进行懒加载。
-
加载时间过长的组件,例如带有大量数据的列表或图表,可以异步加载,以便用户能够更快地查看和交互。
-
组件在特定条件下才会被渲染,例如按需加载的侧栏或模式对话框,可以将其设置为异步。
14.多个组件有公共逻辑,如何抽离?
可以将公共逻辑提取为一个单独的模块或者组件,然后在需要使用这些逻辑的组件中引用该模块或者组件。这样可以避免重复编写相同的代码,提高代码复用性和维护性
15.Redux如何进行异步请求?
Redux本身不直接支持异步请求,但是可以使用中间件来实现。常用的中间件有redux-thunk、redux-saga和redux-observable。
16.React-router如何配置懒加载?
React Router 可以使用 React.lazy() 方法和 Suspense 组件进行懒加载配置。
17.什么是PureComponent?
PureComponent是React中的一个优化技巧,它是React.Component的一个变体。与React.Component不同,PureComponent会在shouldComponentUpdate()方法中自动进行浅比较(shallow comparison)来判断是否需要重新渲染组件。如果组件的props和state没有变化,那么PureComponent就不会重新渲染,从而提高了组件的性能
18.React事件和DOM事件有什么区别?
-
事件名称不同:React事件名称采用驼峰命名法,如onClick,而 DOM 事件名称采用小写字母,如onclick。
-
事件处理函数不同:React事件处理函数是一个普通的JS函数,而DOM事件处理函数必须是一个全局函数。
-
事件绑定方法不同:React通过属性来绑定事件,如onClick={this.handleClick},而DOM事件则是通过addEventListener或者直接在HTML元素上绑定事件。
-
事件对象不同:React事件处理函数的事件对象是封装过的合成事件对象,其属性和方法是跨浏览器兼容的;而DOM事件处理函数的事件对象是浏览器原生的事件对象,其属性和方法可能会因浏览器而异。
-
事件传播方式不同:React事件采用的是合成事件机制,事件先在组件内部进行处理,然后再冒泡到组件树的上层。而DOM事件采用的是事件冒泡机制,事件从目标元素开始向上传播,直到根节点。
19.React性能优化方式有哪些?
1.避免不必要的重复渲染:使用PureComponent或React.memo对组件进行优化,避免组件重复渲染,从而提高性能。
2.使用shouldComponentUpdate:手动控制渲染,只在需要更新的时候才更新组件。
3.使用异步渲染:使用React.lazy、Suspense和Error Boundary等技术进行异步渲染,提高应用性能。
4.减少重渲染:将组件拆分成更小的组件,减少重渲染时的计算量。
5.使用生命周期方法:生命周期方法可以帮助我们在适当的时候进行一些操作,如componentDidMount、componentDidUpdate等。
6.使用useCallback和useMemo:在函数组件中使用useCallback和useMemo可以避免不必要的计算和函数重复定义,从而提高性能。
7.虚拟化长列表:使用react-virtualized等库可以虚拟化长列表的展示,避免一次性渲染大量数据。
8.使用CDN:使用CDN可以加快组件和资源的加载速度,提高性能。
9.使用shouldUpdate和react-addons-perf: shouldUpdate可以优化数据更新的效率,react-addons-perf可以提供性能分析工具帮助我们找出性能瓶颈。
20.说一下React和Vue的区别
-
模板语法:React使用JSX语法,将HTML和JavaScript混合在一起,而Vue使用模板语法。
-
生命周期:React和Vue的生命周期概念是类似的,但是具体的生命周期方法不同。
-
组件通信:React使用props向子组件传递数据,使用回调函数或Context API向父组件传递数据。Vue使用props和自定义事件向子组件传递数据,使用$emit和$parent向父组件传递数据。
-
数据绑定:React使用单向数据流,即数据只能从父组件向子组件传递。Vue支持双向数据绑定,即数据的变化可以同时影响到父组件和子组件。
-
状态管理:React使用Redux等状态管理工具,Vue使用Vuex进行状态管理。
21.说一下什么是 redux、 react-redux、 redux-thunk、 redux-saga、redux-toolkit,它们之间的 区别是什么?
Redux是一个独立的JavaScript状态管理库,它可以与任何JavaScript应用程序一起使用。它提供了一种可预测性的状态管理解决方案,可以使应用程序变得更加可靠和易于调试。Redux的核心原则是单一状态树,状态只能通过纯函数来修改。
React-Redux是Redux库的官方绑定库,它提供了一些用于将Redux与React集成的组件和高阶函数。使用React-Redux,可以更容易地将Redux状态管理集成到React应用程序中。
Redux-Thunk是Redux中间件之一,它允许应用程序进行异步操作,例如发起API请求。Thunk是一个函数,它在Redux action中处理异步逻辑,可以返回一个函数而不是对象,这个函数可以在异步操作完成后被调用。
Redux-Saga是另一个Redux中间件,它提供了一种在Redux中管理副作用的方式,例如异步请求、定时器和WebSocket连接等。它使用ES6的Generator函数,使得副作用的管理变得更加容易。
Redux-Toolkit是一个官方维护的Redux工具集,它提供了一些用于简化Redux开发的实用工具。Redux Toolkit包括Redux中间件、帮助函数、常常使用的Redux模式和标准化代码结构等。
第二部分:react hooks
01.列举十个常用的React内置Hooks
- useState:管理组件的局部状态。
- useEffect:副作用处理,类似于componentDidMount 和 componentDidUpdate。
- useContext:用于在组件中获取上下文。
- useReducer:基于 reducer 的状态管理,用于管理复杂的状态逻辑。
- useCallback:缓存函数引用,用于优化组件性能。
- useMemo:缓存值引用,用于优化组件性能。
- useRef:获取 DOM 元素或存储任意值的可变容器。
- useImperativeHandle:暴露自定义 ref,允许更精细的控制子组件暴露给父组件的 DOM 节点或实例。
- useLayoutEffect:和 useEffect 相似,但在浏览器执行绘制之前同步执行。
- useDebugValue:在 React 开发者工具中显示自定义 hook 的标签。
02.为什么会有React Hooks,他解决了哪些问题?
React Hooks 是 React 16.8 发布的一个新特性,它的主要目的是解决维护 class 组件中状态逻辑复杂、难以复用的问题。React Hooks 让我们可以在函数组件中使用类组件中才能使用的 state、生命周期函数等特性,从而可以更方便、清晰地管理组件状态和逻辑。
React Hooks 通过两个核心 hook 函数 useState 和 useEffect 来实现以上目的:
-
useState: useState 可以让我们在函数组件中使用 state,它可以取代类组件中的 this.state,并且可以通过解构赋值的方式获取到当前状态值和更新状态的函数。这让我们可以轻松地在多个组件中复用状态逻辑,并且更加方便地管理组件状态。
-
useEffect: useEffect 可以让我们在函数组件中使用生命周期函数,它可以取代类组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount,从而让我们可以在函数组件中处理副作用。这让我们可以更好地控制组件的行为,并且更加清晰地表达组件的意图。
除了以上两个核心 hook 函数外,还有其他一些常用的 hook 函数,如 useContext、useReducer、useCallback、useMemo 等,它们可以帮助我们更好地解决组件状态逻辑复杂、难以复用的问题,并且让我们可以更加轻松地编写高质量的 React 应用程序。
03.React Hooks如何模拟组件的生命周期?
React Hooks提供了一些钩子函数,可以用来模拟组件的生命周期。
04.如何自定义Hook?
-
Hook应该只在函数最顶层调用,不要在循环、条件或嵌套函数中调用。
-
Hook应该只在React函数组件或自定义Hook中调用,不要在普通的JavaScript函数中调用。
05.说一下React Hooks性能优化
-
useMemo和useCallback:在需要进行计算操作的组件中,可以使用useMemo来避免重复计算;在需要传递函数的组件中,可以使用useCallback来返回一个memoized版本的函数,提升性能。
-
React.memo:使用React.memo来记忆组件渲染的结果,如果组件的props没有改变,则不再重新渲染组件,从而提高性能。
-
使用useRef:可以使用useRef来保存组件中的变量,避免每次组件渲染时都重新声明新的变量,提高性能。
-
使用useLayoutEffect:在需要操作DOM和布局的情况下,可以使用useLayoutEffect来确保操作在DOM更新之前完成,避免出现渲染延迟的问题,提高性能。
-
使用React.lazy和Suspense:在需要异步加载组件时,可以使用React.lazy和Suspense,当第一次渲染时,只加载必要的组件,避免一次性加载所有组件,提高性能。
06.使用React Hooks遇到哪些坑?
-
必须在函数组件的顶层使用:Hooks只能在函数组件的顶层使用,如果在条件语句或循环中使用会导致错误。这是因为Hooks的调用顺序必须保持一致,如果在组件的不同地方使用,就会导致调用顺序混乱。
-
useState初始值的问题:useState的初始值只会在第一次渲染时使用,后面的渲染会忽略它。因此,如果useState的初始值需要根据外部变量计算,需要使用useEffect来解决。
-
useEffect不依赖于某个变量时,需要传入空数组作为第二个参数:useEffect的第二个参数控制了什么时候该effect会被执行,如果传入一个空数组,表示该effect不依赖于任何变量,只有在组件挂载和卸载时才会执行。
-
useEffect不要在循环中使用:如果在循环中使用useEffect,会导致该effect被执行的次数过多,造成性能瓶颈。解决方法是将循环放在effect中,并使用useRef来记录循环的状态,避免重复执行。
-
useCallback和useMemo需要传入依赖变量:useCallback和useMemo都需要传入依赖变量,以控制什么时候重新计算结果。如果不传入依赖变量,会导致无法更新结果,或者结果频繁更新。
-
useContext使用时需要传入一个context对象:useContext需要传入一个context对象,否则会导致错误。在使用时,需要使用context对象的Provider组件将需要共享的状态包裹起来
07.Hooks相比HOC和Render Prop有哪些优点?
-
代码可读性更高:Hooks让逻辑更加清晰、易于理解和组合,顺序性代码有助于日后维护和排错。
-
更好的代码组织:使用Hooks可以将状态和副作用逻辑分离,使代码逻辑更加清晰和易于管理。
-
更少的代码量:相较于HOC和Render Props来说,Hooks代码更加简洁,没有需要额外的高阶组件或者组件渲染方法wrapper。
-
更好的性能:Hooks的实现方式与类组件不同,它们避免了不必要的render,并且可以完全避免不必要的生命周期方法等,从而提高了性能。
-
更便于单元测试:Hooks的逻辑是函数式的,可以将其视为纯函数进行单元测试
第三部分:webpack
01.前端为何要进行打包和构建?
-
减少 HTTP 请求次数:打包后的代码可以将多个文件合并成一个,减少了浏览器对服务器的请求,提高页面加载速度。
-
代码压缩:打包时可以进行代码压缩,减少代码体积,进一步提高页面加载速度。
-
格式转换和优化:通过构建工具可以将 ES6 以上的语法转换成 ES5 及以下的语法,解决了浏览器对新语法的兼容问题,并可以对代码进行优化,提高代码执行效率。
-
模块化管理:通过模块化管理可以方便地引入和管理各个模块,提高代码的可维护性和可读性。
-
自动化构建:通过自动化构建可以更方便地进行代码的打包、压缩、格式转换、模块化管理等操作,提高开发效率。
02.module chunk bundle的区别
- Module是代码的最小单位,每个文件都会被视为一个Module;
- Chunk是一组Module的集合,最终会被打包成一个文件,可以是异步加载的;
- Bundle是由一组Chunk组合而成的文件,用于最终的部署。
03.loader和plugin的区别
-
Loader:Webpack中的loader是一个转换器,用来将一种类型的文件转换成另一种类型的文件。它们对于构建系统的过程至关重要,因为它们允许你在打包模块的时候预处理文件。比如,将ES6的代码转换成ES5兼容的代码、将Sass编译成CSS、将图片转换成DataURL等等。Webpack中有许多内置的loader,你也可以自己编写自己的loader。
-
Plugin:Webpack中的plugin用于扩展Webpack的功能,通过在打包过程中注入钩子,来实现各种自定义的需求。它们可以用于压缩代码、提取公共代码、生成HTML文件、拷贝静态文件等等。和loader不同的是,plugin作用于整个打包过程,而不是单个文件。它们也可以被看作是打包过程中的事件监听器,能够在Webpack运行到某个特定的阶段时对其进行操作
04.常见的loader和plugin有哪些?
Loader:
- babel-loader:将ES6+的JavaScript代码转换为浏览器可识别的JavaScript代码。
- css-loader:用于处理css文件,支持css-modules、CSS代码压缩等功能。
- file-loader:用于处理文件,可将文件输出到输出目录并返回文件路径。
- url-loader:类似于file-loader,但可以将图片等文件转换为base64编码,减少http请求。
- style-loader:将css代码注入到HTML中,使其生效。
- sass-loader:用于编译Sass/Scss代码。
- less-loader:用于编译Less代码。
- postcss-loader:用于对css代码进行后处理,如添加浏览器兼容前缀、CSS代码压缩等。
Plugin:
- HtmlWebpackPlugin:用于生成HTML文件。
- CleanWebpackPlugin:用于每次构建前清除输出目录。
- MiniCssExtractPlugin:将css代码抽离为单独的文件,而不是注入到HTML中。
- UglifyJsPlugin:用于压缩JavaScript代码。
- DefinePlugin:用于定义全局变量,可以在代码中使用。
- CopyWebpackPlugin:用于拷贝静态文件到输出目录。
- HotModuleReplacementPlugin:用于热更新,使得修改代码后无需刷新页面即可生效。
05.Babel和webpack的区别?
Babel是一个JavaScript编译器,可以将ES6+代码转换为向后兼容的JavaScript代码,以便在旧浏览器上运行。它是一个独立的工具,可用于任何JavaScript项目中,并且不仅限于Web开发。Babel可以将新的ECMAScript语法转换成浏览器或Node.js可识别的语言。
Webpack是一个模块打包器,它可以将多个JavaScript文件和其他资源打包成一个或多个文件。Webpack可以处理各种资源类型,如JavaScript、CSS、图片、字体等,并将它们打包成单个文件。Webpack还可以通过“加载器”,将编译后的代码优化等,提高项目的性能和质量。06.Webpack如何产出一个lib库?
1.配置打包入口文件
2.配置输出文件的库名称
3.配置输出文件的库的导出方式
4.配置externals忽略打包
5.添加babel配置
6.添加minification配置
7.运行构建命令
07.说一下Babel-polyfill和babel-runtime的区别
Babel-polyfill :Babel-polyfill 是一个用于模拟完整的 ES2015+ 环境的库
Babel-runtime: Babel-runtime 是一个工具库,它可以将 ECMAScript 6+ 语法转换成 ES5 语法
08.Webpack如何实现懒加载?
Webpack 可以通过代码分割(code splitting)的方式实现懒加载。代码分割是一个将代码拆分成较小块的技术,每个块都可以独立加载,这样可以优化应用程序的加载速度。
09.为何Proxy不能被Polyfill?
Proxy是ES6中新增的一种语法,用于创建一个对象的代理,用以控制对目标对象的访问。而Polyfill是一种将新特性通过旧的语法和API模拟出来的技术,用于解决一些旧浏览器不支持新特性的问题。因为Proxy是一种新特性,它的用法和功能是在语言层面实现的,而Polyfill主要是通过模拟新特性的语法和API来实现。因此,Proxy不能被简单的Polyfill所模拟,必须要在语言层面进行支持才能使用。
10.Webpack如何优化构建速度?
-
升级Webpack版本:Webpack不断更新新版本,每个版本都有一些针对性的性能优化,因此升级Webpack版本是一个简单有效的优化方式。
-
缩小Webpack打包范围:优化Webpack的构建速度可以从缩小Webpack打包范围入手,可以通过配置Webpack的Entry、Output、Loader和Plugin等选项来缩小Webpack打包范围。
-
优化Webpack的Loader:在开发过程中,选择合适的Loader可以提高Webpack的构建速度。一些优化Loader的方式如下:
- 使用exclude和include选项排除不需要处理的文件或只处理需要处理的文件
- 使用cache选项启用缓存,避免重复处理相同文件
- 使用thread-loader和happypack等多线程处理工具,提高构建速度
- 优化Webpack的Plugin:除了Loader外,Plugin也可以帮助我们优化Webpack的构建速度。
- 使用HappyPack来并行处理任务提高构建速度
- 使用webpack-parallel-uglify-plugin来提高压缩速度
- 使用webpack.DllPlugin和webpack.DllReferencePlugin进行预编译,提高构建速度。
- 使用Webpack性能优化:有一些Webpack的选项可以在配置文件中开启以提高构建速度,如:
- 使用cheap-module-eval-source-map代替source-map提高构建速度
- 使用Tree shaking和Scope hoisting来减少代码体积,提高构建速度
11.Webpack如何优化产出代码?
-
代码压缩:Webpack 内置了 UglifyJSPlugin 插件,可以将 JavaScript 代码压缩,从而减小文件大小,提高加载速度。
-
CSS 提取:通过使用 MiniCssExtractPlugin 插件,可以将 CSS 文件单独提取出来,并压缩代码,减小文件大小,提高加载速度。
-
Tree Shaking:通过使用 Webpack 内置的 Tree Shaking 机制,可以剔除无用的代码,减小文件大小。
-
按需加载:通过使用 Webpack 的动态导入(Dynamic Import)功能,可以将页面中不必要的代码按需加载,避免一次性加载所有代码。
-
去除未使用的依赖项:通过使用 PurgeCSSPlugin 等插件,可以去除未使用的 CSS 样式,并通过使用 Bundle Analyzer 可以分析哪些依赖项并没有被使用,从而去除这些依赖项,减小文件大小。
-
代码拆分:通过使用 Webpack 的 SplitChunksPlugin 插件,可以将多个小型的 JavaScript 文件拆分成一个大型的文件,从而减少文件数量,提高加载速度。