React-使用总结

聊聊我对 react 的理解。

  • react 历史发展。
  • react 性能优化。

React 历史发展

Jquery 时代

在 jquery 主流的时代,web 页面开发流程是通过 ajax 获取到后端数据,然后使用 jquery 生成 jquery 更新到页面中,但是随着业务发展,我们的项目可能会越来越复杂,我们每次请求到数据,或者数据有更改的时候,我们又需要重新组装一次 dom 结构,然后更新页面,这样我们手动同步 dom 和数据的成本就越来越高,而且频繁的操作 dom,也使我我们页面的性能慢慢的降低。

痛点:手动同步 dom 和数据频繁地操作 dom

双向数据绑定 MVVM

解决手动同步 dom 和数据的问题,这个时候 mvvm 出现了。mvvm 的双向数据绑定可以让我们在数据修改的同时去更新 dom,dom 的更新也可以直接同步到数据的更改,这个特定可以大大降低我们手动去维护 dom 更新的成本。虽然 react 属于单项数据流,但是我们可以手动实现双向数据绑定。

虚拟 DOM

有了 mvvm 还不够,如果每次有数据做了更改,我们都全量更新 dom 结构的话,也没办法解决频繁操作 dom的问题。为了解决这个问题,react 内部实现了一套虚拟 dom 结构,也就是用 js 模拟的一套 dom 结构,他的作用是将真实 dom 在 js 中做一套缓存,每次有数据更改的时候,react 内部先使用算法,也就是鼎鼎有名的 diff 算法对 dom 结构进行对比,找到那些我们需要新增、更新、删除的 dom 节点,然后一次性对真实 dom 进行更新,这样就能大大降低操作 dom 的次数。

那么 diff 算法是怎么运作的呢?

  • 递归所有虚拟 dom 节点,逐层级,逐顺序进行比较。
  • 类型不同的节点,会直接删除原来节点,并用新的节点来代替。
  • 节点类型相同的节点,会对比这个节点的所有属性。
    • 如果节点的所有属性相同,那么判定这个节点不需要更新。
    • 如果节点属性不相同,那么只会更新这个节点的属性。
  • 对于同一层级的元素,如果只是改变了顺序,diff 算法会根据 key 去优化。
    • 有 key,算法只需要调整一下虚拟 dom 的顺序即可,不用删除,重建。
    • 没有 key,会严格按顺序进行比较,发现不同就销毁,重建。

:::tip 虚拟 dom 的其他优势

  • 将 虚拟 dom 作为一个兼容层,让我们还能对接非 web 端的系统,实现跨端开发。
  • 同样的,通过 虚拟 dom 我们可以渲染到其他的平台,比如实现 ssr、同构渲染等等。
  • 实现组件的高度抽象化

:::

状态管理

react 设计之初是主要负责 ui 层的渲染,每个组件有自己的状态,当状态需要变化的时候,需要使用 setState 更新我们的组件。但如果我们想渲染一个组件的兄弟组件,我们就需要将组件的状态提升到父组件当中,让父组件来管理这两个组件的渲染,当我们组件的层次越来越深的时候,状态需要一直往下传,无疑加大了我们代码的复杂度,我们需要一个状态管理中心,来帮我们管理 state。

Redux

这个时候,redux 出现了,我们可以将所有的 state 交给 redux 去管理,当我们的某一个 state 有变化的时候,依赖到这个 state 的组件就会进行一次重渲染,这样就解决了我们的我们需要一直把 state 往下传的问题。redux 有 action、reducer 的概念。

  • action 为唯一修改 state 的来源。
  • reducer 为唯一确定 state 如何变化的入口。

通过以上规范,使得 redux 的数据流变得非常清晰,但同时也暴露出了 redux 代码的复杂,本来那么简单的功能,却需要完成那么多的代码

Mobx

后来,社区就出现了另外一套解决方案,也就是 mobx,它推崇代码简约易懂,只需要定义一个可观测的对象 store,哪个组件需要状态就进行状态注入,store 中的数据改变,会通知注入过状态的组件进行更新。

而且 mobx 内部实现了 shouldComponentUpdate 用来解决常用的性能问题,这使得我们使用 mobx 开发项目的时候可以简单快速的完成很多功能。但随着项目的不断变大,mobx 也不断暴露出了它的缺点,就是数据流太随意,不好追溯数据的流向,这个缺点正好体现出了 redux 的优点所在,所以针对于小项目来说,社区推荐使用 mo 大专栏  React-使用总结bx,对大项目推荐使用 redux

Context API

随着 react 生态不断发展,状态管理已成为 react 生态圈必不可少的技术,react 官方也在 v16.3.0 版本上推出了自带的状态管理 API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

const ThemeContext = React.createContext('light');

class extends React.Component {
state = { theme: 'light' };

render() {
return (
// 给子组件提供 context
<ThemeContext.Provider value={this.state.theme}>
{this.props.children}
</ThemeContext.Provider>
);
}
}

class ThemedButton extends React.Component {
render() {
return (
// 子组件使用 context
<ThemeContext.Consumer>
{theme => <Button theme={theme} />}
</ThemeContext.Consumer>
);
}
}

React 性能优化

我们都知道,react 项目渲染时会分为 2 个阶段。

  • 生成虚拟 dom。
  • 虚拟 dom 渲染成真实 dom。

其中,虚拟 dom 渲染成真实 dom 的过程,这部分是 React 内置的功能,我们不需要再进行优化,所以我们探讨一下生成虚拟 dom 阶段的优化。

  • 避免直接操作 dom,将与 dom 操作的事都丢给 react 去做。
  • 由于 dom diff 算法的问题,对于同一层级的组件,建议加上 key 属性,方便 react 进行 dom diff 时能够保持复用,而不至于直接创建一个新的元素。
  • 抽离无状态组件,无状态组件渲染时不需要 new 实例化,可以提高 js 效率;由于无状态组件基于函数式编程思想,同样的属性,必定会渲染出相同的内容,可以起到缓存作用。
  • 使用 immutable 库,避免修改引用类型造成副作用,高效实现对象深拷贝,比传统的 deepClone 效率高。
  • 抽离 render 函数中的变量和方法,避免每一次 render 方法执行都要重新创建一次。例如我们常常使用 bind 函数区绑定 this,应提前到 constructor 中。
  • 使用 PureComponent 代替 Component,相当于是用 shouldComponentUpdate 做了一次浅比较,可以优化不必要的渲染,但对于引用类型的属性,应做好 immutable 处理,以免造成组件不会渲染等问题。
  • 使用 recompose 库通过函数式写法业务逻辑和组件分离,作者已停止维护,建议用 hooks 代替。
  • 使用 react hooks 编写高度可复用的函数式组件,编写更少的代码,更精炼的生命周期实现更高的性能。

相关链接

原文地址:https://www.cnblogs.com/lijianming180/p/12401965.html