React开发中应知道的知识(一)
解答React的使用闭坑
React中 keys 的作用
Keys是React用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识
- 在开发过程中,我们需要保证某个元素的
key在其同级元素中具有唯一性。在React Diff算法中React会借助元素的Key值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助Key值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中Key的重要性
传入 setState 函数的第二个参数的作用
该函数会在
setState函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成:
1this.setState(
2 { username: 'tylermcginnis33' },
3 () => console.log('setState has finished and the component has re-rendered.')
4)1this.setState((prevState, props) => {
2 return {
3 streak: prevState.streak + props.count
4 }
5})React中refs 的作用
Refs是React提供给我们的安全访问DOM元素或者某个组件实例的句柄- 可以为元素添加
ref属性然后在回调函数中接受该元素在DOM树中的句柄,该值会作为回调函数的第一个参数返回
在生命周期中的哪一步你应该发起 AJAX 请求
我们应当将AJAX 请求放到
componentDidMount函数中执行,主要原因有下
React下一代调和算法Fiber会通过开始或停止渲染的方式优化应用性能,其会影响到componentWillMount的触发次数。对于componentWillMount这个生命周期函数的调用次数会变得不确定,React可能会多次频繁调用componentWillMount。如果我们将AJAX请求放到componentWillMount函数中,那么显而易见其会被触发多次,自然也就不是好的选择。- 如果我们将
AJAX请求放置在生命周期的其他函数中,我们并不能保证请求仅在组件挂载完毕后才会要求响应。如果我们的数据请求在组件挂载之前就完成,并且调用了setState函数将数据添加到组件状态中,对于未挂载的组件则会报错。而在componentDidMount函数中进行AJAX请求则能有效避免这个问题
shouldComponentUpdate 的作用
shouldComponentUpdate允许我们手动地判断是否要进行组件更新,根据组件的应用场景设置函数的合理返回值能够帮我们避免不必要的更新
如何告诉 React 它应该编译生产环境版
通常情况下我们会使用
Webpack的DefinePlugin方法来将NODE_ENV变量值设置为production。编译版本中React会忽略propType验证以及其他的告警信息,同时还会降低代码库的大小,React使用了Uglify插件来移除生产环境下不必要的注释等信息
概述下 React 中的事件处理逻辑
为了解决跨浏览器兼容性问题,
React会将浏览器原生事件(Browser Native Event)封装为合成事件(SyntheticEvent)传入设置的事件处理器中。这里的合成事件提供了与原生事件相同的接口,不过它们屏蔽了底层浏览器的细节差异,保证了行为的一致性。另外有意思的是,React并没有直接将事件附着到子元素上,而是以单一事件监听器的方式将所有的事件发送到顶层进行处理。这样React在更新DOM的时候就不需要考虑如何去处理附着在DOM上的事件监听器,最终达到优化性能的目的
react生命周期函数
react旧版生命周期函数
初始化阶段
getDefaultProps:获取实例的默认属性getInitialState:获取每个实例的初始化状态componentWillMount:组件即将被装载、渲染到页面上render:组件在这里生成虚拟的DOM节点componentDidMount:组件真正在被装载之后
运行中状态
componentWillReceiveProps:组件将要接收到属性的时候调用shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了)componentWillUpdate:组件即将更新不能修改属性和状态render:组件重新描绘componentDidUpdate:组件已经更新
销毁阶段
componentWillUnmount:组件即将销毁
新版生命周期
在新版本中,React 官方对生命周期有了新的 变动建议:
- 使用
getDerivedStateFromProps替换componentWillMount; - 使用
getSnapshotBeforeUpdate替换componentWillUpdate; - 避免使用
componentWillReceiveProps;
其实该变动的原因,正是由于上述提到的
Fiber。首先,从上面我们知道 React 可以分成reconciliation与commit两个阶段,对应的生命周期如下:
reconciliation
componentWillMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdate
commit
componentDidMountcomponentDidUpdatecomponentWillUnmount
在
Fiber中,reconciliation阶段进行了任务分割,涉及到 暂停 和 重启,因此可能会导致reconciliation中的生命周期函数在一次更新渲染循环中被 多次调用 的情况,产生一些意外错误
新版的建议生命周期如下:
1class Component extends React.Component {
2 // 替换 `componentWillReceiveProps` ,
3 // 初始化和 update 时被调用
4 // 静态函数,无法使用 this
5 static getDerivedStateFromProps(nextProps, prevState) {}
6
7 // 判断是否需要更新组件
8 // 可以用于组件性能优化
9 shouldComponentUpdate(nextProps, nextState) {}
10
11 // 组件被挂载后触发
12 componentDidMount() {}
13
14 // 替换 componentWillUpdate
15 // 可以在更新之前获取最新 dom 数据
16 getSnapshotBeforeUpdate() {}
17
18 // 组件更新后调用
19 componentDidUpdate() {}
20
21 // 组件即将销毁
22 componentWillUnmount() {}
23
24 // 组件已销毁
25 componentDidUnMount() {}
26}使用建议:
- 在
constructor初始化state; - 在
componentDidMount中进行事件监听,并在componentWillUnmount中解绑事件; - 在
componentDidMount中进行数据的请求,而不是在componentWillMount; - 需要根据
props更新state时,使用getDerivedStateFromProps(nextProps, prevState);- 旧 props 需要自己存储,以便比较;
1public static getDerivedStateFromProps(nextProps, prevState) {
2 // 当新 props 中的 data 发生变化时,同步更新到 state 上
3 if (nextProps.data !== prevState.data) {
4 return {
5 data: nextProps.data
6 }
7 } else {
8 return null1
9 }
10}可以在componentDidUpdate监听 props 或者 state 的变化,例如:
1componentDidUpdate(prevProps) {
2 // 当 id 发生变化时,重新获取数据
3 if (this.props.id !== prevProps.id) {
4 this.fetchData(this.props.id);
5 }
6}- 在componentDidUpdate使用setState时,必须加条件,否则将进入死循环;
- getSnapshotBeforeUpdate(prevProps, prevState)可以在更新之前获取最新的渲染数据,它的调用是在 render 之后, update 之前;
- shouldComponentUpdate: 默认每次调用setState,一定会最终走到 diff 阶段,但可以通过shouldComponentUpdate的生命钩子返回false来直接阻止后面的逻辑执行,通常是用于做条件渲染,优化渲染的性能。