当调用setState
时,React render
是如何工作的?
咱们能够将"render
"分为两个步骤:
- 虚构 DOM 渲染:当
render
办法被调用时,它返回一个新的组件的虚构 DOM 构造。当调用setState()
时,render
会被再次调用,因为默认状况下shouldComponentUpdate
总是返回true
,所以默认状况下 React 是没有优化的。 - 原生 DOM 渲染:React 只会在虚构DOM中批改实在DOM节点,而且批改的次数非常少——这是很棒的React个性,它优化了实在DOM的变动,使React变得更快。
什么是 Props
Props 是 React 中属性的简写。它们是只读组件,必须放弃纯,即不可变。它们总是在整个利用中从父组件传递到子组件。子组件永远不能将 prop 送回父组件。这有助于保护单向数据流,通常用于出现动静生成的数据。
React.Component 和 React.PureComponent 的区别
PureComponent示意一个纯组件,能够用来优化React程序,缩小render函数执行的次数,从而进步组件的性能。
在React中,当prop或者state发生变化时,能够通过在shouldComponentUpdate生命周期函数中执行return false来阻止页面的更新,从而缩小不必要的render执行。React.PureComponent会主动执行 shouldComponentUpdate。
不过,pureComponent中的 shouldComponentUpdate() 进行的是浅比拟,也就是说如果是援用数据类型的数据,只会比拟不是同一个地址,而不会比拟这个地址外面的数据是否统一。浅比拟会疏忽属性和或状态渐变状况,其实也就是数据援用指针没有变动,而数据产生扭转的时候render是不会执行的。如果须要从新渲染那么就须要从新开拓空间援用数据。PureComponent个别会用在一些纯展现组件上。
应用pureComponent的益处:当组件更新时,如果组件的props或者state都没有扭转,render函数就不会触发。省去虚构DOM的生成和比照过程,达到晋升性能的目标。这是因为react主动做了一层浅比拟。
React组件的构造函数有什么作用?它是必须的吗?
构造函数次要用于两个目标:
- 通过将对象调配给this.state来初始化本地状态
- 将事件处理程序办法绑定到实例上
所以,当在React class中须要设置state的初始值或者绑定事件时,须要加上构造函数,官网Demo:
class LikeButton extends React.Component { constructor() { super(); this.state = { liked: false }; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState({liked: !this.state.liked}); } render() { const text = this.state.liked ? 'liked' : 'haven\'t liked'; return ( <div onClick={this.handleClick}> You {text} this. Click to toggle. </div> ); }}ReactDOM.render( <LikeButton />, document.getElementById('example'));
构造函数用来新建父类的this对象;子类必须在constructor办法中调用super办法;否则新建实例时会报错;因为子类没有本人的this对象,而是继承父类的this对象,而后对其进行加工。如果不调用super办法;子类就得不到this对象。
留神:
- constructor () 必须配上 super(), 如果要在constructor 外部应用 this.props 就要 传入props , 否则不必
- JavaScript中的 bind 每次都会返回一个新的函数, 为了性能等思考, 尽量在constructor中绑定事件
React组件的state和props有什么区别?
(1)props
props是一个从内部传进组件的参数,次要作为就是从父组件向子组件传递数据,它具备可读性和不变性,只能通过内部组件被动传入新的props来从新渲染子组件,否则子组件的props以及展示模式不会扭转。
(2)state
state的次要作用是用于组件保留、管制以及批改本人的状态,它只能在constructor中初始化,它算是组件的公有属性,不可通过内部拜访和批改,只能通过组件外部的this.setState来批改,批改state属性会导致组件的从新渲染。
(3)区别
- props 是传递给组件的(相似于函数的形参),而state 是在组件内被组件本人治理的(相似于在一个函数内申明的变量)。
- props 是不可批改的,所有 React 组件都必须像纯函数一样爱护它们的 props 不被更改。
- state 是在组件中创立的,个别在 constructor中初始化 state。state 是多变的、能够批改,每次setState都异步更新的。
useEffect 与 useLayoutEffect 的区别
(1)共同点
- 使用成果: useEffect 与 useLayoutEffect 两者都是用于解决副作用,这些副作用包含扭转 DOM、设置订阅、操作定时器等。在函数组件外部操作副作用是不被容许的,所以须要应用这两个函数去解决。
- 应用形式: useEffect 与 useLayoutEffect 两者底层的函数签名是完全一致的,都是调用的 mountEffectImpl办法,在应用上也没什么差别,根本能够间接替换。
(2)不同点
- 应用场景: useEffect 在 React 的渲染过程中是被异步调用的,用于绝大多数场景;而 useLayoutEffect 会在所有的 DOM 变更之后同步调用,次要用于解决 DOM 操作、调整款式、防止页面闪动等问题。也正因为是同步解决,所以须要防止在 useLayoutEffect 做计算量较大的耗时工作从而造成阻塞。
- 应用成果: useEffect是依照程序执行代码的,扭转屏幕像素之后执行(先渲染,后扭转DOM),当扭转屏幕内容时可能会产生闪动;useLayoutEffect是扭转屏幕像素之前就执行了(会推延页面显示的事件,先扭转DOM后渲染),不会产生闪动。useLayoutEffect总是比useEffect先执行。
在将来的趋势上,两个 API 是会长期共存的,临时没有删减合并的打算,须要开发者依据场景去自行抉择。React 团队的倡议十分实用,如果切实分不清,先用 useEffect,个别问题不大;如果页面有异样,再间接替换为 useLayoutEffect 即可。
对React的插槽(Portals)的了解,如何应用,有哪些应用场景
React 官网对 Portals 的定义:
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优良的计划
Portals 是React 16提供的官网解决方案,使得组件能够脱离父组件层级挂载在DOM树的任何地位。艰深来讲,就是咱们 render 一个组件,但这个组件的 DOM 构造并不在本组件内。
Portals语法如下:
ReactDOM.createPortal(child, container);
- 第一个参数 child 是可渲染的 React 子项,比方元素,字符串或者片段等;
- 第二个参数 container 是一个 DOM 元素。
个别状况下,组件的render函数返回的元素会被挂载在它的父级组件上:
import DemoComponent from './DemoComponent';render() { // DemoComponent元素会被挂载在id为parent的div的元素上 return ( <div id="parent"> <DemoComponent /> </div> );}
然而,有些元素须要被挂载在更高层级的地位。最典型的利用场景:当父组件具备overflow: hidden
或者z-index
的款式设置时,组件有可能被其余元素遮挡,这时就能够思考要不要应用Portal使组件的挂载脱离父组件。例如:对话框,模态窗。
import DemoComponent from './DemoComponent';render() { // DemoComponent元素会被挂载在id为parent的div的元素上 return ( <div id="parent"> <DemoComponent /> </div> );}
为什么 React 要用 JSX?
JSX 是一个 JavaScript 的语法扩大,或者说是一个相似于 XML 的 ECMAScript 语法扩大。它自身没有太多的语法定义,也不冀望引入更多的规范。
其实 React 自身并不强制应用 JSX。在没有 JSX 的时候,React 实现一个组件依赖于应用 React.createElement 函数。代码如下:
class Hello extends React.Component { render() { return React.createElement( 'div', null, `Hello ${this.props.toWhat}` ); }}ReactDOM.render( React.createElement(Hello, {toWhat: 'World'}, null), document.getElementById('root'));
而 JSX 更像是一种语法糖,通过相似 XML 的形容形式,刻画函数对象。在采纳 JSX 之后,这段代码会这样写:
class Hello extends React.Component { render() { return <div>Hello {this.props.toWhat}</div>; }}ReactDOM.render( <Hello toWhat="World" />, document.getElementById('root'));
通过比照,能够清晰地发现,代码变得更为简洁,而且代码构造档次更为清晰。
因为 React 须要将组件转化为虚构 DOM 树,所以在编写代码时,实际上是在手写一棵构造树。而XML 在树结构的形容上天生具备可读性强的劣势。
但这样可读性强的代码仅仅是给写程序的同学看的,实际上在运行的时候,会应用 Babel 插件将 JSX 语法的代码还原为 React.createElement 的代码。
总结: JSX 是一个 JavaScript 的语法扩大,构造相似 XML。JSX 次要用于申明 React 元素,但 React 中并不强制应用 JSX。即便应用了 JSX,也会在构建过程中,通过 Babel 插件编译为 React.createElement。所以 JSX 更像是 React.createElement 的一种语法糖。
React 团队并不想引入 JavaScript 自身以外的开发体系。而是心愿通过正当的关注点拆散放弃组件开发的纯正性。
对React-Fiber的了解,它解决了什么问题?
React V15 在渲染时,会递归比对 VirtualDOM 树,找出须要变动的节点,而后同步更新它们, 零打碎敲。这个过程期间, React 会占据浏览器资源,这会导致用户触发的事件得不到响应,并且会导致掉帧,导致用户感觉到卡顿。
为了给用户制作一种利用很快的“假象”,不能让一个工作长期霸占着资源。 能够将浏览器的渲染、布局、绘制、资源加载(例如 HTML 解析)、事件响应、脚本执行视作操作系统的“过程”,须要通过某些调度策略正当地调配 CPU 资源,从而进步浏览器的用户响应速率, 同时兼顾工作执行效率。
所以 React 通过Fiber 架构,让这个执行过程变成可被中断。“适时”地让出 CPU 执行权,除了能够让浏览器及时地响应用户的交互,还有其余益处:
- 分批延时对DOM进行操作,防止一次性操作大量 DOM 节点,能够失去更好的用户体验;
- 给浏览器一点喘息的机会,它会对代码进行编译优化(JIT)及进行热代码优化,或者对 reflow 进行修改。
核心思想: Fiber 也称协程或者纤程。它和线程并不一样,协程自身是没有并发或者并行能力的(须要配合线程),它只是一种管制流程的让出机制。让出 CPU 的执行权,让 CPU 能在这段时间执行其余的操作。渲染的过程能够被中断,能够将控制权交回浏览器,让位给高优先级的工作,浏览器闲暇后再复原渲染。
参考:前端react面试题具体解答
hooks 和 class 比拟的劣势?
一、更容易复用代码
二、清新的代码格调+代码量更少
毛病
状态不同步
不好用的useEffect,
React中refs的作用是什么?有哪些利用场景?
Refs 提供了一种形式,用于拜访在 render 办法中创立的 React 元素或 DOM 节点。Refs 应该审慎应用,如下场景应用 Refs 比拟适宜:
- 解决焦点、文本抉择或者媒体的管制
- 触发必要的动画
- 集成第三方 DOM 库
Refs 是应用 React.createRef()
办法创立的,他通过 ref
属性附加到 React 元素上。要在整个组件中应用 Refs,须要将 ref
在构造函数中调配给其实例属性:
class MyComponent extends React.Component { constructor(props) { super(props) this.myRef = React.createRef() } render() { return <div ref={this.myRef} /> }}
因为函数组件没有实例,因而不能在函数组件上间接应用 ref
:
function MyFunctionalComponent() { return <input />;}class Parent extends React.Component { constructor(props) { super(props); this.textInput = React.createRef(); } render() { // 这将不会工作! return ( <MyFunctionalComponent ref={this.textInput} /> ); }}
但能够通过闭合的帮忙在函数组件外部进行应用 Refs:
function CustomTextInput(props) { // 这里必须申明 textInput,这样 ref 回调才能够援用它 let textInput = null; function handleClick() { textInput.focus(); } return ( <div> <input type="text" ref={(input) => { textInput = input; }} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); }
留神:
- 不应该适度的应用 Refs
ref
的返回值取决于节点的类型:- 当
ref
属性被用于一个一般的 HTML 元素时,React.createRef()
将接管底层 DOM 元素作为他的current
属性以创立ref
。 - 当
ref
属性被用于一个自定义的类组件时,ref
对象将接管该组件已挂载的实例作为他的current
。
- 当
- 当在父组件中须要拜访子组件中的
ref
时可应用传递 Refs 或回调 Refs。
对 React-Intl 的了解,它的工作原理?
React-intl是雅虎的语言国际化开源我的项目FormatJS的一部分,通过其提供的组件和API能够与ReactJS绑定。
React-intl提供了两种应用办法,一种是援用React组件,另一种是间接调取API,官网更加举荐在React我的项目中应用前者,只有在无奈应用React组件的中央,才应该调用框架提供的API。它提供了一系列的React组件,包含数字格式化、字符串格式化、日期格式化等。
在React-intl中,能够配置不同的语言包,他的工作原理就是依据须要,在语言包之间进行切换。
constructor 为什么不先渲染?
由ES6的继承规定得悉,不论子类写不写constructor,在new实例的过程都会给补上constructor。
所以:constructor钩子函数并不是不可短少的,子组件能够在一些状况略去。比方不本人的state,从props中获取的状况
React 中的高阶组件使用了什么设计模式?
应用了装璜模式,高阶组件的使用:
function withWindowWidth(BaseComponent) { class DerivedClass extends React.Component { state = { windowWidth: window.innerWidth, } onResize = () => { this.setState({ windowWidth: window.innerWidth, }) } componentDidMount() { window.addEventListener('resize', this.onResize) } componentWillUnmount() { window.removeEventListener('resize', this.onResize); } render() { return <BaseComponent {...this.props} {...this.state}/> } } return DerivedClass;}const MyComponent = (props) => { return <div>Window width is: {props.windowWidth}</div>};export default withWindowWidth(MyComponent);
装璜模式的特点是不须要扭转 被装璜对象 自身,而只是在里面套一个外壳接口。JavaScript 目前曾经有了原生装璜器的提案,其用法如下:
@testable class MyTestableClass {}
对 React 和 Vue 的了解,它们的异同
相似之处:
- 都将注意力集中放弃在外围库,而将其余性能如路由和全局状态治理交给相干的库
- 都有本人的构建工具,能让你失去一个依据最佳实际设置的我的项目模板。
- 都应用了Virtual DOM(虚构DOM)进步重绘性能
- 都有props的概念,容许组件间的数据传递
- 都激励组件化利用,将利用分拆成一个个性能明确的模块,进步复用性
不同之处:
1)数据流
Vue默认反对数据双向绑定,而React始终提倡单向数据流
2)虚构DOM
Vue2.x开始引入"Virtual DOM",打消了和React在这方面的差别,然而在具体的细节还是有各自的特点。
- Vue声称能够更快地计算出Virtual DOM的差别,这是因为它在渲染过程中,会跟踪每一个组件的依赖关系,不须要从新渲染整个组件树。
- 对于React而言,每当利用的状态被扭转时,全副子组件都会从新渲染。当然,这能够通过 PureComponent/shouldComponentUpdate这个生命周期办法来进行管制,但Vue将此视为默认的优化。
3)组件化
React与Vue最大的不同是模板的编写。
- Vue激励写近似惯例HTML的模板。写起来很靠近规范 HTML元素,只是多了一些属性。
- React举荐你所有的模板通用JavaScript的语法扩大——JSX书写。
具体来讲:React中render函数是反对闭包个性的,所以咱们import的组件在render中能够间接调用。然而在Vue中,因为模板中应用的数据都必须挂在 this 上进行一次直达,所以 import 完组件之后,还须要在 components 中再申明下。
4)监听数据变动的实现原理不同
- Vue 通过 getter/setter 以及一些函数的劫持,能准确晓得数据变动,不须要特地的优化就能达到很好的性能
- React 默认是通过比拟援用的形式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的vDOM的从新渲染。这是因为 Vue 应用的是可变数据,而React更强调数据的不可变。
5)高阶组件
react能够通过高阶组件(Higher Order Components-- HOC)来扩大,而vue须要通过mixins来扩大。
起因高阶组件就是高阶函数,而React的组件自身就是纯正的函数,所以高阶函数对React来说大海捞针。相同Vue.js应用HTML模板创立视图组件,这时模板无奈无效的编译,因而Vue不采纳HOC来实现。
6)构建工具
两者都有本人的构建工具
- React ==> Create React APP
- Vue ==> vue-cli
7)跨平台
- React ==> React Native
- Vue ==> Weex
React中props.children和React.Children的区别
在React中,当波及组件嵌套,在父组件中应用props.children
把所有子组件显示进去。如下:
function ParentComponent(props){ return ( <div> {props.children} </div> )}
如果想把父组件中的属性传给所有的子组件,须要应用React.Children
办法。
比方,把几个Radio组合起来,合成一个RadioGroup,这就要求所有的Radio具备同样的name属性值。能够这样:把Radio看做子组件,RadioGroup看做父组件,name的属性值在RadioGroup这个父组件中设置。
首先是子组件:
//子组件function RadioOption(props) { return ( <label> <input type="radio" value={props.value} name={props.name} /> {props.label} </label> )}
而后是父组件,不仅须要把它所有的子组件显示进去,还须要为每个子组件赋上name属性和值:
//父组件用,props是指父组件的propsfunction renderChildren(props) { //遍历所有子组件 return React.Children.map(props.children, child => { if (child.type === RadioOption) return React.cloneElement(child, { //把父组件的props.name赋值给每个子组件 name: props.name }) else return child })}//父组件function RadioGroup(props) { return ( <div> {renderChildren(props)} </div> )}function App() { return ( <RadioGroup name="hello"> <RadioOption label="选项一" value="1" /> <RadioOption label="选项二" value="2" /> <RadioOption label="选项三" value="3" /> </RadioGroup> )}export default App;
以上,React.Children.map
让咱们对父组件的所有子组件又更灵便的管制。
虚构 DOM 的引入与间接操作原生 DOM 相比,哪一个效率更高,为什么
虚构DOM绝对原生的DOM不肯定是效率更高,如果只批改一个按钮的文案,那么虚构 DOM 的操作无论如何都不可能比实在的 DOM 操作更快。在首次渲染大量DOM时,因为多了一层虚构DOM的计算,虚构DOM也会比innerHTML插入慢。它能保障性能上限,在实在DOM操作的时候进行针对性的优化时,还是更快的。所以要依据具体的场景进行探讨。
在整个 DOM 操作的演化过程中,其实主要矛盾并不在于性能,而在于开发者写得爽不爽,在于研发体验/研发效率。虚构 DOM 不是别的,正是前端开发们为了谋求更好的研发体验和研发效率而发明进去的高阶产物。虚构 DOM 并不一定会带来更好的性能,React 官网也素来没有把虚构 DOM 作为性能层面的卖点对外输入过。**虚构 DOM 的优越之处在于,它可能在提供更爽、更高效的研发模式(也就是函数式的 UI 编程形式)的同时,依然放弃一个还不错的性能。
在React中遍历的办法有哪些?
(1)遍历数组:map && forEach
import React from 'react';class App extends React.Component { render() { let arr = ['a', 'b', 'c', 'd']; return ( <ul> { arr.map((item, index) => { return <li key={index}>{item}</li> }) } </ul> ) }}class App extends React.Component { render() { let arr = ['a', 'b', 'c', 'd']; return ( <ul> { arr.forEach((item, index) => { return <li key={index}>{item}</li> }) } </ul> ) }}
(2)遍历对象:map && for in
class App extends React.Component { render() { let obj = { a: 1, b: 2, c: 3 } return ( <ul> { (() => { let domArr = []; for(const key in obj) { if(obj.hasOwnProperty(key)) { const value = obj[key] domArr.push(<li key={key}>{value}</li>) } } return domArr; })() } </ul> ) }}// Object.entries() 把对象转换成数组class App extends React.Component { render() { let obj = { a: 1, b: 2, c: 3 } return ( <ul> { Object.entries(obj).map(([key, value], index) => { // item是一个数组,把item解构,写法是[key, value] return <li key={key}>{value}</li> }) } </ul> ) }}
React中Diff算法的原理是什么?
原理如下。
(1)节点之间的比拟。
节点包含两种类型:一种是 React组件,另一种是HTML的DOM。
如果节点类型不同,按以下形式比拟。
如果 HTML DOM不同,间接应用新的替换旧的。如果组件类型不同,也间接应用新的替换旧的。
如果 HTML DOM类型雷同,按以下形式比拟。
在 React里款式并不是一个纯正的字符串,而是一个对象,这样在款式产生扭转时,只须要扭转替换变动当前的款式。批改完以后节点之后,递归解决该节点的子节点。
如果组件类型雷同,按以下形式比拟。
如果组件类型雷同,应用 React机制解决。个别应用新的 props替换旧的 props,并在之后调用组件的 componentWillReceiveProps办法,之前组件的 render办法会被调用。
节点的比拟机制开始递归作用于它的子节点。
(2)两个列表之间的比拟。
一个节点列表中的一个节点产生扭转, React无奈很妤地解决这个问题。循环新旧两个列表,并找出不同,这是 React惟一的解决办法。
然而,有一个方法能够把这个算法的复杂度升高。那就是在生成一个节点列表时给每个节点上增加一个key。这个key只须要在这一个节点列表中惟一,不须要全局惟一。
(3)取舍
须要留神的是,下面的启发式算法基于两点假如。
类型相近的节点总是生成同样的树,而类型不同的节点也总是生成不同的树
能够为屡次 render都体现稳固的节点设置key。
下面的节点之间的比拟算法基本上就是基于这两个假如而实现的。要进步 React利用的效率,须要依照这两点假如来开发。
diff 虚构DOM 比拟的规定
【旧虚构DOM】 与 【新虚构DOM】中雷同key
若虚构DOM中的内容没有产生扭转,间接应用旧的虚构DOM
若虚构DOM中的内容产生扭转了,则生成新实在的DOM,随后替换页面中之前的实在DOM
【旧虚构DOM】 中未找到 与 【新虚构DOM】雷同的key
依据数据创立实在DOM,随后渲染到页面