1. 什么是React?
React是一个开源前端JavaScript库,用于构建用户界面,尤其是单页面应用程序。 它用于处理Web和移动应用程序的视图层。 React是由Facebook工作的软件工程师Jordan Walke创建的。 React于2011年首次部署在Facebook的News Feed上,2012年首次部署在Instagram上。
2. React的主要特点是什么?
React的主要特点是:
- 考虑到RealDOM操作很昂贵,它使用VirtualDOM而不是RealDOM。
- 支持服务器端呈现。
- 遵循单向*数据流或数据绑定。
- 使用可重用/可组合的UI组件来开发视图。
3. 什么是JSX?
JSX是ECMAScript的类似XML的语法扩展(首字母缩略词代表JavaScript XML)。 基本上它只是为React.createElement()函数提供语法糖,给我们表达JavaScript和HTML之类的模板语法。
在下面的示例中,< h1>标签内的文本作为JavaScript函数返回到render函数。
class App extends React.Component {
render() {
return(
<div>
<h1>{'Welcome to React world!'}</h1>
</div>
)
}
}
4. 元素和组件有什么区别?
一个元素是描述你要在DOM节点或其他部件方面出现在屏幕上什么平原对象。元素可以在其道具中包含其他元素。创建React元素很便宜。一旦创建了一个元素,它就永远不会发生变异。
React Element的对象表示如下:
const element = React.createElement(
'div',
{id : 'login-btn' },
'登录'
)
上面的React.createElement()函数返回一个对象:
{
type: 'div',
props: {
children: 'Login',
id: 'login-btn'
}
}
最后它使用ReactDOM.render()以下方式呈现给DOM :
<div id = 'login-btn '>登录</div >
而组件可以以几种不同的方式声明。它可以是一个带有render()方法的类。或者,在简单的情况下,可以将其定义为函数。在任何一种情况下,它都将props作为输入,并返回一个JSX树作为输出:
const Button =({onLogin})=>
<div id = { 'login-btn' } onClick = {onLogin} />
然后JSX被转换为React.createElement()函数树
const Button =({onLogin})=> React.createElement(
'div',
{id : 'login-btn',onClick :onLogin},
'登录' )
5. 如何在React中创建组件?
有两种可能的方法来创建组件。
- 功能组件:这是创建组件的最简单方法。这些是纯JavaScript函数,它接受props对象作为第一个参数并返回React元素:
function Greeting({ message }){
return < h1 > { ` Hello,$ { message } ` } </ h1 >
}
- 类组件:您还可以使用ES6类来定义组件。上面的函数组件可以写成:
class Greeting extends React.Component {
render(){
return < h1 > { ` Hello,$ { this.props.message} ` } </ h1 >
}
}
6.何时在功能组件上使用类组件?
如果组件需要状态或生命周期方法,则使用类组件,否则使用函数组件。
7.什么是PureCompoent?
React.PureComponent除了为你提供了一个具有浅比较的shouldComponentUpdate()方法,React.PureComponent和React.Component基本上完全相同。当props或者state改变时,PureComponent将对props和state进行浅比较。另一方面,Component不会比较当前和下个状态的props和state。因此,每当shouldComponentUpdate被调用时,组件默认的会重新渲染。
8.React的state是什么?
State是Component的对象,存储一些可能在组件的生命周期内发生变化的信息。我们应该尽量让我们的State简单,并尽量减少组件中State的数量。下面创建一个有State组件,
class User extends React.Component {
constructor(props) {
super(props)
this.state = {
message: 'Welcome to React world'
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
)
}
}
State类似于props,但它是私有的并且完全由组件控制。也就是说,除了拥有它的组件之外的任何组件都不能访问和改变它。
9.React的props是什么?
props是组件的一种输入方式。它们可以是单个参或者一组值的对象。这些值在创建的时候,使用类似于Html tag的命名约定方式传递给组件。将数据由父组件传递个子组件。
React 中props的主要目的是为组件提供如下功能:
- 组件之间传递数据
- 更改组件state的状态
- 可以在组件的render()方法内使用this.props.reactProp
10.state 和 props 有什么不同?
首先props和state都是JavaScript的对象。虽然它们都包含影响渲染输出的信息。但是它们在组件内的功能却不同。Props组件之间的传递类似于函数参数的传递。而state是由所在的组件管理的,类似于函数内的声明变量。
11.为什么不能直接修改state?
- 如果你在组件没有render结束的直接修改state
//Wrong
this.state.message = 'Hello world'
- 使用setState()方法来改变state的值。这个方法会对组件中的state进行修改。当state发生变化的时候,所在组件会被重新render.
//Correct
this.serState({message : 'Hello world'})
注意:你可以直接在constructor中声明state对象。还有就是ESNEXT方式声明对象提案
12.setState()方法的callback?
setState()会触发组件页面的重新渲染,渲染结束后callback将被执行,由于setState()属于异步操作,callback来执行render之后的操作。
__注意:__setState()管理大量组件状态也许会导致不必要的生命周期函数钩子调用。
setState({ name: 'John' }, () => console.log('The name has updated and component re-rendered'))
13.HTML和React时间处理有什么区别?
- 在HTML中,事件名称应为小写
<button onclick='activateLasers()'>
而在React中事件名需要遵循 camelCase
< button onClick = { activateLasers } >
- 在HTML中,你可以通过返回false以防止默认行为
<a href='#' onclick='console.log("The link was clicked."); return false;' />
而在React中你必须明确的调用preventDefault()
function handleClick(event) {
event.preventDefault()
console.log('The link was clicked.')
}
14.如何在JSX的回调中进行方法绑定或者事件处理
有三种方式来实现:
- 在组件的constructor()中进行绑定。在JavaScript类中,默认是不绑定方法的。同样与类方法相似,适用于通过define方式的React事件。
class Component extends React.Componenet {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
// ...
}
}
- 公共类方式绑定:
handleClick = () => {
console.log('this is:', this)
}
<button onClick={this.handleClick}>
{'Click me'}
</button>
- 可以通过箭头函数进行绑定。可以直接在回调中使用箭头函数。
<button onClick={(event) => this.handleClick(event)}>
{'Click me'}
</button>
注意: 如果需要传递参数的话,那么这个方式是不行的。在这种情况下,最好考虑使用.bind()或公共类方式。
15.如何进行事件或者回调之间的参数传递
使用箭头函数
<button onClick={() => this.handleClick(id)} />
也可以使用.bind():
<button onClick={this.handleClick.bind(this, id)} />
16.React中合成事件(SyntheticEvent)是什么?
SyntheticEvent是一个跨浏览器原生事件包装器。 它具有与浏览器原生事件相同的接口,包括 stopPropagation() 和 preventDefault() ,除了事件在所有浏览器中他们工作方式都相同。
17.什么是内联条件表达式?
你可以使用JS提供的if语句或者三元表达式来呈现条件表达式。除了这些方法,你可以在JSX中嵌入任何表达式。用{}将他们包装起来,然后是JS逻辑。运算符&&。
<h1>Hello!</h1>
{
messages.length > 0 && !isLogin?
<h2>
You have {messages.length} unread messages.
</h2>
:
<h2>
You don't have unread messages.
</h2>
}
18.什么是主键索引,在节点数组中使用它们有什么好处?
主键是创建节点数组的时候就应包含的特殊字符串属性。主键帮助React识别哪些节点已更改,已添加活已删除。
大多数情况下,会使用节点的ID作为主键:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
)
当你节点没有默认ID的时候,你可以使用数组的下标作为主键索引:
const todoItems = todos.map((todo, index) =>
<li key={index}>
{todo.text}
</li>
)
注意:
i. 使用下标作为主键索引是不被推荐的,因为节点的顺序可能会发生变化。可能对性能产生影响,并会导致组件的state出现问题.
ii. 如果将列List的子节点提取为单独的组件,则在List上应用主键索引而不是li组件中
iii.如果List的子节点中不存在主键索引,则控制台会显示警告。
19.refs有什么用?
ref来获取组件的引用。在大多数情况下应避免使用它们,但是,当您需要直接访问DOM元素或组件实例时,它们可能很有用。
20.如何创建refs?
有两种方法
- 这是新增加的方法。Refs是使用React.createRef()方法创建的,并通过ref属性附加到React元素。为了在整个组件中使用ref,只需将ref分配给构造函数中的instance属性。
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return <div ref={this.myRef} />
}
}
- 你也可以仍然使用使用ref回调方法。例如,搜索栏组件的输入框元素访问如下
class SearchBar extends Component {
constructor(props) {
super(props);
this.txtSearch = null;
this.state = { term: '' };
this.setInputSearchRef = e => {
this.txtSearch = e;
}
}
onInputChange(event) {
this.setState({ term: this.txtSearch.value });
}
render() {
return (
<input
value={this.state.term}
onChange={this.onInputChange.bind(this)}
ref={this.setInputSearchRef} />
);
}
}
您还可以使用 闭包 在函数组件中使用refs。 注意: 即使不是推荐的方法,您也可以使用内联引用回调
21.什么是forward refs ?
Ref forwarding是一种将ref钩子自动传递给组件的子孙组件的技术。对于应用的大部分组件,这种技术并不是那么的必要。然而,它对于个别的组件还是特别地有用的,尤其是那些可复用组件的类库。
const ButtonElement = React.forwardRef((props, ref) => (
<button ref={ref} className="CustomButton">
{props.children}
</button>
));
// Create ref to the DOM button:
const ref = React.createRef();
<ButtonElement ref={ref}>{'Forward Ref'}</ButtonElement>
22.在Refs回调方法和findDOMNode()中优先选择哪种方式获取Dom?
建议最好是使用Refs回调方法而不是findDOMNode()。因为findDOMNode()将不被React建议使用。
使用findDOMNode获取节点:
class MyComponent extends Component {
componentDidMount() {
findDOMNode(this).scrollIntoView()
}
render() {
return <div />
}
}
建议使用方法:
class MyComponent extends Component {
componentDidMount() {
this.node.scrollIntoView()
}
render() {
return <div ref={node => this.node = node} />
}
}
23. 为什么Refs字符串方式被遗留?
如果你之前使用React,可能会对一个比较旧的API比较熟悉,这个API中ref属性是一个字符串,像是textInput,而DOM节点通过this.refs.textInput来访问。我们现在建议不要再使用该方案,因为它有些问题,这个方案因此也已经作为遗留方案被废弃了,很可能在将来的某个版本中会被移除。在React v16版本中Refs字符串方式开始被删除。
- 针对类似Flow这样静态类型检测不支持,会产生引用异常
- 对复杂用例难以实现:需要向父组件暴露dom;单个实例绑定多个dom.
- 绑定到的实例,是执行render方法的实例,结果会让人很意外
class MyComponent extends Component {
renderRow = (index) => {
// This won't work. Ref will get attached to DataTable rather than MyComponent:
return <input ref={'input-' + index} />;
// This would work though! Callback refs are awesome.
return <input ref={input => this['input-' + index] = input} />;
}
render() {
return <DataTable data={this.props.data} renderRow={this.renderRow} />
}
}
24什么是虚拟DOM?
虚拟DOM(VDOM)是一种编程概念,是指虚拟的视图被保存在内存中,并通过诸如ReactDOM这样的库与“真实”的DOM保持同步。这个过程被称为和解。
这种编程方法使用了React的声明式API:你需要告诉React你想让视图处于什么状态,React则负责确保DOM与该状态相匹配。因此你在构建你的应用时不必自己去完成属性操作、事件处理、DOM更新,React会替你完成这一切。
由于“虚拟DOM”更像一种模式而不是特定的技术,有时候我们也会用它表示其他的意思。在React的世界中,由于 “虚拟DOM” 和 React元素 都是用于表示视图的对象,因此常常被关联在一起。然而React也使用被称为“fibers”的对象来存放组件树的附加信息。在React中,它们也被认为是“虚拟DOM”实现的一部分。
25. Virtual DOM如何工作?
- Babel和JSX;
- 创建VNode-一个简单的virtual DOM元素;
- 处理组件和子组件;
- 初始化渲染和创建一个DOM元素;
- 重新渲染;
- 移除DOM元素;
- 替换DOM元素。
26. Shadow DOM和Virtual DOM有什么区别?
它们并是不相同的概念。影子DOM(Shadow DOM)是一种浏览器技术,主要被设计用来为Web组件中的变量和CSS提供封装。虚拟DOM(Virtual DOM)是由JavaScript库在浏览器API之上实现的一种概念。
27. 什么是React Fiber?
React Fiber 是对 React 核心算法的重新实现。它的主要目的是使虚拟DOM能够进行增量渲染。其他的核心特性还包括当新的更新到来时暂停、中止或恢复 work 的能力;为不同类型的更新设置优先级的能力;以及新的并发原语。
28. React Fiber的主要目标是什么?
React Fiber 的目标是提高其对动画,布局和手势等领域的适用性。它的最重要的特性是 incremental rendering(增量渲染):它能够将渲染 work 拆分成多块并将这些任务块分散到多个帧执行。
29. 什么是受控组件?
在HTML当中,像< input >,< textarea >, 和 < select >这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新。
我们通过使react变成一种单一数据源的状态来结合二者。React负责渲染表单的组件仍然控制用户后续输入时所发生的变化。相应的,其值由React控制的输入表单元素称为“受控组件”。
例如,我们想要使上个例子中在提交表单时输出大写字符串,我们可以写成“受控组件”的形式:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()})
}
30. 什么是非受控组件?
非受控组件真实数据保存在 DOM 中,因此在使用非受控组件时,通过ref的方式查找DOM。这有点像传统的HTML。
在下面的UserProfile组件中,使用ref访问name。
class UserProfile extends React.Component {
constructor(props) {
super(props)
this.handleSubmit = this.handleSubmit.bind(this)
this.input = React.createRef()
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
{'Name:'}
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
在大多数情况下,建议使用受控组件来实现表单。
31.createElement和cloneElement有什么区别?
JSX元素将被转换为React.createElement()函数以创建将用于UI的对象表示的React元素。而cloneElement用于克隆元素并传递新的props。
32.React中的状态提升是什么?
React中的状态提升概括来说,就是将多个组件需要共享的状态提升到它们最近的父组件上.在父组件上改变这个状态然后通过props分发给子组件.
33.组件生命周期的不同阶段是什么?
【装配】
这些方法会在组件实例被创建和插入DOM中时被调用:
constructor(),componentWillMount() ,render()
,componentDidMount()【更新】
属性或状态的改变会触发一次更新。当一个组件在被重渲时,这些方法将会被调用:
componentWillReceiveProps(),shouldComponentUpdate(),componentWillUpdate(),render(),componentDidUpdate()-
【卸载】
当一个组件被从DOM中移除时,该方法被调用:
componentWillUnmount()React v16.3 的生命周期图
React v16 之前的生命周期图
34.React的生命周期方法是什么?
v16.3之后
-
getDerivedStateFromProps: 组件实例化后和接受新属性时将会调用getDerivedStateFromProps。它应该返回一个对象来更新状态,或者返回null来表明新属性不需要更新任何状态。
注意,如果父组件导致了组件的重新渲染,即使属性没有更新,这一方法也会被调用。如果你只想处理变化,你可能想去比较新旧值。
调用this.setState() 通常不会触发 getDerivedStateFromProps() - componentDidMount: 紧跟在组件装载后(被插入树中)调用。要求的DOM节点初始化应该放到这里。若你需要从远端加载数据,这是一个适合实例化网络请求的好地方。
-
shouldComponentUpdate: 使用shouldComponentUpdate()以让React知道是否组件的输出不受当前状态或属性影响。默认行为是在每一次状态的改变重新渲染,在大部分情况下你应该依赖于默认行为。
当接收到新属性或状态时,shouldComponentUpdate() 在渲染前被调用。默认为true。该方法在初始化渲染或当使用forceUpdate()时并不会被调用。 - getSnapshotBeforeUpdate: 在最新的渲染输出提交给DOM前将会立即调用。它让你的组件能在当前的值可能要改变前获得它们。这一生命周期返回的任何值将会 作为参数被传递给componentDidUpdate()。
-
componentDidUpdate: 紧跟在更新发生后调用。对于初次的渲染,该方法并不会调用。
当组件被更新之后,使用此方法作为操作DOM的一次机会。这也是一个适合发送请求的地方,只要你对比了当前属性和前一次属性(例如,如果属性没有改变那么请求也就没必要了)。 -
componentWillUnmount: 紧挨着在组件被卸载和销毁之前调用。可以在该方法里处理任何必要的清理工作,例如解绑定时器,取消网络请求,清理任何在componentDidMount环节创建的订阅。
你不应该调用 setState() 在 componentWillUnmount() 中,因为组件将永远不会被重新渲染了。一旦一个组件实例被卸载,它将永远不会再次装载。
v16.3之前
- componentWillMount:在渲染前调用,在客户端也在服务端。
- componentDidMount:在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
- componentWillReceiveProps:在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
-
shouldComponentUpdate:返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。 - componentWillUpdate:在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
- componentDidUpdate:在组件完成更新后立即调用。在初始化时不会被调用。
- componentWillUnmount:在组件从 DOM 中移除之前立刻被调用。它将用于取消任何传出网络请求,或删除与该组件关联的所有事件侦听器。
35. 什么是高阶组件?
高阶组件(HOC)是react中的高级技术,用来重用组件逻辑。但高阶组件本身并不是React API。它只是一种模式,这种模式是由react自身的组合性质必然产生的。
具体而言,高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
const EnhancedComponent = higherOrderComponent(WrappedComponent)
HOC可以应用在下列场景中:
- 代码重用,逻辑和引导抽象。
- 渲染劫持。
- 抽离state。
- 操纵prop
36. 如何创建代理方式的高阶组件?
您可以增减,删除,修改组件的props,并通过代理类型高阶组件返回传递给被包裹的组件,如下:
function HOC(WrappedComponent) {
return class Test extends Component {
render() {
const newProps = {
title: 'New Header',
footer: false,
showFeatureX: false,
showFeatureY: true
}
return <WrappedComponent {...this.props} {...newProps} />
}
}
}
37. 什么是Context?
Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
例如,要在应用程序中通过许多组件访问用户身份认证,地区偏好,UI主题
const {Provider, Consumer} = React.createContext(defaultValue)
38. 什么是children属性?
Children是一个prop(this.prop.children),它允许您将组件作为参数传递给其他组件,就像您使用的任何其他组件一样。
React API中有包含如下几个属性。React.Children.map, React.Children.forEach, React.Children.count, React.Children.only, React.Children.toArray.
简单实例如下:
const MyDiv = React.createClass({
render: function() {
return <div>{this.props.children}</div>
}
})
ReactDOM.render(
<MyDiv>
<span>{'Hello'}</span>
<span>{'World'}</span>
</MyDiv>,
node
)
39. 如何在React中添加注释行?
React/JSX 中的注释行与 JavaScript Multiline 类似,但是需要用大括号括起来的。
单行注释:
<div>
{/* Single-line comments(In vanilla JavaScript, the single-line comments are represented by double slash(//)) */}
{`Welcome ${user}, let's play React`}
</div>
多行注释:
<div>
{/* Multi-line comments for more than
one line */}
{`Welcome ${user}, let's play React`}
</div>
40. 在构造函数中使用super(props)的目的是什么?
子类本身没有this,需要使用super()继续父类的属性.其中 super语法来自es6。super(props)的作用就是在父类的构造函数中给props赋值一个对象,这样就可以使用this.props这种方式了。
传props:
class MyComponent extends React.Component {
constructor(props) {
super(props)
console.log(this.props) // prints { name: 'John', age: 42 }
}
}
未传Props
class MyComponent extends React.Component {
constructor(props) {
super()
console.log(this.props) // prints undefined
// but props parameter is still available
console.log(props) // prints { name: 'John', age: 42 }
}
render() {
// no difference outside constructor
console.log(this.props) // prints { name: 'John', age: 42 }
}
}
上面的代码片段显示 this.props仅在构造函数内输出的结果不同,在构造函数之外是一致的。
41.什么是协调Reconciliation?
当一个组件的props或状态发生变化时,React通过比较新返回的元素和先前渲染的元素来决定是否需要实际的DOM更新。当它们不相等时,React将更新DOM。 这个过程被称为“协调”。
42.如果设置一个以动态密钥为名的state?
如果您使用ES6或Babel来编译您的JSX代码,你可以使用computed property name来解决这个问题
handleInputChange(event) {
this.setState({ [event.target.id]: event.target.value })
}
43. 每次组件渲染时调用函数的常见错误是什么?
您需要确保在将函数作为参数传递时未被调用该函数。
render() {
// Wrong: handleClick is called instead of passed as a reference!
return <button onClick={this.handleClick()}>{'Click Me'}</button>
}
相反,传递函数本身没有括号:
render() {
// Correct: handleClick is passed as a reference!
return <button onClick={this.handleClick}>{'Click Me'}</button>
}
44.为什么组件名大写是必须的?
这是必要的,因为组件不是DOM元素,它们是构造函数。此外,在JSX中,小写标记名称是指HTML元素,而不是组件。
45. 为什么React使用className而不用class属性?
class 是JavaScript中的保留关键字,而JSX是JavaScript的扩展。这就是React不使用class而使用className的主要原因
render() {
return <span className={'menu navigation-menu'}>{'Menu'}</span>
}
46.什么是React Fragments?
它是React中的常见的用于组件返回多个元素的模式。Fragments允许您将子组件分组,而无需向DOM添加额外节点。
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
)
}
也有简易代码版的,但目前有些前端工具支持的还不太好:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
)
}
47.为什么使用Fragments比使用Div好?
- 通过不创建额外的DOM节点,Fragments更快并且使用更少的内存。这层级复杂的树有实际好处。
- 一些CSS机制(如Flexbox和CSS Grid)具有特殊的父子关系,并且在中间添加div使得很难保持所需的布局。
- DOM Inspector不那么杂乱。
48. React中Portals是什么?
Portals 能将子节点渲染到父组件的 DOM 层次之外
ReactDOM.createPortal(child, container)
第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 片段(fragment)。第二个参数(container)则是一个 DOM 元素。
49. 什么是无状态组件?
如果一个组件不需要管理 state 只是纯的展示,那么就可以定义成无状态组件。。您可以使用无状态的函数或类来创建无状态组件。如果你需要在组件中使用生命周期钩子的话你需要选择使用功能组件。如果你决定在这里使用无状态组件,这里有很好处:代码整洁、可读性高,便于测试,没有 this「由于使用的是箭头函数事件无需绑定」。
50. 什么是有状态组件?
如果组件的需要管理state,那么它可以被称为有状态组件。这些有状态组件始终是类组件,并且在组件的构造函数中初始化state。
class App extends Component {
constructor(props) {
super(props)
this.state = { count: 0 }
}
render() {
// ...
}
}
此篇仅仅是个人理解翻译,如有误可以留言指出。
您可以在此处下载完整问题。
未完,待续...
喜欢的话就点个赞就好了,谢谢!