1、简述React有什么特点?
React是一个用于构建用户界面的JavaScript库,由Facebook开发并维护。React有以下几个主要特点:
-
声明式设计: React采用声明式设计,让代码更易于理解,且方便调试。你只需描述出你希望程序的最终状态,React会自动确保用户界面与你描述的状态保持一致。
-
组件化: React基于组件构建界面,每个组件都可以包含自己的状态和逻辑,可以复用,这使得代码的组织和维护变得更简单。例如,一个购物网站的前端可以由“导航栏组件”、“商品列表组件”、“购物车组件”等多个组件构成。
-
虚拟DOM: React引入了虚拟DOM的概念,当组件的状态改变时,React会创建一个新的虚拟DOM,然后与旧的虚拟DOM进行对比,最后只更新真实DOM中差异的部分。这种方式大大提高了性能。
-
单向数据流: React的数据流动是单向的,从父组件流向子组件。这种方式使得组件的状态预测变得更加容易,同时也简化了应用的复杂度。
-
跨平台: React可以用于开发Web应用,同时,通过React Native,也可以开发iOS和Android应用,代码复用率高。
例如,如果我们要创建一个任务列表应用,每一个任务都可以是一个单独的组件,每个任务的完成状态、标题等就是它的状态。当我们添加一个新的任务时,React会创建一个新的虚拟DOM,然后与旧的虚拟DOM进行对比,最后只更新真实DOM中差异的部分,例如添加一个新的任务项。
2、简述虚拟DOM的概念和机制 ?
虚拟DOM(Virtual DOM)是React的一项核心特性,它是对真实DOM的抽象表达,是一个轻量级的JavaScript对象。
在React中,当组件的状态改变时,React并不会直接去更新真实的DOM。相反,它会创建一个新的虚拟DOM,这个虚拟DOM代表了在状态改变后应用的新状态。然后,React会将这个新的虚拟DOM与旧的虚拟DOM进行比较,这个过程叫做"diffing"。通过比较,React可以找出两个虚拟DOM之间的区别,也就是哪些部分需要更新。最后,React会将这些差异应用到真实的DOM上,这个过程叫做"reconciliation"或"协调"。这种方式称为DOM diffing算法。
虚拟DOM的主要优势在于其性能。操作真实DOM在浏览器中是非常消耗性能的。而使用虚拟DOM,React可以最小化真实DOM的更新,从而提高性能和应用的响应速度。
例如,假设我们有一个评论列表,当新的评论添加时,React会创建一个新的虚拟DOM,然后与旧的虚拟DOM进行比较,找出需要更新的部分(即新的评论),然后只将这个新的评论添加到真实DOM中,而不是整个评论列表都重新渲染。这样就大大提高了性能。
3、React 类组件和函数组件之间的区别是什么?
React提供了两种主要的方式来创建组件:类组件(Class Components)和函数组件(Function Components)。以下是它们之间的主要区别:
-
定义方式: 类组件是使用ES6的类来定义的,需要继承React.Component。而函数组件则是简单的JavaScript函数。
-
状态管理: 在React的早期版本中,类组件是唯一可以使用内部状态(state)的组件类型。函数组件是无状态的,只能接收props。但是从React 16.8版本开始,引入了Hooks这个新特性,使得函数组件也可以使用状态以及其他React特性了。
-
生命周期方法: 类组件提供了生命周期方法,如
componentDidMount
,componentDidUpdate
,componentWillUnmount
等。而在引入Hooks之前,函数组件无法使用这些生命周期方法。但是现在,通过使用useEffect
Hook,函数组件也可以模拟生命周期方法的行为。 -
使用方式: 类组件需要使用
this
关键字来访问props和状态,而函数组件则可以直接访问这些值。
例如,一个简单的类组件可以这样定义:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> {this.props.message} <button onClick={() => this.setState({ count: this.state.count + 1 })}> Count: {this.state.count} </button> </div> ); } }
同样的功能,使用函数组件和Hooks可以这样实现:
function MyComponent(props) { const [count, setCount] = React.useState(0); return ( <div> {props.message} <button onClick={() => setCount(count + 1)}> Count: {count} </button> </div> ); }
在这个例子中,类组件和函数组件都实现了同样的功能,即点击按钮时计数器增加。但是函数组件的代码更简洁,更易于理解。
4、简述React 中 refs 的作用 ?
Refs(引用)在React中是一种特殊的属性,可以用来访问和交互React元素或组件的DOM节点。虽然我们通常推荐使用React的声明式方式来管理应用程序的状态和DOM,但在某些情况下,我们可能需要直接操作DOM,这就是Refs的用武之地。
以下是Refs的一些常见用途:
-
管理焦点: 对于一些需要用户输入的元素(如input或textarea),Refs可以用来获取焦点。
-
触发强制动画: 有时候,我们可能需要直接操作DOM元素来触发动画。
-
集成第三方DOM库: 当需要和非React库(如jQuery插件)集成时,Refs可以用来直接操作DOM。
例如,如果我们创建一个自动获取焦点的输入框,我们可以使用Refs来实现:
class AutoFocusTextInput extends React.Component { constructor(props) { super(props); // 创建一个 ref 来存储 textInput 的 DOM 元素 this.textInput = React.createRef(); } componentDidMount() { // 使用原生的 DOM API 让 text input 元素获取焦点 this.textInput.current.focus(); } render() { // 把 `this.textInput` 指向 `<input>` DOM 节点 return ( <input type="text" ref={this.textInput} /> ); } }
在这个例子中,React.createRef()
被用来创建一个新的ref,然后在<input>
元素上通过ref
属性将其附加。这样,我们就可以在组件的其他地方(如生命周期方法)通过this.textInput.current
来访问这个DOM元素。
请注意,过度使用Refs可能会导致代码难以理解和维护,因此在大多数情况下,我们应优先考虑使用React的声明式模式。
5、简述React store的概念 ?
在React中,"store"通常指的是在使用Redux或MobX等状态管理库时用来管理应用全局状态的地方。
下面以Redux为例来解释一下store的概念:
在Redux中,store是一个JavaScript对象,它可以存储应用的整个状态树。Redux的store有以下几个主要的特性和功能:
-
维护应用的状态: store中包含了应用的整个状态,包括应用的数据以及UI的状态。
-
提供获取状态的方法: 可以通过store的
getState
方法来获取应用的当前状态。 -
提供更新状态的方法: 可以通过store的
dispatch
方法来分发(dispatch)action,更新应用的状态。这是改变store中状态的唯一方式。 -
注册和注销监听器: 可以通过store的
subscribe
方法来添加状态变化的监听器,当状态发生变化时,这些监听器会被调用。监听器可以通过返回的函数来注销。
例如,假设我们正在开发一个待办事项应用,我们可能会在store中存储一个待办事项列表的状态。当用户添加一个新的待办事项时,我们会分发一个action,这个action描述了这个变化(即添加一个新的待办事项)。然后,我们的reducer会接收到这个action,并根据这个action来更新store中的状态。当状态更新后,我们的UI会自动更新来反映这个新的状态。
// 创建一个 Redux store 来以存放应用的状态。 // API 是 { subscribe, dispatch, getState }。 let store = createStore(todoApp); // 可以手动订阅更新,也可以事件绑定到视图层。 store.subscribe(() => console.log(store.getState())); // 改变内部 state 惟一方法是 dispatch 一个 action。 // action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行 store.dispatch(addTodo('Learn about actions'));
Redux的store提供了一种集中管理和更新应用状态的方式,使得状态的管理变得可预测且易于理解。
6、请介绍React中的key有什么作用?
在React中,key
是一个特殊的字符串属性,你需要在创建元素数组时将其传递。在React的diff算法中,key
用于识别哪些元素发生了变化,哪些被添加或删除。
具体来说,当我们渲染一个元素列表时,React需要追踪每个元素的身份,以便在状态发生变化时能正确地更新和渲染元素。key
就是这个身份的标识。
例如,如果我们有一个待办事项列表,每次添加一个新的待办事项时,React需要确定是添加新的待办事项,还是重新排序现有的待办事项。如果每个待办事项都有一个稳定的、独一无二的key
,React就可以正确地识别和更新每个待办事项。
const todoItems = todos.map((todo) => <li key={todo.id}> {todo.text} </li> );
在这个例子中,todo.id
被用作每个元素的key
。这样,无论列表如何变化(添加、删除、重新排序等),React都可以通过key
来识别每个元素。
需要注意的是,尽管在很多情况下,你可能会被诱使使用元素的索引作为key
,但这通常是不推荐的。如果列表可以重新排序,这可能会导致性能降低,或者状态错误。除非你可以保证元素的顺序永远不会改变,否则最好使用一个唯一且稳定的标识符作为key
。
7、简述类组件和函数式组件的区别 ?
React提供了两种主要的方式来定义组件:类组件(Class Components)和函数组件(Function Components)。
类组件(Class Components)
类组件是使用ES6类来定义的,它必须扩展React.Component,并定义一个render方法,该方法返回一个React元素。类组件支持本地状态(也就是this.state
)和生命周期方法(如componentDidMount
)。
class Welcome extends React.Component { constructor(props) { super(props); this.state = { name: 'John' }; } render() { return <h1>Hello, {this.state.name}</h1>; } }
函数组件(Function Components)
函数组件是一个接收props并返回React元素的函数。在React 16.8及其之后的版本中,函数组件通过使用Hooks还可以支持本地状态和生命周期方法。
function Welcome(props) { const [name, setName] = React.useState('John'); return <h1>Hello, {name}</h1>; }
主要区别:
-
类组件需要使用
this
关键字来访问props和状态,而函数组件则可以直接访问这些值。 -
原来只有类组件可以使用的特性,比如状态和生命周期方法,现在函数组件通过Hooks也可以使用。
-
函数组件通常更简洁,更易于测试和理解。
-
类组件目前仍然支持一些尚未在函数组件中提供的特性,比如错误边界。
8、请列举React和vue.js的相似性和差异性 ?
React和Vue.js都是非常流行的前端JavaScript框架,它们有许多相似之处,但也有一些重要的差异。
相似性:
-
虚拟DOM: React和Vue.js都使用虚拟DOM来提高性能。虚拟DOM是真实DOM的抽象,只有当虚拟DOM与真实DOM有差异时,才进行最小化的DOM更新。
-
组件化: 两者都推崇组件化的开发模式,通过组合不同的组件来创建复杂的用户界面。
-
响应式: 两者都实现了响应式数据流,当数据发生变化时,框架会自动更新DOM。
-
单向数据流: 在组件层级中,两者都实现了单向数据流。这使得状态管理更加可预测,有利于应用的维护和理解。
差异性:
-
学习曲线: Vue.js通常被认为比React更容易上手。Vue的API较为简单直观,且提供了更多的内置功能和指令,使得开发者可以更快地上手开发。而React的学习曲线相对更陡峭一些,特别是当涉及到高级特性如Hooks和Context时。
-
编程范式: React更倾向于函数式编程,推崇不可变性和纯函数。Vue则更接近于经典的MVVM模式,更加灵活。
-
模板语法: Vue使用基于HTML的模板语法,可以直接在模板中写逻辑,较为直观。而React则使用JSX,这是一种JavaScript和HTML混写的语法,需要一些时间去适应。
-
社区和生态系统: React由Facebook维护,拥有大量的用户和丰富的第三方库。Vue.js虽然社区规模小一些,但发展非常活跃,且核心库和工具的集成度更高。
-
设计哲学: React倾向于提供较少的内置功能,但提供更多的灵活性,让开发者可以选择最适合他们的解决方案。而Vue则试图提供一个更完善的解决方案,内置了更多的功能,如动画支持、路由等。
选择React还是Vue,很大程度上取决于你的项目需求和团队的技术背景。
9、React中什么是受控组件和非控组件?
在React中,表单元素的行为分为两种类型:受控组件(Controlled Components)和非受控组件(Uncontrolled Components)。
受控组件:在受控组件中,表单数据由React组件的state管理。也就是说,表单输入的值会与组件的state同步,每当状态变化时,都会触发一个函数(通常是onChange),然后在该函数中更新状态。这样,React组件就始终控制了输入的状态。
例如,一个简单的受控组件可能是这样的:
class ControlledForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } render() { return ( <form> <label> Name: <input type="text" value={this.state.value} onChange={this.handleChange} /> </label> <input type="submit" value="Submit" /> </form> ); } }
非受控组件:非受控组件则是让表单数据由DOM自身来管理,而不是存储在React组件的state中。通过使用ref来从DOM元素中获取表单值,而不是为每次键盘敲击都编写事件处理程序。
例如,一个简单的非受控组件可能是这样的:
class UncontrolledForm extends React.Component { constructor(props) { super(props); this.handleSubmit
10、Redux 中间件是怎么拿到store 和 action? 然后怎么处理?
Redux 中间件的设计是基于高阶函数和链式调用的概念。中间件的基本形式是一个函数,这个函数返回另一个函数,这个返回的函数再返回一个函数,这样形成了一个闭包。
这里先看一下标准的Redux中间件的基本形式:
const middleware = store => next => action => { // 在这里处理你的代码 }
-
store
:Redux的store实例,你可以调用store.getState()获取当前的state,或者store.dispatch()派发一个新的action。 -
next
:这是一个函数,你可以调用next(action)来把控制权交给下一个中间件,如果没有下一个中间件,那么控制权就交给Redux。 -
action
:这是当前派发的action。
中间件的处理流程通常是这样的:
-
中间件拿到当前的
action
和store
。 -
根据需要,中间件可以在
action
被传递到reducer之前,修改action
,拦截action
,或者在action
被处理后做一些额外的操作。 -
中间件处理完后,通过
next(action)
将action
传递给下一个中间件,如果没有下一个中间件,那么action
将被传递给reducer。
例如,一个简单的中间件,用于在console里记录每个action和state的变化:
const logger = store => next => action => { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result }
在这个例子中,每当一个action被派发,我们都会在console里记录这个action,然后调用next(action)
将action传递给下一个中间件或者reducer,然后再记录新的state。
11、React Hook 的使用限制有哪些?
React Hook在使用过程中确实有一些限制和规则要遵守,主要有以下几点:
-
只在最顶层使用Hook:不要在循环,条件判断或者子函数中调用Hook。确保总是在你的React函数的最顶层调用他们。遵守这个规则,你可以确保Hooks在每一次渲染中都在同一个顺序被调用。这让React能够在多次的useState和useEffect调用之间保持正确的状态。
-
只在React函数中调用Hook:不要在普通的JavaScript函数中调用Hook。你可以在React的函数组件中调用Hook,也可以在你的自定义Hook中调用其他Hook。
-
在自定义Hook中使用Hook:当你想在两个函数间共享一些状态逻辑时,你可以创建你自己的自定义Hook,然后在那里调用其他的Hook。
这些限制主要是为了保证React能正确地追踪各个Hook之间的状态。不遵守这些规则可能会导致复杂且难以追踪的bug。为了帮助开发者遵守这些规则,React团队提供了一个ESLint插件:eslint-plugin-react-hooks,这个插件可以帮助检测你的代码是否违反了这些规则。
12、React Hooks在平时开发中需要注意的问题和原因 ?
在使用React Hooks时,除了官方提出的使用规则,还有一些其他需要注意的事项:
-
避免过度使用useEffect:有时候我们可能会过度使用useEffect,导致组件的渲染和重渲染过于频繁。我们需要明确地理解何时使用useEffect,尤其是它的依赖项数组。如果你的effect依赖于多个state或props,记得将它们全部包含在依赖项数组中,以避免不必要的副作用。
-
闭包问题:在使用useState和useEffect时,经常会遇到闭包问题。也就是说,当你在effect或事件处理函数中引用了state,你获取到的其实是当次渲染中的state值,而不是最新的state值。如果你需要获取最新的state值,可以考虑使用useRef或者函数形式的setState。
-
注意清理副作用:在使用useEffect处理诸如订阅事件、网络请求等副作用操作时,别忘了在effect返回的函数中进行清理,否则可能会导致内存泄漏。
-
自定义Hooks的命名要以“use”开头:这是一种约定俗成的规则,这样可以使得代码更清晰,更容易分辨出哪些函数是Hook。
-
避免在循环、条件或嵌套函数中调用Hook:这是由于React依赖于Hook调用的顺序来正确地保存内部state。如果我们在循环、条件或嵌套函数中调用Hook,可能会在多次渲染之间改变Hook的调用顺序,从而导致错误。
-
不要在函数组件体外部定义Hook:这也是为了保证Hook的调用顺序的一致性。
遵守以上规则和注意事项,可以帮助我们更好地使用React Hooks,编写出更健壮、更易于维护的代码。
13、React的严格模式如何使用,有什么用处?
React的严格模式是一种帮助你发现潜在问题的工具。要启用严格模式,你可以将应用程序或其部分包裹在<React.StrictMode>
标签中。这是一个不渲染任何可见UI的组件,只用于检查其子组件中的潜在问题。
import React from 'react'; function ExampleApplication() { return ( <React.StrictMode> <div> {/* Your application code goes here */} </div> </React.StrictMode> ); }
严格模式当前有助于检查以下方面的问题:
-
识别不安全的生命周期方法:如componentWillMount、componentWillReceiveProps和componentWillUpdate。这些生命周期方法在新版本的React中已经被废弃,严格模式会警告你不要使用它们。
-
关于使用过时或未预期的ref用法的警告:例如,确保你没有在函数组件上使用string ref。
-
检测意外的副作用:严格模式会在开发模式下,故意将生命周期方法如render、componentDidUpdate调用两次,以帮助你发现可能的副作用。注意,这只在开发模式下会发生。
-
检测过时的context API:旧的context API在新版本的React中已经被废弃,严格模式会警告你不要使用它。
需要注意的是,严格模式只检查开发环境下的应用程序。在生产环境中,它不会有任何影响,也不会消耗任何额外的资源。
14、State 是怎么注入到组件的,从 reducer 到组件经历了什么样的过程 ?
在React和Redux中,state的流动过程如下:
-
Dispatch Action:当用户交互或者某些事件触发时,你的应用会dispatch一个action。这个action是一个描述了发生了什么的普通对象。
-
Reducer处理Action:Redux store会调用你提供的reducer函数,传递当前的state和刚刚dispatch的action作为参数。Reducer是一个纯函数,它接收旧的state和action,然后返回新的state,描述了用户action如何改变state。
-
Store更新:Redux store保存了根reducer返回的整个state树。当新的state返回后,Redux store会更新state,并且会通知所有的监听器。
-
组件重新渲染:当store更新后,所有与state有关的组件都会重新渲染。在React和Redux应用中,你会使用
react-redux
库的Provider
组件将store提供给你的组件树,然后使用connect
函数将你的组件连接到Redux store。当state更新时,connect
函数会确保React组件接收到新的props并重新渲染。
通过这一系列过程,state被注入到React组件中,当state更新时,与之相关联的组件也会更新。
15、React state和props区别是什么 ?
在React中,state
和props
都是组件处理数据和交互的重要方式,但它们的用途和行为有一些重要的区别:
-
所有权:
state
是在组件内部自身管理的数据,可以被组件自身修改。而props
则是由父组件传递给子组件的数据,子组件只能读取props
,不能修改props
。 -
改变的方式:
state
可以通过组件内部的this.setState
方法进行改变,这会导致组件重新渲染。而props
只能通过父组件改变传递给子组件的值来改变,子组件不能直接修改props
。 -
数据的流动:
state
可以在组件内部流动,可以被传递给子组件作为props
,但是不能流动到父组件。而props
则可以在父子组件之间流动,父组件可以通过props
将数据传递给子组件。 -
用途:
state
通常用于存储组件的内部状态,比如用户输入、UI状态等需要响应用户交互改变的数据。而props
则用于父组件向子组件传递数据和回调函数。
简单来说,state
是让组件控制自己的状态,props
是让外部对组件自身进行配置。
16、简述什么是React 高阶组件?
高阶组件(Higher-Order Components,简称HOC)是React中用于复用组件逻辑的一种高级技术。它不是React的API的一部分,而是一种基于React的组合特性的设计模式。
高阶组件就是一个函数,接受一个组件作为参数,并返回一个新的组件。这个新的组件会使用原始的组件,并可以在其基础上添加新的props或者新的功能。
function higherOrderComponent(WrappedComponent) { return class extends React.Component { render() { return <WrappedComponent {...this.props} />; } } }
在这个例子中,higherOrderComponent
就是一个高阶组件。它接收一个组件WrappedComponent
,返回一个新的组件。新的组件会渲染WrappedComponent
,并且将自己接收的props传递给WrappedComponent
。
高阶组件的用途非常广泛,例如,可以用于控制props,抽象state,控制渲染等等。比如React-Redux的connect
函数就是一个高阶组件,它用于将React组件连接到Redux store,使得组件可以访问store中的state和dispatch函数。
17. 请简述useCallback 和 useMemo 的使用场景 ?
20. useEffect()的清除机制是什么?在什么时候执行?
21. useState()的 state 是否可以直接修改?是否可以引起组件渲染?
23. 请简述react-router 和 react-router-dom 的有什么区别?
24. 在 React中元素( element)和组件( component)有什么区别?
25. 约束性组件( controlled component)与非约束性组件( uncontrolled component)有什么区别?
26. React shouldComponentUpdate有什么用?为什么它很重要?
28. createElement和 cloneElement有什么区别?
29. React setState方法的第二个参数有什么用?使用它的目的是什么?
30. 请说岀 React从 ES5编程规范到 ES6 编程规范过程中的几点改变?
35. 简述如何使用4.0版本的 React Router?
37. 请用源码解释React setState 调用的原理 ?
38. 简述shouldComponentUpdate 作用?为什么它很重要?
41. 解释为什么调用 setState 而不是直接改变 state?
42. 解释 React 中 render() 的目的和作用 ?
44. 请说明React中getDefaultProps 的作用 ?
46. 简述React Hooks在平时开发中需要注意的问题和原因 ?
47. 在React中组件的this.state和setState有什么区别?
49. 简述React中hooks是如何模拟组件的生命周期的?
53. 执行两次setState的时候会render几次?会不会立即触发?
54. React 什么是 Reselect 以及它是如何工作的 ?
58. 请简述当调用setState时,React render 是如何工作的?
60. Component, Element, Instance 之间有什么区别和联系?
61. 简述React.createClass和extends Component的区别有哪些?
62. 简述对React中Fragment的理解,它的使用场景是什么?
66. 简述super()和super(props)有什么区别?
69. React.forwardRef是什么?它有什么作用?
70. React中constructor和getInitialState的区别?
72. React ⾼阶组件、Render props、hooks 有什么区别,为什么要 不断迭代 ?
73. 哪些方法会触发 React 重新渲染?重新渲染 render 会做些什么 ?
74. 简述为什么React并不推荐优先考虑使⽤Context?
75. 简述React中的setState和replaceState的区别是什么 ?
77. 在React中组件的props改变时更新组件的有哪些方法 ?
78. React 16.X 中 props 改变后在哪个生命周期中处理 ?
79. React 性能优化在哪个生命周期?它优化的原理是什么?
80. 简述state 和 props 触发更新的生命周期分别有什么区别?
84. 简述React-Router 4怎样在路由变化时重新渲染同⼀个组件 ?
87. Redux 中间件是什么?接受几个参数?柯里化函数两端的参数具体是什么 ?
89. 简述Redux 和 Vuex 有什么区别,它们的共同思想 ?
90. 简述Redux 中间件是怎么拿到store 和 action? 然后怎么处理 ?
91. 简述为什么 useState 要使用数组而不是对象 ?
95. 简述 React key 是干嘛用的 为什么要加?key 主要是解决哪⼀类问题的?
96. 简述React 与 Vue 的 diff 算法有何不同 ?
97. 简述 react 最新版本解决了什么问题,增加了哪些东⻄ ?
98. 简述在React中怎么使⽤async/await ?