05.04 使用JavaScript和Node.js来有效重构-重写代码

你制定了一个计划,选择了一个工作,编写测试,现在你已经准备好清理一些代码。是时候了!你会被诱惑打开一个文件,并开始改变变量名称或清理一个已经让你疯狂的文件夹的内容。然而,在开始深入代码库之前,您应该熟悉一些概念以确保重构的顺利进行。

在本系列的最后部分,我将介绍一些在重写过程中可能遇到的常见错误。我还会介绍一些加速一些更乏味的任务的技巧。我不会涵盖与重构相关的特定机制,定义或流程。

使用JavaScript和Node.js来有效重构-重写代码

识别自动化的机会

有一个很好的机会,你会有一些项目任务会很乏味。我遇到的最大的罪犯之一是移动文件。例如,我重构的应用程序在各自的文件夹中有Redux操作、还原器和选择器。我的一个项目任务是通过模块(例如appActions)对Redux文件进行分组。js,appReducer.js和appSelectors.js)。我可以运行一个git mv command to move /actions/app.js to /redux/app/appActions.js,然后做同样的for /reducers/app.js 和 /selectors/app.js,但这只需要应用程序模块。在这个项目中有11个模块,所以我必须为Redux元素输入33个git mv命令。您可能需要运行git mv 150多次才能在正确的文件夹位置获得响应容器和组件!

你会发现这种情况很快就会变得站不住脚。我没有写出所有这些命令,而是编写了一个脚本来使用JavaScript和Node.js为我完成:

const fs = require('fs');

const path = require('path');

const chalk = require('chalk');

const sh = require('shelljs');

const _ = require('lodash');

const sourcePath = path.resolve(process.cwd(), 'src');

// This is the new /src/redux folder that gets created:

const reduxPath = path.resolve(sourcePath, 'redux');

// I used "entities" instead of "modules", but they represent the same thing:

const entities = [

'app',

'projects',

'schedules',

'users',

];

const createReduxFolders = () => {

if (!fs.existsSync(reduxPath)) fs.mkdirSync(reduxPath);

// Code to create entities folders in /src/redux...

};

// Executes a `git mv` command (I omitted some additional code that validates

// if the file already exists for brevity).

const gitMoveFile = (sourcePath, targetPath) => {

console.log(chalk.cyan(`Moving ${sourcePath} to ${targetPath}`));

const command = `git mv ${sourcePath} ${targetPath}`;

sh.exec(command);

console.log(chalk.green('Move successful.'));

};

const moveReduxFiles = () => {

entities.forEach(entity => {

['actions', 'reducers', 'selectors'].forEach(reduxType => {

// Get the file associated with the specified entity for the specified reduxType,

// so the first file might be /src/actions/app.js:

const sourceFile = path.resolve(sourcePath, reduxType, `${entity}.js`);

if (fs.existsSync(sourceFile)) {

// Capitalize the reduxType to append to the file name (e.g. appActions.js):

const fileSuffix = _.capitalize(reduxType);

// Build the path to the target file, so this would be /src/redux/app/appActions.js:

const targetPath = `${reduxPath}/${entity}`;

const targetFile = `${targetPath}/${entity}${fileSuffix}.js`;

// Execute a `git mv` command for the file:

gitMoveFile(sourceFile, targetFile);

}

});

});

};

moveReduxFiles();

您可以在运行脚本后进入文件并更新相对路径,也可以编写脚本以使用字符串操作更新路径。自动化可以变得很花哨,但知道什么时候可以划清界限:花20个小时编写一个脚本来节省一个小时的手动工作是没有意义的。大多数代码库都是独特的,并且具有特定的结构,这意味着您可能无法重用这些脚本。

经常提交

对文件或文件集进行重大更改可能会破坏您的应用程序并导致测试失败。这是可以预料的。如果您做了一系列更改并且一切正常,请提交更改。如果您只做了一些小更新并且一切仍然有效,请提交更改。提交便宜。以下是我重构的应用的一些指标:

  • 提交1747次。

  • 378个关闭请求。

  • 横跨169个 Jest快照文件的42,017行文本。

  • 在181个测试文件中总共有13,555行文本。

  • 跨2个夹具文件(responses.js 和state.js)共24,261行文本。

  • 跨198个 JavaScript文件共16,685行文字。

  • 横跨111个 Sass文件的3,823行文本。

这是659个文件中的76,080行代码,并且存储库大小仅为10MB。做所有你想要的提交 - git可以处理它。如果你想清理你的历史,你可以随时挤压它们。我敦促你尽可能频繁地执行; 当重构一个大型应用程序时,你很快就会意识到追踪小问题有多难。

