面试的那些题-React篇
最近想复习总结下面试中遇到的所有面试题,以及自己觉得可能出现的面试题,又或者是我觉得不错的需要掌握的知识。这是Master the FED Interviews系列的第五篇:关于React
前言
秋招提前批已经基本结束了,即将进入金九银十,正式的号角已经打响。春招,以及秋招提前批一路过来,断断续续也面了一些公司,自己在笔记上也有总结,甚至自己进行过一些猜题。发现基本问到的问题八九不离十,但是有些知识,特别是偏工程的知识点,如果没遇到过,很难产生深刻的印象。结合自己之前的笔记,也想在正式进入9月之前,整理一个面试题集系列,加深理解
这是这个系列的第五篇,关于React
React作为一个火爆的前端框架,面试时被问到的基本原理题还是有点,现在我们越来越喜欢直接上手一个框架,而往往忽略一些基础,这篇文章也当做复习React基础知识了
一、React的生命周期
React组件有三个生命周期,每种生命周期有一些函数可以调用,完成某些功能。其中以will作为前缀的方法在经历周期之前调用,以did作为前缀的方法在经历周期之后调用
-
Mount,React组件初始化之后需要挂载到真实DOM,需要经历挂载这个生命周期,其中有几个重要的方法
constructor(),这个构造函数方法一般做两件事,首先是初始化组件状态;其次是组件中方法的作用域绑定。一般如果无需这两个操作,可以不定义constructor()方法,React.Component会默认实现
class foo extends React.Component {
constructor() {
super();
this.state = {
name: "foo"
}
}
setName(name) {
this.setState({name});
}
}
// React.Component默认实现的constructor()
constructor(props) {
super(props);
this.state = {
color: props.initialColor
};
}
render(),React使用该方法生成一颗Virtual DOM树
componentDidMount(),React组件被加载进入真实DOM后执行该函数,在该函数中可以发Ajax请求,绑定事件处理函数,添加计时器,操作真实DOM等等
-
Update,当React组件的state或者props发生变化时,需要经历更新这个生命周期,其中有几个重要的方法
static getDerivedStateFromProps(nextProps, prevState),组件props改变,或者父组件引发的渲染会调用该方法,该方法根据组件props决定是否更新组件state,返回一个state对象用于更新state,或返回null表示无需更新。该方法也可以在挂载生命周期中使用
shouldComponentUpdate(nextProps, nextState),React默认是props或state发生改变默认更新,如果我们需要自行决定是否更新,则需要自己实现这个方法。该方法返回false,则组件会停止更新,周期后面的方法不会调用 componentDidUpdate(prevProps, prevState),更新发生之后调用该方法,可以实现DOM操作
-
Unmount,当关闭一个网页,或者Virtual DOM树的根节点元素完全改变时,组件会经历卸载这个生命周期,其中只有一个比较重要的方法
componentWillUnmount(),该方法一般用来解除事件绑定,取消定时器等等,防止内存泄漏
二、State VS Props
先说说它们的相同点
- 两者都是JavaScript对象
- 两者的改变都会引起React组件的更新
两者何时可以发生改变
props | state | |
---|---|---|
能否从父组件获取初始值 | 能 | 能 |
值能否被父组件改变 | 能 | 不能 |
能否设置默认值 | 能 | 能 |
能否在组件内发生改变 | 不能 | 能 |
能否用来设置子组件的Props或State | 能 | 能 |
能否在子组件中被改变 | 不能 | 能 |
总结来说
- state用于实现组件内部的逻辑,props定义了一个外部的接口
- props的初始化赋值在父组件中,state则在自身组件中
- 父组件可以通过传值给子组件,作为子组件的props,子组件再使用props初始化state
- 父组件也可以通过传递回调函数给子组件,从而在子组件中调用回调函数修改父组件state
参考
三、React Element VS React Component
- React Element是构建React应用的基本单元,描述了我们在屏幕上看到的东西,具有不可变性(immutable),也就是每次
render()
方法返回的都是全新的React Element - React Element只是一个对象,并不是真实的DOM节点,最终要由ReactDOM根据该对象生成真实的节点(
ReactDOM.render()
)。通常我们不直接使用React Element,而是从React Component的render函数中返回 - React Component是一个抽象概念,是React中可重用的代码块,它可以返回React Element用于渲染真实DOM
- 如果页面的一部分经常被重复使用,或者逻辑复杂,就可以考虑将其包装为Component
- Component是用户自定义的,Component也可以返回Component,而React Element是本来就存在的纯对象
四、类组件(Class Components) VS 函数组件(Functional Components)
// 函数组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 类组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
上面的代码中,两者对于React来说没有区别,因为函数返回的也是一个React Element
但是函数组件功能单一,如果只需要返回一个React Element或者React Component的时候,使用功能组件即可
如果组件有状态或者需要使用一些生命周期函数,则需要使用类组件
五、受控组件(Controled Component) VS 非受控组件(Uncontroled Component)
- 对于受控组件,所有的组件数据托管在组件中,不为真实DOM所有
- 对于非受控组件,通过ref绑定真实DOM元素,组件数据可以从真实DOM元素中获得
关于受控组件和非受控组件的一个最好的例子就是如果需要获取<input>
元素的值,是否需要对其绑定onChange
事件
- 如果不绑定事件,则直接从元素中获取value,需要使用ref绑定真实DOM,之后便可以直接访问
value
,此时作为非受控组件 - 如果绑定
onChange
事件,每一次事件调用时便可以通过setState()
设置组件的state,值可以通过onChange
方法的默认参数:事件对象e.target.value
中获得,此时为受控组件
六、显示组件(Presentational Component) VS 容器组件(Container Component)
关于这两种组件,还有哑组件(Dumb Coponent)和聪明组件(Smart Component)的说法
顾名思义,显示组件做的工作就是只展示页面元素,而容器组件要实现的就是页面的业务逻辑
两者有下面的区别
显示组件 | 容器组件 | |
---|---|---|
目的 | 展示页面 | 实现页面交互逻辑 |
数据来源 | 自身props | 与其他组件通信得到或者发出请求得到 |
响应交互操作 | 使用容器组件提供的回调函数 | 触发真正的操作 |
关于这两者的概念,诠释得最清晰的是Redux,Redux的connect()
方法可以将一个显示组件包装成为一个容器组件,并且将容器组件获得的数据通过props传给显示组件进行显示,同时,将修改状态的回调方法也通过props传给显示组件,让其可以调用回调方法完成交互
看下面一幅图就明白了
Redux Flow by buckyroberts
七、React高阶组件(HOC)
React High Order Component (HOC) 是React用来复用组件逻辑的一种技术。本质上来说,高阶组件就是一个函数,函数接收一个组件作为参数,在此基础上增加一些新的功能,最后返回一个新的组件,例如
function logProps(WrappedComponent) {
return class extends React.Component {
componentWillReceiveProps(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
}
高阶组件在React第三方库中出现得比较多,其中最为常见的是React-Redux中的connect()
,该方法的返回值就是一个高阶组件,下面是该函数的模型
function connect(mapStateToProps, mapDispatchToProps) {
return function (WrappedComponent) {
// we return a Wrapper component:
return class extends React.Component {
render() {
return (
<WrappedComponent
{...this.props}
/>
)
}
}
}
}
还有一点需要注意,HOC看起来和前面提到的Container Component非常相似,下面是React官网给出的解释
You may have noticed similarities between HOCs and a pattern called container components. Container components are part of a strategy of separating responsibility between high-level and low-level concerns. Containers manage things like subscriptions and state, and pass props to components that handle things like rendering UI. HOCs use containers as part of their implementation. You can think of HOCs as parameterized container component definitions.
其实大致意思就是,HOC实现了Container Component,它的返回值就是这个,只不过它作为一个函数,可以传递额外的参数来增强Container Component的功能
使用HOC有如下几个好处
- HOC可以使用同一个Component,返回不同的Container Component,可以复用组件,减少代码
- HOC可以传递不同的数据源作为参数,方便我们使用不同的数据源