React常见的面试题以及答案
说说对React的理解?有哪些特性?
区分Real DOM和Virtual DOM
什么是JSX和它的特性?
类组件和函数组件之间有什么区别?
了解 Virtual DOM 吗?解释一下它的工作原理。
说说对 State 和 Props的理解,有什么区别?
说说对React refs 的理解?应用场景?
setState是同步还是异步
super()和super(props)有什么区别?
说说对React事件机制的理解?
React事件绑定的方式有哪些?区别?
React组件生命周期有几个阶段
详细解释 React 组件的生命周期方法
react在哪个生命周期做优化
受控组件和非受控组件的区别
React组件事件代理的原理
为什么虚拟 dom 会提高性能
React中的key有什么作用?
react的diff算法是怎么完成的
react组件之间如何通信
什么是高阶组件?
说说对React Hooks的理解?解决了什么问题?
说说react中引入css的方式有哪几种?区别?
在React中组件间过渡动画如何实现?
React context是什么?
说说你对Redux的理解?其工作原理?
Redux遵循的三个原则是什么?
数据如何通过 Redux 流动?
react-redux的两个最主要功能?
Redux中异步的请求怎么处理
介绍Redux中间件
你在React项目中是如何使用Redux的? 项目结构是如何划分的?
为什么 React Router 中使用 Switch 关键字 ?
说说对React的理解?有哪些特性?
是什么
React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案,遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效,使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流,帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面.
特性
JSX语法
单向数据绑定
虚拟DOM
声明式编程
Component(组件化)
优势
高效灵活
声明式的设计,简单使用
组件式开发,提高代码复用率
单向响应的数据流会比双向绑定的更安全,速度更快
区分Real DOM和Virtual DOM
Real DOM
Real DOM,真实DOM, 意思为文档对象模型,是一个结构化文本的抽象,在页面渲染出的每一个结点都是一个真实DOM结构
更新缓慢
可以直接更新 HTML
如果元素更新,则创建新DOM
DOM操作代价很高
消耗的内存较多
Virtual DOM
Virtual Dom,本质上是以 JavaScript 对象形式存在的对 DOM 的描述。创建虚拟DOM目的就是为了更好将虚拟的节点渲染到页面视图中,虚拟DOM对象的节点与真实DOM的属性一一照应
更新更快
无法直接更新 HTML
如果元素更新,则更新 JSX
DOM 操作非常简单
很少的内存消耗
什么是JSX和它的特性?
JSX 是JavaScript XML的缩写,不是html或xml,基于ECMAScript的一种新特性,一种定义带属性树结构的语法;
特性:
自定义组件名首字母大写
嵌套;在render函数中return返回的只能包含一个顶层标签,否则也会报错。
求值表达式;JSX基本语法规则,遇到HTML标签(以<开头),就用HTML规则解析;遇到代码块(以{开头),就用JS规则解析
驼峰命名
class属性需要写成className
JSX允许直接在模板插入JS变量。如果这个变量是一个数组,则会展开这个数组的所有成员
在JSX中插入用户输入是安全的,默认情况下ReactDOM会在渲染前,转义JSX中的任意值,渲染前,所有的值都被转化为字符串形式,这能预防XSS攻击。
类组件和函数组件之间有什么区别?
类组件:
无论是使用函数或是类来声明一个组件,它决不能修改它自己的 props。
所有 React 组件都必须是纯函数,并禁止修改其自身 props。
React是单项数据流,父组件改变了属性,那么子组件视图会更新。
属性 props是外界传递过来的,状态 state是组件本身的,状态可以在组件中任意修改
组件的属性和状态改变都会更新视图。
函数组件:
函数组件接收一个单一的 props 对象并返回了一个React元素
函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
了解 Virtual DOM 吗?解释一下它的工作原理。
Virtual DOM 是一个轻量级的 JavaScript 对象,它最初只是 real DOM 的副本。它是一个节点树,它将元素、它们的属性和内容作为对象及其属性。 React 的渲染函数从 React 组件中创建一个节点树。然后它响应数据模型中的变化来更新该树,该变化是由用户或系统完成的各种动作引起的。
Virtual DOM 工作过程有三个简单的步骤:
每当底层数据发生改变时,整个 UI 都将在 Virtual DOM 描述中重新渲染。
然后计算之前 DOM 表示与新表示的之间的差异。
完成计算后,将只用实际更改的内容更新 real DOM。
说说对 State 和 Props的理解,有什么区别?
State
一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state,一般在 constructor 中初始化
当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用,并且重新调用组件render方法
setState还可以接受第二个参数,它是一个函数,会在setState调用完成并且组件开始重新渲染时被调用,可以用来监听渲染是否完成
Props
React的核心思想就是组件化思想,页面会被切分成一些独立的、可复用的组件,组件从概念上看就是一个函数,可以接受一个参数作为输入值,这个参数就是props,所以可以把props理解为从外部传入组件内部的数据
react具有单向数据流的特性,所以他的主要作用是从父组件向子组件中传递数据
props除了可以传字符串,数字,还可以传递对象,数组甚至是回调函数
在子组件中,props在内部不可变的,如果想要改变它看,只能通过外部组件传入新的props来重新渲染子组件,否则子组件的props和展示形式不会改变
相同点
两者都是 JavaScript 对象
两者都是用于保存信息
props 和 state 都能触发渲染更新
区别
props 是外部传递给组件的,而 state 是在组件内被组件自己管理的,一般在 constructor 中初始化
props 在组件内部是不可修改的,但 state 在组件内部可以进行修改
state 是多变的、可以修改
说说对React refs 的理解?应用场景?
是什么
React 中的 Refs提供了一种方式,允许我们访问 DOM节点或在 render方法中创建的 React元素。
本质为ReactDOM.render()返回的组件实例,如果是渲染组件则返回的是组件实例,如果渲染dom则返回的是具体的dom节点。
如何使用
传入字符串,使用时通过 this.refs.传入的字符串的格式获取对应的元素
传入对象,对象是通过 React.createRef() 方式创建出来,使用时获取到创建的对象中存在 current 属性就是对应的元素
传入函数,该函数会在 DOM 被挂载时进行回调,这个函数会传入一个 元素对象,可以自己保存,使用时,直接拿到之前保存的元素对象即可
传入hook,hook是通过 useRef() 方式创建,使用时通过生成hook对象的 current 属性就是对应的元素
应用场景
在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,过多使用refs,会使组件的实例或者是DOM结构暴露,违反组件封装的原则;
但下面的场景使用refs非常有用:
对Dom元素的焦点控制、内容选择、控制
对Dom元素的内容设置及媒体播放
对Dom元素的操作和对组件实例的操作
集成第三方 DOM 库
setState是同步还是异步
setState本身并不是异步,之所以会有一种异步方法的表现形式,归根结底还是因为react框架本身的性能机制所导致的。因为每次调用setState都会触发更新,异步操作是为了提高性能,将多个状态合并一起更新,减少re-render调用。
实现同步:
setState提供了一个回调函数供开发者使用,在回调函数中,我们可以实时的获取到更新之后的数据。
state = {
number:1
};
componentDidMount(){
this.setState({number:3},()=>{
console.log(this.state.number) // 3
})
}
利用setTimeout
state = {
number:1
};
componentDidMount(){
setTimeout(()=>{
this.setState({number:3})
console.log(this.state.number) //3
},0)
}
还有在原生事件环境下
state = {
number:1
};
componentDidMount() {
document.body.addEventListener(‘click’, this.changeVal, false);
}
changeVal = () => {
this.setState({
number: 3
})
console.log(this.state.number) //3
}
super()和super(props)有什么区别?
在ES6中,通过extends关键字实现类的继承,super关键字实现调用父类,super代替的是父类的构建函数,使用super(xx)相当于调用sup.prototype.constructor.call(this.xx),如果在子类中不使用super关键字,则会引发报错
super()就是将父类中的this对象继承给子类的,没有super()子类就得不到this对象
在React中,类组件是基于es6的规范实现的,继承React.Component,因此如果用到constructor就必须写super()才初始化this,在调用super()的时候,我们一般都需要传入props作为参数,如果不传进去,React内部也会将其定义在组件实例中,所以无论有没有constructor,在render中this.props都是可以使用的,这是React自动附带的,但是也不建议使用super()代替super(props),因为在React会在类组件构造函数生成实例后再给this.props赋值,所以在不传递props在super的情况下,调用this.props为undefined,而传入props的则都能正常访问,确保了 this.props 在构造函数执行完毕之前已被赋值,更符合逻辑
总结
在React中,类组件基于ES6,所以在constructor中必须使用super
在调用super过程,无论是否传入props,React内部都会将porps赋值给组件实例porps属性中
如果只调用了super(),那么this.props在super()和构造函数结束之间仍是undefined
说说对React事件机制的理解?
是什么
React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等,在React中这套事件机制被称之为合成事件;
合成事件是 React模拟原生 DOM事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器
执行顺序
React 所有事件都挂载在 document 对象上
当真实 DOM 元素触发事件,会冒泡到 document 对象后,再处理 React 事件
所以会先执行原生事件,然后处理 React 事件
最后真正执行 document 上挂载的事件
总结
React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
React 有一套自己的合成事件 SyntheticEvent
了解更多详情请点击React事件机制的理解
React事件绑定的方式有哪些?区别?
render方法中使用bind
render方法中使用箭头函数
constructor中bind
定义阶段使用箭头函数绑定
class App extends React.Component {
handleClick() {
console.log('this > ', this);
}
render() {
return (
{/ 1.render方法中使用bind/}
test
{/2.render方法中使用箭头函数 /}
<div onClick={e => this.handleClick(e)}>test
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
//3.constructor中bind
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('this > ', this);
}
render() {
return (
test
)
}
}
class App extends React.Component {
//4.定义阶段使用箭头函数绑定
handleClick = () => {
console.log('this > ', this);
}
render() {
return (
test
)
}
}
区别
编写方面:方式一、方式二写法简单,方式三的编写过于冗杂
性能方面:方式一和方式二在每次组件render的时候都会生成新的方法实例,性能问题欠缺。若该函数作为属性值传给子组件的时候,都会导致额外的渲染。而方式三、方式四只会生成一个方法实例
综合上述,方式四(箭头函数绑定)是最优的事件绑定方式
React组件生命周期有几个阶段
初始渲染阶段:这是组件即将开始其生命之旅并进入 DOM 的阶段。
getDefaultProps:获取实例的默认属性
getInitialState:获取每个实例的初始化状态
componentWillMount:组件即将被装载、渲染到页面上
render:组件在这里生成虚拟的 DOM 节点
componentDidMount:组件真正在被装载之后
更新阶段:一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。
componentWillReceiveProps:组件将要接收到属性的时候调用
shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
componentWillUpdate:组件即将更新不能修改属性和状态
render:组件重新描绘
componentDidUpdate:组件已经更新
卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除。
componentWillUnmount:组件即将销毁
详细解释 React 组件的生命周期方法
componentWillMount() – 在渲染之前执行,在客户端和服务器端都会执行。
componentDidMount() – 仅在第一次渲染后在客户端执行。
componentWillReceiveProps() – 当从父类接收到 props 并且在调用另一个渲染器之前调用。
shouldComponentUpdate() – 根据特定条件返回 true 或 false。如果你希望更新组件,请返回true ,不想更新组件则返回 false就会阻止render渲染。默认情况下,它返回 true。
componentWillUpdate() – 在 DOM 中进行渲染之前调用。
componentDidUpdate() – 在渲染发生后立即调用。
componentWillUnmount() – 从 DOM 卸载组件后调用。用于清理内存空间。
react在哪个生命周期做优化
shouldComponentUpdate,这个方法用来判断是否需要调用 render 方法重绘 dom。
因为 dom 的描绘非常消耗性能,如果我们能在这个方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
点击React学习笔记四——受控组件和非受控组件查看详解
受控组件和非受控组件的区别
受控组件是React控制的组件,input等表单输入框值不存在于 DOM 中,而是以我们的组件状态存在。每当我们想要更新值时,我们就像以前一样调用setState。
不受控制组件是您的表单数据由 DOM 处理,而不是React 组件,Refs 用于获取其当前值;
React组件事件代理的原理
和原生HTML定义事件的唯一区别就是JSX采用驼峰写法来描述事件名称,大括号中仍然是标准的JavaScript表达式,返回一个事件处理函数。在JSX中你不需要关心什么时机去移除事件绑定,因为React会在对应的真实DOM节点移除时就自动解除了事件绑定。
React并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点document上为每种事件添加唯一的Listener,然后通过事件的target找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React都会触发对应的事件处理函数。这就是所谓的React模拟事件系统。
为什么虚拟 dom 会提高性能
虚拟dom(virtual dom) 是 JS对象,是一个真实dom的JS对象;虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。
React中的key有什么作用?
跟Vue一样,React 也存在diff算法,而元素key属性的作用是用于判断元素是新创建的还是被移动的元素,从而减少不必要的Diff,因此key的值需要为每一个元素赋予一个确定的标识。
如果列表数据渲染中,在数据后面插入一条数据,key作用并不大;前面的元素在diff算法中,前面的元素由于是完全相同的,并不会产生删除创建操作,在最后一个比较的时候,则需要插入到新的DOM树中。因此,在这种情况下,元素有无key属性意义并不大。
如果列表数据渲染中,在前面插入数据时,当拥有key的时候,react根据key属性匹配原有树上的子元素以及最新树上的子元素,只需要将元素插入到最前面位置,当没有key的时候,所有的li标签都需要进行修改
并不是拥有key值代表性能越高,如果说只是文本内容改变了,不写key反而性能和效率更高,主要是因为不写key是将所有的文本内容替换一下,节点不会发生变化,而写key则涉及到了节点的增和删,发现旧key不存在了,则将其删除,新key在之前没有,则插入,这就增加性能的开销
总结
良好使用key属性是性能优化的非常关键的一步,注意事项为:
key 应该是唯一的
key不要使用随机值(随机数在下一次 render 时,会重新生成一个数字)
避免使用 index 作为 key
react的diff算法是怎么完成的
把树形结构按照层级分解,只比较同级元素
通过给列表结构的每个单元添加的唯一 key值进行区分同层次的子节点的比较。
React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty. 到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制。
选择性渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。
在这里插入图片描述
react组件之间如何通信
父子:父传子:props; 子传父:子调用父组件中的函数并传参;
兄弟:利用redux实现和利用父组件
所有关系都通用的方法:利用PubSub.js订阅
点击React学习笔记十二——组件之间的通信方式查看详细内容
什么是高阶组件?
高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。基本上,这是从React的组成性质派生的一种模式,我们称它们为“纯”组件, 因为它们可以接受任何动态提供的子组件,但它们不会修改或复制其输入组件的任何行为。
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧
高阶组件的参数为一个组件返回一个新的组件
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件
点击对高阶组件的理解查看详解
说说对React Hooks的理解?解决了什么问题?
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性,因此,现在的函数组件也可以是有状态的组件,内部也可以维护自身的状态以及做一些逻辑方面的处理;
最常见的hooks有:useState、useEffect
hooks的出现,使函数组件的功能得到了扩充,拥有了类组件相似的功能;
点击React学习笔记十一——扩展知识点(setState / lazyLoad / Hook / Fragment / Context)查看其中的Hook知识点详解
说说react中引入css的方式有哪几种?区别?
组件式开发选择合适的css解决方案尤为重要,而在react中,引入CSS就不如Vue方便简洁,其引入css的方式有很多种,各有利弊;
在组件内直接使用
组件中引入 .css 文件
组件中引入 .module.css 文件
CSS in JS
点击 react中引入css的方式查看以上几种引入方式的代码案例
区别
在组件内直接使用css该方式编写方便,容易能够根据状态修改样式属性,但是大量的演示编写容易导致代码混乱
组件中引入 .css 文件符合我们日常的编写习惯,但是作用域是全局的,样式之间会层叠
引入.module.css 文件能够解决局部作用域问题,但是不方便动态修改样式,需要使用内联的方式进行样式的编写
通过css in js 这种方法,可以满足大部分场景的应用,可以类似于预处理器一样样式嵌套、定义、修改状态等
至于使用react用哪种方案引入css,并没有一个绝对的答案,可以根据各自情况选择合适的方案
在React中组件间过渡动画如何实现?
在react中,react-transition-group是一种很好的解决方案,其为元素添加enter,enter-active,exit,exit-active这一系列勾子,可以帮助我们方便的实现组件的入场和离场动画
其主要提供了三个主要的组件:
CSSTransition:在前端开发中,结合 CSS 来完成过渡动画效果
SwitchTransition:两个组件显示和隐藏切换时,使用该组件
TransitionGroup:将多个动画组件包裹在其中,一般用于列表中元素的动画
在React中组件间过渡动画查看以上过度组件案例详解
React context是什么?
简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。
在这里插入图片描述
使用props或者state传递数据,数据自顶下流。
在这里插入图片描述
使用Context,可以跨越组件进行数据传递。
说说你对Redux的理解?其工作原理?
redux将所有的状态进行集中管理,当需要更新状态的时候,仅需要对这个管理集中处理,而不用去关心状态是如何分发到每一个组件内部的,redux是一个实现集中管理的容器,遵循三大基本原则:单一数据源、state 是只读的、使用纯函数来执行修改;
注意的是,redux并不是只应用在react中,还与其他界面库一起使用,如Vue;
工作原理
redux要求我们把数据都放在 store公共存储空间,一个组件改变了 store 里的数据内容,其他组件就能感知到 store的变化,再来取数据,从而间接的实现了这些数据传递的功能
工作流程图如下所示:
在这里插入图片描述
根据流程图,可以想象,React Components 是借书的用户, Action Creactor 是借书时说的话(借什么书), Store 是图书馆管理员,Reducer 是记录本(借什么书,还什么书,在哪儿,需要查一下), state 是书籍信息;
整个流程就是借书的用户需要先存在,然后需要借书,需要一句话来描述借什么书,图书馆管理员听到后需要查一下记录本,了解图书的位置,最后图书馆管理员会把这本书给到这个借书人
转换为代码是,React Components 需要获取一些数据, 然后它就告知 Store 需要获取数据,这就是就是 Action Creactor , Store 接收到之后去 Reducer 查一下, Reducer 会告诉 Store 应该给这个组件什么数据
如何使用
点击Redux的理解和使用查看详解和使用案例
Redux遵循的三个原则是什么?
单一事实来源:整个应用的状态存储在单个 store 中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序。
状态是只读的:改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示。
使用纯函数进行更改:为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数。
数据如何通过 Redux 流动?
在这里插入图片描述
react-redux的两个最主要功能
Provider:提供包含store的context
connect:连接容器组件和傻瓜组件;connect接收两个参数mapStateToProps和mapDispatchToProps。mapStateToProps把Store上的状态转化为内层傻瓜组件的prop,mapDispatchToProps把内层傻瓜组件中的用户动作转化为派送给Store的动作
Redux中异步的请求怎么处理
redux使用redux-thunk中间件处理异步状态管理请求;redux-thunk 允许我们 dispatch 一个包含异步处理逻辑函数(thunk);我们可以借助这种简单的机制在 redux 中处理异步逻辑;
介绍Redux中间件
什么是中间件:
中间件其实就是一个函数,中间件允许我们扩展redux应用程序 。具体体现在对action的处理能力上,当组件触发一个action后,这个action会优先被中间件处理,当中间件处理完后,中间件再把action传递给reducer,让reducer继续处理这个action
加入中间件的redux工作流程:
在这里插入图片描述
常用中间件:
redux-thunk
redux-saga
redux-actions
你在React项目中是如何使用Redux的 项目结构是如何划分的?
在Redux介绍中,我们了解到redux是用于数据状态管理,而react是一个视图层面的库。如果将两者连接在一起,可以使用官方推荐react-redux库,其具有高效且灵活的特性;
react-redux将组件分成:
容器组件:存在逻辑处理
UI 组件:只负责现显示和交互,内部不处理逻辑,状态由外部控制
通过redux将整个应用状态存储到store中,组件可以派发dispatch行为action给store,其他组件通过订阅store中的状态state来更新自身的视图
点击如何使用和项目结构划分查看详解
为什么 React Router 中使用 Switch 关键字 ?
由于router和switch对于路由的渲染策略不同,对router来说,如果有的链接既可以被路由A匹配,又可以被路由B匹配,那么Router会同时渲染它们
对于switch来说,它只会渲染符合条件的第一个路径,避免重复匹配
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。