想象一下,对10个文件进行更改,验证您的应用程序仍然有效,并且对代码感到满意。您决定不提交您的更改,并且在重构另一半小时后,您会破坏某些内容。您可以花三个小时追踪错误,或者放弃所有更改并从头开始。由于您没有对这10个文件进行更改,因此您失去了完美的代码!

我并不是说要确定是否需要重置这些文件是不可能的,但是当您知道可以跳回最后一次提交并从那里开始时,为什么会浪费时间和精力。尝试将提交的更改限制在一次只提交几个文件,并经常提交,以便可靠地保存“保存点”。

避免诱惑

避免清理超出手头任务范围的代码。这是重构最困难的方面之一。假设您正在开发一项名为Standardize Redux Selector Names的任务,这些任务涉及更新选择器的名称,以确保所有这些名称都加上了前缀 select。作为风险较低,范围界定明确的比较简单的任务,应该顺利进行; 您只需更新选择器名称(以及引用这些选择器的任何文件)以适应更改(并且不要忘记测试!)。

假设你遇到一个正在使用的选择器 Object.assign(),而你未来的任务之一是更新代码以利用一些新的ESNext功能,如扩展语法。您可能会试图更新该代码,但不要!

你必须不偏离当前的任务。即使是最微不足道的改变也会让你走上错误的道路。即使滑坡的概念是一个合乎逻辑的谬误,经常会出现一个较不双曲的情况。你开始改变 Object.assign() 语句,在你知道它之前,已经过了四个小时,你只更新了两个选择器名称!

随着我重构任务的进展,我变得不那么容易受这种行为的影响。我// REFACTOR: Fix this later 在代码中添加了一条 评论,并检查了我的项目以确保我有一项任务来覆盖更改,然后我继续前进。

对拉取请求有一定的方法性

拉请求是重构项目的一个很好的工具,原因有很多,它们是:

  • 提供与项目任务相对应的更改记录。

  • 定义一个回滚点,如果引入新的错误。

  • 让同行审阅你的代码并寻找潜在的错误。

  • 提供解释您为什么进行某些更改的机会。

如果您花费大量时间清理代码库的一部分,则可能很难退后一步并评估更改的质量。拉取请求会让您的同事有机会查看您的更改并确定它们是否可以增加价值并改进现有的代码。

当您提交pull请求时,请在摘要中尽可能描述。将一个模板放在一起并将其包含在您的存储库中。如果你使用的是GitHub,这个模板将自动被拉入新的PR。尝试包括标题,高级描述,重要笔记的章节以及变更列表。变更清单不一定要广泛而罗嗦,但应包括审评人员应重点关注的重要变化。

您需要了解的一个指标是与请求相关的更改数量。尝试限制更改的数量为+/- 500行代码。添加Jest快照文件可以轻松地将数千行添加到拉取请求中,因此请务必在摘要中包含关于此的注释。如果你不能最小化改变的数量,努力减少复杂性。如果您只是重新排序导入语句,只要这些更改不是太复杂,就可以接受超过2000行的代码。尝试在单个PR中隔离这种变化,并确保您在摘要中描述变更的性质。它有助于在摘要的开头添加一个大胆的注释,例如“没有进行逻辑更改”,因此审阅者具有适当的上下文。

通过在拉取请求中编写描述性摘要并确保更改的范围与项目任务相对应,您将使审阅者变得更加轻松。我承认,我并不总是能够遵循我自己的建议(我仍然欠我的一些同事一顿午餐或10美元),但我确信我很快回复了询问并解决了问题。

结束

这就是重构系列!我希望我所提出的集体智慧将为您重构项目的成功做出贡献。重构不像重新编排代码库中的文件或重命名函数那么简单,但是如果您制定了一个稳定的计划,遵守该计划并定期更新,那么您将有一个良好的开端。拥有一个强大的测试基础架构将使您有信心在不破坏任何功能的情况下进行更改。

在执行方面,尽可能地自动化,经常实施,避免诱惑。拉请求是一个很好的文档和审查工具,所以确保他们的详细和范围有限。如果你想知道,这里是我的重构项目的结果:

  • 完成40项任务。

  • 12项待处理任务。

  • 1记录了Jira bug(直接与重构相关)。

  • 在181个测试套件中编写了1,473个测试(其中737个是快照测试)。

  • 108450线并将44241线除去。

  • 38个拉取请求。

  • 分支机构覆盖率95.07%。

添加和删除的行并没有多大意义,因为其中大部分代表了文件移动,快照和测试夹具文件。高测试覆盖率也应该带着一丝盐:我忽略了一些图表组件,因为很难测试HTML Canvas元素。我在重构过程中添加了几个功能并修复了各种错误,并且在测试覆盖率较高的情况下,在可能包含在发布之前,发现了几个潜在问题。总的来说,我认为重写肯定是值得的。


分享到:


相關文章: