百度前端高频react面试题总结
可以使用TypeScript写React应用吗?怎么操作?
(1)如果还未创建 Create React App 项目
- 直接创建一个具有 typescript 的 Create React App 项目:
npx create-react-app demo --typescript
(2)如果已经创建了 Create React App 项目,需要将 typescript 引入到已有项目中
- 通过命令将 typescript 引入项目:
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
- 将项目中任何 后缀名为 ‘.js’ 的 JavaScript 文件重命名为 TypeScript 文件即后缀名为 ‘.tsx’(例如 src/index.js 重命名为 src/index.tsx )
pureComponent和FunctionComponent区别
PureComponent
和Component
完全相同,但是在shouldComponentUpdate
实现中,PureComponent
使用了props
和state
的浅比较。主要作用是用来提高某些特定场景的性能
什么是 React Fiber?
Fiber 是 React 16 中新的协调引擎或重新实现核心算法。它的主要目标是支持虚拟DOM的增量渲染。React Fiber 的目标是提高其在动画、布局、手势、暂停、中止或重用等方面的适用性,并为不同类型的更新分配优先级,以及新的并发原语。
React Fiber 的目标是增强其在动画、布局和手势等领域的适用性。它的主要特性是增量渲染:能够将渲染工作分割成块,并将其分散到多个帧中。
React 父组件如何调用子组件中的方法?
- 如果是在方法组件中调用子组件(>= react@16.8),可以使用 useRef 和
useImperativeHandle
:
const { forwardRef, useRef, useImperativeHandle } = React;
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
getAlert() {
alert("getAlert from Child");
}
}));
return <h1>Hi</h1>;
});
const Parent = () => {
const childRef = useRef();
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.getAlert()}>Click</button>
</div>
);
};
- 如果是在类组件中调用子组件(
>= react@16.4
),可以使用 createRef:
const { Component } = React;
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
diff算法是怎么运作
每一种节点类型有自己的属性,也就是prop,每次进行diff的时候,react会先比较该节点类型,假如节点类型不一样,那么react会直接删除该节点,然后直接创建新的节点插入到其中,假如节点类型一样,那么会比较prop是否有更新,假如有prop不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点
这段代码有什么问题吗?
这段代码有什么问题:
this.setState((prevState, props) => {
return {
streak: prevState.streak + props.count,
};
});
答案:
没有什么问题。这种方式很少被使用,咱们可以将一个函数传递给setState
,该函数接收上一个 state
的值和当前的props
,并返回一个新的状态,如果咱们需要根据以前的状态重新设置状态,推荐使用这种方式。
Redux内部原理 内部怎么实现dispstch一个函数的
以
redux-thunk
中间件作为例子,下面就是thunkMiddleware
函数的代码
// 部分转为ES5代码,运行middleware函数会返回一个新的函数,如下:
return ({ dispatch, getState }) => {
// next实际就是传入的dispatch
return function (next) {
return function (action) {
// redux-thunk核心
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
};
}
redux-thunk
库内部源码非常的简单,允许action
是一个函数,同时支持参数传递,否则调用方法不变
redux
创建Store
:通过combineReducers
函数合并reducer
函数,返回一个新的函数combination
(这个函数负责循环遍历运行reducer
函数,返回全部state
)。将这个新函数作为参数传入createStore
函数,函数内部通过dispatch,初始化运行传入的combination
,state生成,返回store对象redux
中间件:applyMiddleware
函数中间件的主要目的就是修改dispatch
函数,返回经过中间件处理的新的dispatch
函数redux
使用:实际就是再次调用循环遍历调用reducer
函数,更新state
这三个点(...)在 React 干嘛用的?
...
在React(使用JSX)代码中做什么?它叫什么?
<Modal {...this.props} title='Modal heading' animation={false}/>
这个叫扩展操作符号或者展开操作符,例如,如果this.props
包含a:1
和b:2
,则
<Modal {...this.props} title='Modal heading' animation={false}>
等价于下面内容:
<Modal a={this.props.a} b={this.props.b} title='Modal heading' animation={false}>
扩展符号不仅适用于该用例,而且对于创建具有现有对象的大多数(或全部)属性的新对象非常方便,在更新state
咱们就经常这么做:
this.setState((prevState) => {
return { foo: { ...prevState.foo, a: "updated" } };
});
什么是 prop drilling,如何避免?
在构建 React 应用程序时,在多层嵌套组件来使用另一个嵌套组件提供的数据。最简单的方法是将一个 prop
从每个组件一层层的传递下去,从源组件传递到深层嵌套组件,这叫做prop drilling。
prop drilling
的主要缺点是原本不需要数据的组件变得不必要地复杂,并且难以维护。
为了避免prop drilling
,一种常用的方法是使用React Context。通过定义提供数据的Provider
组件,并允许嵌套的组件通过Consumer
组件或useContext
Hook 使用上下文数据。
React如何判断什么时候重新渲染组件?
组件状态的改变可以因为props
的改变,或者直接通过setState
方法改变。组件获得新的状态,然后React决定是否应该重新渲染组件。只要组件的state发生变化,React就会对组件进行重新渲染。这是因为React中的shouldComponentUpdate
方法默认返回true
,这就是导致每次更新都重新渲染的原因。
当React将要渲染组件时会执行shouldComponentUpdate
方法来看它是否返回true
(组件应该更新,也就是重新渲染)。所以需要重写shouldComponentUpdate
方法让它根据情况返回true
或者false
来告诉React什么时候重新渲染什么时候跳过重新渲染。
什么是纯函数?
纯函数是不依赖并且不会在其作用域之外修改变量状态的函数。本质上,纯函数始终在给定相同参数的情况下返回相同结果。
React如何获取组件对应的DOM元素?
可以用ref来获取某个子节点的实例,然后通过当前class组件实例的一些特定属性来直接获取子节点实例。ref有三种实现方法:
-
字符串格式:字符串格式,这是React16版本之前用得最多的,例如:
<p ref="info">span</p>
-
函数格式:ref对应一个方法,该方法有一个参数,也就是对应的节点实例,例如:
<p ref={ele => this.info = ele}></p>
-
createRef方法:React 16提供的一个API,使用React.createRef()来实现
在React中组件的this.state和setState有什么区别?
this.state通常是用来初始化state的,this.setState是用来修改state值的。如果初始化了state之后再使用this.state,之前的state会被覆盖掉,如果使用this.setState,只会替换掉相应的state值。所以,如果想要修改state的值,就需要使用setState,而不能直接修改state,直接修改state之后页面是不会更新的。
什么是虚拟DOM?
虚拟 DOM (VDOM)是真实 DOM 在内存中的表示。UI 的表示形式保存在内存中,并与实际的 DOM 同步。这是一个发生在渲染函数被调用和元素在屏幕上显示之间的步骤,整个过程被称为调和。
s# 如何避免在React重新绑定实例?
有几种常用方法可以避免在 React 中绑定方法:
1.将事件处理程序定义为内联箭头函数
class SubmitButton extends React.Component {
constructor(props) {
super(props);
this.state = {
isFormSubmitted: false,
};
}
render() {
return (
<button
onClick={() => {
this.setState({ isFormSubmitted: true });
}}
>
Submit
</button>
);
}
}
2.使用箭头函数来定义方法:
class SubmitButton extends React.Component {
state = {
isFormSubmitted: false,
};
handleSubmit = () => {
this.setState({
isFormSubmitted: true,
});
};
render() {
return <button onClick={this.handleSubmit}>Submit</button>;
}
}
3.使用带有 Hooks 的函数组件
const SubmitButton = () => {
const [isFormSubmitted, setIsFormSubmitted] = useState(false);
return (
<button
onClick={() => {
setIsFormSubmitted(true);
}}
>
Submit
</button>
);
};
何为 Children
在JSX表达式中,一个开始标签(比如<a>
)和一个关闭标签(比如</a>
)之间的内容会作为一个特殊的属性props.children
被自动传递给包含着它的组件。
这个属性有许多可用的方法,包括 React.Children.map
,React.Children.forEach
, React.Children.count
, React.Children.only
,React.Children.toArray
。
React 16.X 中 props 改变后在哪个生命周期中处理
在getDerivedStateFromProps中进行处理。
这个生命周期函数是为了替代componentWillReceiveProps
存在的,所以在需要使用componentWillReceiveProps
时,就可以考虑使用getDerivedStateFromProps
来进行替代。
两者的参数是不相同的,而getDerivedStateFromProps
是一个静态函数,也就是这个函数不能通过this访问到class的属性,也并不推荐直接访问属性。而是应该通过参数提供的nextProps以及prevState来进行判断,根据新传入的props来映射到state。
需要注意的是,如果props传入的内容不需要影响到你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量将其写到函数的末尾:
static getDerivedStateFromProps(nextProps, prevState) {
const {type} = nextProps;
// 当传入的type发生变化的时候,更新state
if (type !== prevState.type) {
return {
type,
};
}
// 否则,对于state不进行任何操作
return null;
}
hooks 为什么不能放在条件判断里
以 setState 为例,在 react 内部,每个组件(Fiber)的 hooks 都是以链表的形式存在 memoizeState 属性中
update 阶段,每次调用 setState,链表就会执行 next 向后移动一步。如果将 setState 写在条件判断中,假设条件判断不成立,没有执行里面的 setState 方法,会导致接下来所有的 setState 的取值出现偏移,从而导致异常发生。
什么原因会促使你脱离 create-react-app 的依赖
当你想去配置 webpack 或 babel presets。
传入 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
}
})