携手创作,共同成长!这是我参与「日新计划 8 月更文挑战」的第 24 天,点击查看活动详情

起因

React 的开发工程中,不管是组件的设计也好,性能优化也好,它们之间彼此相对比较割裂,并不能反映真实的前端工程项目质量,也不能反映代码的质量。不管是在日常的工作还是面试中,我们都需要落地真实的代码,并且要知道代码如何写、如何放、以及如何用,才能更好的维护。因此在面试中,如何提升 React 代码的可维护性也是面试官常问的一个问题,今天我们就一起来探讨一下 React 代码可维护性的 最佳实践

当我们在探讨 React 代码的 可维护性 时,我们究竟是在探讨什么?很难用一句话来解释这个模糊的概念。

可维护性 是指代码的规范,还是指设计模式?如果我们要认真探讨这个问题,可以从多个维度展开,并没有一个标准的答案。

可维护性

表面上我们讨论的是 React 的代码,实际上是基于 React 开发的项目来展开,因此可以从 软件工程的角度 去尝试理解。

在软件工程中,可维护性的单词是 — Maintainability,与它相近的概念还有技术债和代码异变,这些名词都可以用来表示当前代码迭代的难易程度。简单来说,当项目可维护性很差的时候,往往意味着该项目既难以修改,也难以拓展。

当我们使用 React 构建项目时,可维护性通过什么额外的方案来保障呢?例如:特殊的文档规范、设计准则或是自动化检测工具等等。因此我们需要建立特征与方案之间的联系。

在实际的开发中,我们可以通过人工干预的手段对代码进行检查,俗称 Code Review,但是不可能所有的代码都进行 Code Review。工业的发展还是需要借助相关的工具,而通常在项目的开发中,我们能借助的工具就有JSLintJSHintESLint 等。业界基于ESLint总结了很多最佳实践,最经典的配置方案莫过于airbab出品的eslint-config-airbnb,这些规则方案将人工审查工作转化为工具自动化审查,节省了团队内部大量的时间,那么 Code Review 是否就没用了呢?其实并不是,因为工具只能校验代码的编写,而不能校验业务逻辑的正确性,因此更推荐团队内部将 Code Review 的重心放在代码的业务逻辑上。

在实际的项目发布中,我们需要配备有一定的兜底方案,主要是为了能够快速定位线上的报错。一般在生产环境中,代码通常都是经过 UglifyJS 进行混淆并压缩的,所以我们无法直接定位到具体的报错信息在什么地方,不利于进行问题的排查,因此我们需要考虑代码报错信息该如何收集,才能在线上环境报错后能够及时进行问题的定位和修复。

最理想的情况莫过于改造编译流水线,在发布的过程中将 sourcemap 上传到报错收集平台。目前业界开源并且比较好用的平台有 Sentry,我们可以通过修改 Webpack 的配置项,添加 sourcemap 的相关插件就可以在编译的过程中直接将 sourcemap 上传到 Sentry 的报错平台,如下代码所示:

const SentryWebpackPlugin = require("@sentry/webpack-plugin");
module.exports = {
    configureWebpack: {
        plugins: [
            new SentryWebpackPlugin({
                authToken: process.env.SENTRY_AUTH_TOKEN,
                org: "example-org",
                project: "example-project",
                include: ".",
                ignore: ["node_modules", "webpack.config.js"]
            })
        ]
    }
};

通过完成上述的 Webpack 配置后,我们在使用 Sentry 捕获报错信息时,就能够直接查看到相关的源码了,代码如下:

try {
    exampleFunction();
} catch (err) {
    Sentry.captureException(err);
}

我们通过 try...catch... 来捕获代码的报错信息,然后使用 Sentry.captureException() 将错误信息收集到 Sentry 的平台中。

如果当前的报错平台不支持 sourcemap 的访问,那我们该怎么办呢?我们可以使用 Mozilla 开源的工具 sourcemap,直接将报错代码恢复到对应的源代码信息,如下图所示:

不会吧?你还不知道如何提升 React 代码的可维护性?

可改变性

从代码的层面来说,可变性代表了代码的可拓展能力。我们可以从两个思路提升代码的可拓展性:

  • 从组件的角度出发,通过分离容器组件与木偶组件的方式来分离模块,我们可以通过 Storybook 或者 dumi 来沉淀展示组件。
  • 从状态管理框架出发,状态管理框架中有相当成熟的设计模式,例如 Reudx 中的 actionreducer等,它的边界很清楚,我们也很容易明白业务逻辑该如何拆解、如何放入模块中。

稳定性

在开发中,如果要增强项目的稳定性,从代码层面来说,常规的思路是需要添加测试,但写测试并不是一件容易的事情。

在我们开发的项目中,无论是单元测试还是集成测试,整体的覆盖比例都很低,往往只能通过测试人员进行人工的点点点的操作方法来保证代码的稳定性。

之所以测试代码的覆盖率低,主要的原因也是因为前端测试并不好写,这里说的不好写并不是指测试代码不好写,而是指 UI 层的测试不好写,因为我们项目的迭代周期都很短,短到你还没来得及编写相关的 UI 层测试代码,可能就要发布上线了,因此我们如果有一定的编写测试的条件,也是优先给核心业务代码写测试

例如:购物车中商品与价格的计算逻辑,每当有人修改代码时,必然会心惊胆战的,生怕一个不小心就造成生产事故。如果可以针对这样的核心业务逻辑代码编写一定的测试用例,并且尽量保证能够做到完全覆盖相关的内容,这样确保在后续的开发中修改相关代码后能够自动化的跑一次单元测试。

在前端开发的领域中,单元测试主要有 ChaiMochaJest,其中 JestReact 的生态最为紧密,也是 Facebook 主推的测试框架。由于 UI 层的迭代更新快,因此在代码的编写中,我们更应该选取业务核心逻辑代码编写相关的测试用例,这样更有利于整体项目的稳定性。

最后

总结来说,如果我们要提升 React代码的可维护性,就需要从多个不同的维度来展开。通过搜集 sourcemap 的报错信息来提升快速解决 bug 的能力;通过编写代码 测试 来提升代码的稳定性;以及通过 Code ReviewEslint 来对代码进行规范的约束的逻辑的检查,这样对代码的可维护性将会大大的提高,当然这些维度也不仅仅只适用于 React,在其它的项目中也可以基于相关的原则来进行代码的编写和维护。

最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家

往期回顾

在 React 中为什么要用JSX?

setState 是同步更新还是异步更新?

useEffect 与 useLayoutEffect 有什么区别?