记录优化 React 项目的一些手段

记录可以优化 React 应用的手段。

防抖处理DOM事件的回调函数

这个优化常用于鼠标事件或者键盘事件中,更抽象一点来说,我们将其统称为频繁触发的事件。

使用 debounce 可以将频繁触发的事件,控制在我们规定好的时间间隔之后才真正被触发。

频繁触发的事件有很多,除了上述说的鼠标事件或者键盘事件之外,还有我们在使用 Redux 时也可能会遇到,这个需要我们根据实际情况去看了。

生产环境肯定使用 production 代码

在开发环境下 React 将载入更多的 React 代码库以帮助提示错误加载,并在终端里为我们提供其他的小消息。但是在生产环境中,我们就不需要这些东西了,所以对于生产环境的 React,它的体积就会小很多。

虚拟化处理长列表

这里介绍一个处理长列表的插件 react-window,里面的原理下来可以研究一下,比如 lazy-load 等等

Tree Shaking

引用方式的不同也会影响到打包构建的大小。

一般情况下,我们可能都习惯于使用以下语法来引用某些插件库提供的方法:

js 复制代码
import { debounce } from 'lodash'

这种方式下打包之后的体积可能会大一些,而要如何进行优化呢?

js 复制代码
import debounce from 'lodash/debounce'

只需要这种按需加载的方式,就可以减少打包后的文件体积。

webpack analyzer 查看包的大小

安装

bash 复制代码
yarn add -D webpack-bundle-analyzer

然后在 webpack 的配置文件中进行引入:

js 复制代码
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

再次启动项目后,除了会展示项目本身之外,还会启动一个 8888 端口的构建产物分析页面:

这个页面很清晰的为我们展示了项目中体积比较大的模块都有哪些,我们可以根据这个页面来针对性的做一些优化,减少它的体积。

容器化子组件内的状态

也就是说子组件的状态由子组件自身来进行维护,而不是由父组件进行维护,除非是子组件的公用状态,才会由父组件去维护。

使用 React 浏览器插件

假如我们安装了 React Dev Tools,那么在生产环境下,我们打开浏览器的控制台,可以看到两个选项「Components」和「Profiler」。

我们选择 Profiler 选项卡,点击左侧的「Start profiling」开始记录组件的渲染会话并查看我们的组件。

假如我们需要在生产环境来使用它,那就需要在打包构建的时候执行 npx react-scripts build --profile 命令进行构建项目

使用 Profiler 这个选项卡的目的是为了监控每个组件的渲染时长,我们也可以理解成监控每个组件的性能。

而使用 Components 选项卡则是可以看到页面上的组件构成或者说看到组件树的构成,以及每个组件的状态和 props。

使用 React.memo 优化组件函数

在 React 中一个普遍的现象是,当我们在 JSX 中更高的位置改变状态时,会导致触发一些其他子组件不必要的渲染。一个比较好的做法是使用 React.memo

  • useMemo 和 useCallback 也是 React 优化的一种手段

引入 why did you render 包来捕获不必要的更新

安装

bash 复制代码
npm i -D @welldone-software/why-did-you-render

然后在我们的业务组件目录下新建一个 wdyr.js

js 复制代码
import React from 'react';

if (process.env.NODE_ENV === 'development') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React, {
    trackAllPureComponents: true,
  });
}

之后把它引入到我们需要进行分析的组件中去(文档里直接引到了入口文件也是OK的,但是这个对于大项目来说信息就会太多了,所以建议我们需要分析哪个组件就给哪个组件使用):

js 复制代码
import './wdyr'; 

import 'react-hot-loader';
import {hot} from 'react-hot-loader/root';

import React from 'react';
import ReactDOM from 'react-dom';

import {App} from './app';

const HotApp = hot(App);

ReactDOM.render(<HotApp/>, document.getElementById('root'));

刷新页面并触发 rerender 的方法后,就会在控制台打印出来,是什么因素导致的重新渲染:

借此信息我们就可以对我们的组件进行优化了,MD真赞啊!

使用 React.lazy 来拆分组件

实际上说的就是懒加载。

在 React 中我们可以使用 React.lazy 来实现组件的懒加载:

js 复制代码
import React from "react";

const Dashboard = React.lazy(() => import("./pages/Dashboard/Dashboard.js"));

除了这个 API 之外,我们还需要 Suspense 内置组件来在 react 项目的 routes 里打配合:

js 复制代码
import React, { Suspense } from 'react';

function App() => {
  return (
    <Switch>
      <Sucpense callback={<div>...loading</div>}>
        {
          routes.map((item, index) => {
            
          })
        }
      </Suspense>
    </Switch>
  )
}

到这里我们再去刷新页面,就会发现之前我们只需要加载两三个 chunk,但是每个 chunk 的体积会很大,跳转到别的页面时不会再有新的 chunk 被加载进来;而现在会在当前页面加载特定的 chunk,且体积会很小,跳转至别的路由之后就会加载新的 chunk 进来,体积也视当前页面而定,一般情况下也是很小的,加载的时候都是毫秒级别的,速度很快。

我们也可以使用 @loadable/component来做上述的效果,此时在引入组件的文件(比如 routes.js)都不需要引入 React,直接使用 loadable来替换掉 React.lazy() 即可。替换之后,对应的 Sucpense 组件也可以不要了,最后的效果是一模一样的。

探讨❓:这个虽然能让我们的首屏加载速度得到提升,但是在切换路由时,会因为要"现做现卖",导致出现跳转之间会有一定的 loading,解决这个问题也很好办,既然有懒加载,那么对应的也就有预加载,我们结合两者就可以完美做到跳转无间隙。

相关推荐
Watermelo6179 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_7482489410 分钟前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_7482356122 分钟前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink5 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
LCG元7 小时前
【面试问题】JIT 是什么?和 JVM 什么关系?
面试·职场和发展
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-7 小时前
验证码机制
前端·后端
燃先生._.8 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js