React setState 调用之后产生了什么?是同步还是异步?
(1)React 中 setState 后产生了什么
在代码中调用 setState 函数之后,React 会将传入的参数对象与组件以后的状态合并,而后触发和谐过程(Reconciliation)。通过和谐过程,React 会以绝对高效的形式依据新的状态构建 React 元素树并且着手从新渲染整个 UI 界面。
在 React 失去元素树之后,React 会主动计算出新的树与老树的节点差别,而后依据差别对界面进行最小化重渲染。在差别计算算法中,React 可能绝对准确地晓得哪些地位产生了扭转以及应该如何扭转,这就保障了按需更新,而不是全副从新渲染。
如果在短时间内频繁 setState。React 会将 state 的扭转压入栈中,在适合的机会,批量更新 state 和视图,达到进步性能的成果。
(2)setState 是同步还是异步的
如果所有 setState 是同步的,意味着每执行一次 setState 时(有可能一个同步代码中,屡次 setState),都从新 vnode diff + dom 批改,这对性能来说是极为不好的。如果是异步,则能够把一个同步代码中的多个 setState 合并成一次组件更新。所以默认是异步的,然而在一些状况下是同步的。
setState 并不是单纯同步 / 异步的,它的体现会因调用场景的不同而不同。在源码中,通过 isBatchingUpdates 来判断 setState 是先存进 state 队列还是间接更新,如果值为 true 则执行异步操作,为 false 则间接更新。
- 异步: 在 React 能够管制的中央,就为 true,比方在 React 生命周期事件和合成事件中,都会走合并操作,提早更新的策略。
- 同步: 在 React 无法控制的中央,比方原生事件,具体就是在 addEventListener、setTimeout、setInterval 等事件中,就只能同步更新。
个别认为,做异步设计是为了性能优化、缩小渲染次数:
setState
设计为异步,能够显著的晋升性能。如果每次调用setState
都进行一次更新,那么意味着render
函数会被频繁调用,界面从新渲染,这样效率是很低的;最好的方法应该是获取到多个更新,之后进行批量更新;- 如果同步更新了
state
,然而还没有执行render
函数,那么state
和props
不能放弃同步。state
和props
不能放弃一致性,会在开发中产生很多的问题;
为什么应用 jsx 的组件中没有看到应用 react 却须要引入 react?
实质上来说 JSX 是 React.createElement(component, props, ...children)
办法的语法糖。在 React 17 之前,如果应用了 JSX,其实就是在应用 React,babel
会把组件转换为 CreateElement
模式。在 React 17 之后,就不再须要引入,因为 babel
曾经能够帮咱们主动引入 react。
React 中有应用过 getDefaultProps 吗?它有什么作用?
通过实现组件的 getDefaultProps,对属性设置默认值(ES5 的写法):
var ShowTitle = React.createClass({getDefaultProps:function(){
return{title : "React"}
},
render : function(){return <h1>{this.props.title}</h1>
}
});
React 高阶组件、Render props、hooks 有什么区别,为什么要一直迭代
这三者是目前 react 解决代码复用的次要形式:
- 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
- render props 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术,更具体的说,render prop 是一个用于告知组件须要渲染什么内容的函数 prop。
- 通常,render props 和高阶组件只渲染一个子节点。让 Hook 来服务这个应用场景更加简略。这两种模式仍有用武之地,(例如,一个虚构滚动条组件或者会有一个 renderltem 属性,或是一个可见的容器组件或者会有它本人的 DOM 构造)。但在大部分场景下,Hook 足够了,并且可能帮忙缩小嵌套。
(1)HOC 官网解释∶
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。
简言之,HOC 是一种组件的设计模式,HOC 承受一个组件和额定的参数(如果须要),返回一个新的组件。HOC 是纯函数,没有副作用。
// hoc 的定义
function withSubscription(WrappedComponent, selectData) {
return class extends React.Component {constructor(props) {super(props);
this.state = {data: selectData(DataSource, props)
};
}
// 一些通用的逻辑解决
render() {
// ... 并应用新数据渲染被包装的组件!
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
// 应用
const BlogPostWithSubscription = withSubscription(BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id));
HOC 的优缺点∶
- 长处∶ 逻辑服用、不影响被包裹组件的外部逻辑。
- 毛病∶ hoc 传递给被包裹组件的 props 容易和被包裹后的组件重名,进而被笼罩
(2)Render props 官网解释∶
“render prop” 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术
具备 render prop 的组件承受一个返回 React 元素的函数,将 render 的渲染逻辑注入到组件外部。在这里,”render” 的命名能够是任何其余无效的标识符。
// DataProvider 组件外部的渲染逻辑如下
class DataProvider extends React.Components {
state = {name: 'Tom'}
render() {
return (
<div>
<p> 共享数据组件本人外部的渲染逻辑 </p>
{this.props.render(this.state) } </div>
);
}
}
// 调用形式
<DataProvider render={data => (<h1>Hello {data.name}</h1>
)}/>
由此能够看到,render props 的优缺点也很显著∶
- 长处:数据共享、代码复用,将组件内的 state 作为 props 传递给调用者,将渲染逻辑交给调用者。
- 毛病:无奈在 return 语句外拜访数据、嵌套写法不够优雅
(3)Hooks 官网解释∶
Hook 是 React 16.8 的新增个性。它能够让你在不编写 class 的状况下应用 state 以及其余的 React 个性。通过自定义 hook,能够复用代码逻辑。
// 自定义一个获取订阅数据的 hook
function useSubscription() {const data = DataSource.getComments();
return [data];
}
//
function CommentList(props) {const {data} = props;
const [subData] = useSubscription();
...
}
// 应用
<CommentList data='hello' />
以上能够看出,hook 解决了 hoc 的 prop 笼罩的问题,同时应用的形式解决了 render props 的嵌套天堂的问题。hook 的长处如下∶
- 应用直观;
- 解决 hoc 的 prop 重名问题;
- 解决 render props 因共享数据 而呈现嵌套天堂的问题;
- 能在 return 之外应用数据的问题。
须要留神的是:hook 只能在组件顶层应用,不可在分支语句中应用。、
React key 是干嘛用的 为什么要加?key 次要是解决哪一类问题的
Keys 是 React 用于追踪哪些列表中元素被批改、被增加或者被移除的辅助标识。在开发过程中,咱们须要保障某个元素的 key 在其同级元素中具备唯一性。
在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是早先创立的还是被挪动而来的元素,从而缩小不必要的元素重渲染此外,React 还须要借助 Key 值来判断元素与本地状态的关联关系。
注意事项:
- key 值肯定要和具体的元素—一对应;
- 尽量不要用数组的 index 去作为 key;
- 不要在 render 的时候用随机数或者其余操作给元素加上不稳固的 key,这样造成的性能开销比不加 key 的状况下更蹩脚。
setState 之后 产生了什么?
- (1)代码中调用 setState 函数之后,React 会将传入的参数对象与组件以后的状态合并,而后触发所谓的和谐过程(Reconciliation)。
- (2)通过和谐过程,React 会以绝对高效的形式依据新的状态构建 React 元素树并且着手从新渲染整个 UI 界面;
- (3)在 React 失去元素树之后,React 会主动计算出新的树与老树的节点差别,而后依据差别对界面进行最小化重渲染;
- (4)在差别计算算法中,React 可能绝对准确地晓得哪些地位产生了扭转以及应该如何扭转,这就保障了按需更新,而不是全副从新渲染。
setState 的调用会引起 React 的更新生命周期的 4 个函数执行。
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
参考 前端进阶面试题具体解答
什么是 React Fiber?
Fiber 是 React 16 中新的协调引擎或从新实现外围算法。它的次要指标是反对虚构 DOM 的增量渲染。React Fiber 的指标是进步其在动画、布局、手势、暂停、停止或重用等方面的适用性,并为不同类型的更新调配优先级,以及新的并发原语。
React Fiber 的指标是加强其在动画、布局和手势等畛域的适用性。它的次要个性是增量渲染: 可能将渲染工作宰割成块,并将其扩散到多个帧中。
react 生命周期
初始化阶段:
- getDefaultProps: 获取实例的默认属性
- getInitialState: 获取每个实例的初始化状态
- componentWillMount:组件行将被装载、渲染到页面上
- render: 组件在这里生成虚构的 DOM 节点
- componentDidMount: 组件真正在被装载之后
运行中状态:
- componentWillReceiveProps: 组件将要接管到属性的时候调用
- shouldComponentUpdate: 组件承受到新属性或者新状态的时候(能够返回 false,接收数据后不更新,阻止 render 调用,前面的函数不会被继续执行了)
- componentWillUpdate: 组件行将更新不能批改属性和状态
- render: 组件从新描述
- componentDidUpdate: 组件曾经更新
销毁阶段:
- componentWillUnmount: 组件行将销毁
shouldComponentUpdate 是做什么的,(react 性能优化是哪个周期函数?)
shouldComponentUpdate 这个办法用来判断是否须要调用 render 办法从新描述 dom。因为 dom 的描述十分耗费性能,如果咱们能在 shouldComponentUpdate 办法中可能写出更优化的 dom diff 算法,能够极大的进步性能。
在 react17 会删除以下三个生命周期
componentWillMount,componentWillReceiveProps,componentWillUpdate
对 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.Children.map 和 js 的 map 有什么区别?
JavaScript 中的 map 不会对为 null 或者 undefined 的数据进行解决,而 React.Children.map 中的 map 能够解决 React.Children 为 null 或者 undefined 的状况。
对 React context 的了解
在 React 中,数据传递个别应用 props 传递数据,维持单向数据流,这样能够让组件之间的关系变得简略且可预测,然而单项数据流在某些场景中并不实用。单纯一对的父子组件传递并无问题,但要是组件之间层层依赖深刻,props 就须要层层传递显然,这样做太繁琐了。
Context 提供了一种在组件之间共享此类值的形式,而不用显式地通过组件树的逐层传递 props。
能够把 context 当做是特定一个组件树内共享的 store,用来做数据传递。简略说就是,当你不想在组件树中通过逐层传递 props 或者 state 的形式来传递数据时,能够应用 Context 来实现跨层级的组件数据传递。
JS 的代码块在执行期间,会创立一个相应的作用域链,这个作用域链记录着运行时 JS 代码块执行期间所能拜访的流动对象,包含变量和函数,JS 程序通过作用域链拜访到代码块外部或者内部的变量和函数。
如果以 JS 的作用域链作为类比,React 组件提供的 Context 对象其实就好比一个提供给子组件拜访的作用域,而 Context 对象的属性能够看成作用域上的流动对象。因为组件 的 Context 由其父节点链上所有组件通 过 getChildContext()返回的 Context 对象组合而成,所以,组件通过 Context 是能够拜访到其父组件链上所有节点组件提供的 Context 的属性。
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 中遍历的办法有哪些?
(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>
)
}
}
传入 setState 函数的第二个参数的作用是什么?
该函数会在
setState
函数调用实现并且组件开始重渲染的时候被调用,咱们能够用该函数来监听渲染是否实现:
this.setState({ username: 'tylermcginnis33'},
() => console.log('setState has finished and the component has re-rendered.')
)
this.setState((prevState, props) => {
return {streak: prevState.streak + props.count}
})
class 类的 key 改了,会产生什么,会执行哪些周期函数?
在开发过程中,咱们须要保障某个元素的 key 在其同级元素中具备唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是早先创立的还是被挪动而来的元素,从而缩小不必要的元素重渲染。此外,React 还须要借助 Key 值来判断元素与本地状态的关联关系,因而咱们绝不可漠视转换函数中 Key 的重要性。
答:componentWillMount componentDidMount render
React 中的状态是什么?它是如何应用的
状态是 React 组件的外围,是数据的起源,必须尽可能简略。基本上状态是确定组件出现和行为的对象。与 props 不同,它们是可变的,并创立动静和交互式组件。能够通过 this.state()
拜访它们。
React 的 Fiber 工作原理,解决了什么问题
- React Fiber 是一种基于浏览器的单线程调度算法。
React Fiber 用相似 requestIdleCallback 的机制来做异步 diff。然而之前数据结构不反对这样的实现异步 diff,于是 React 实现了一个相似链表的数据结构,将原来的 递归 diff 变成了当初的 遍历 diff,这样就能做到异步可更新了
React 数据长久化有什么实际吗?
封装数据长久化组件:
let storage={
// 减少
set(key, value){localStorage.setItem(key, JSON.stringify(value));
},
// 获取
get(key){return JSON.parse(localStorage.getItem(key));
},
// 删除
remove(key){localStorage.removeItem(key);
}
};
export default Storage;
在 React 我的项目中,通过 redux 存储全局数据时,会有一个问题,如果用户刷新了网页,那么通过 redux 存储的全局数据就会被全副清空,比方登录信息等。这时就会有全局数据长久化存储的需要。首先想到的就是 localStorage,localStorage 是没有工夫限度的数据存储,能够通过它来实现数据的长久化存储。
然而在曾经应用 redux 来治理和存储全局数据的根底上,再去应用 localStorage 来读写数据,这样不仅是工作量微小,还容易出错。那么有没有联合 redux 来达到持久数据存储性能的框架呢?当然,它就是redux-persist。redux-persist 会将 redux 的 store 中的数据缓存到浏览器的 localStorage 中。其应用步骤如下:
(1)首先要装置 redux-persist:
npm i redux-persist
(2)对于 reducer 和 action 的解决不变,只需批改 store 的生成代码,批改如下:
import {createStore} from 'redux'
import reducers from '../reducers/index'
import {persistStore, persistReducer} from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
const persistConfig = {
key: 'root',
storage: storage,
stateReconciler: autoMergeLevel2 // 查看 'Merge Process' 局部的具体情况
};
const myPersistReducer = persistReducer(persistConfig, reducers)
const store = createStore(myPersistReducer)
export const persistor = persistStore(store)
export default store
(3)在 index.js 中,将 PersistGate 标签作为网页内容的父标签:
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'
import store from './redux/store/store'
import {persistor} from './redux/store/store'
import {PersistGate} from 'redux-persist/lib/integration/react';
ReactDOM.render(<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
{/* 网页内容 */} </PersistGate>
</Provider>, document.getElementById('root'));
这就实现了通过 redux-persist 实现 React 长久化本地数据存储的简略利用。
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 是指父组件的 props
function 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
让咱们对父组件的所有子组件又更灵便的管制。
在 React 中元素(element)和组件(component)有什么区别?
简略地说,在 React 中元素(虛拟 DOM)形容了你在屏幕上看到的 DOM 元素。
换个说法就是,在 React 中元素是页面中 DOM 元素的对象示意形式。在 React 中组件是一个函数或一个类,它能够承受输出并返回一个元素。
留神:工作中,为了进步开发效率,通常应用 JSX 语法示意 React 元素(虚构 DOM)。在编译的时候,把它转化成一个 React. createElement 调用办法。