译:如何构造我的JavaScript文件?

762 阅读8分钟

前言

看到英文技术文档、快哭了。含着泪也要把他读完。 原文How I Structure My JavaScript File

内容

很多人都在问我怎么写我的JavaScript——好吧,这是一个谎言,没人问我,但是如果他们这么做了,我想指出这篇文章。在使用了多年的PHP之后,在阅读了干净代码(和其他书籍)之后,我在多年中采用了我的代码风格。是的,PHP,不要敲它,它有一个伟大的社区和伟大的编码标准。当然,多年来和别人一起写作,跟随不同公司的风格。

结构并不依赖于JS模块,但我现在倾向于只编写JS模块,所以我将使用这些模块。

结构,总结如下:

入口

在文件的顶部是导入。这是有道理的,他们比其他任何东西都要高。进口的顺序无关紧要,除非你使用一些挂钩(比如babel hook),所以我倾向于选择:

  1. 本地模块-节点本地的东西。

  2. 库模块- lodash, knex,等等。

  3. 本地库——比如../db。

  4. 本地文件- like ./helper或类似的。

让我的模块保持有序,让我更容易看到我在导入什么,以及我实际上在使用什么。当我开始编写代码时,我也倾向于以这种方式编写依赖关系。

我倾向于不关心字母排序(除了破坏导入之外),我也没有看到它的一个点。

本地模块 我倾向于将本地模块放在最上面,并以这样的主题保持一个清晰的组织:

如果我在浏览器中,显然跳过这一步。

库模块

我尽可能只从库中导入我需要的东西,但是,我又一次将它们分组到某个主题中。

我还注意到,如果我正在执行一个默认的导入(例如。我倾向于把它放在我的库模块的顶部,并将被破坏的导入降低。没有必要,但我喜欢它的视觉效果。

Local/Internal libraries

在本地库中,我指的是本地共享的模块,比如db。设置与书架连接的js文件。或者,在我的工作中,我们有几个库处理我们产品中使用的数字和计算。

Local files 最后,我导入本地文件,这些文件通常与我正在处理的文件或一个目录(最多)在同一个文件夹中。例如,我为Redux写了一个减速器,并把它放在一个单独的文件夹中。在该文件夹中,我还保留了一个助手文件,通常命名为[reducer name]Helpers.js:

Constants

在导入所有依赖项之后,我通常会做一些前期工作,这些工作将在模块的其余部分中使用。例如,我从书架实例中提取了knex。或者我可以设置值常数。

使用非常量通常表示我依赖于某种类型的单例。我尽量避免使用它们,但有时它是必要的,因为没有简单的其他方法来完成它,或者它不重要(比如一次性命令行脚本)。 Exports

在我基本设置了所有模块级别的依赖项之后:无论它们是常量值还是导入库,我都试着将我的导出分组到文件的顶部。基本上,这就是我把功能作为模块的粘合剂,实现模块的最终目的。

在Redux的情况下,我可能导出一个单一的减速器,然后将工作分解并调用相关的逻辑。在ExpressJS的情况下,我可能在这里导出所有的路由,而实际的路由逻辑在下面。

我想说的是,这并不是我出口功能的唯一部分。

我感觉模块系统的工作方式使得在暴露最窄的API和导出函数在测试中使用它们之间的界限变得有点困难。

例如,在上面的例子中,我从来不想在模块之外使用calculateSomething。我不完全确定OOP语言是如何处理测试私有函数的,但这是一个类似的问题。 Core Logic

这看起来很奇怪,但核心逻辑对我来说是最重要的。我完全理解当人们翻转导出和核心逻辑时,但这对我来说很有效,原因有很多。

当我打开一个文件时,顶层函数会告诉我抽象步骤中将会发生什么。我很喜欢这样。我一眼就能看出文件的作用。我做了大量的CSV操作,并将其插入到DB中,而顶级函数始终是一个容易理解的流程,它有一个类似的流程:fetchCSV aggregateData insertData terminate script。

核心逻辑总是包括从上到下的出口。在内联的例子中,我们有这样的东西:

注意,readCSV没有。这听起来很普通,我应该把它放到一个helper文件中,然后将它导入上面。除此之外,你可以看到我的出口,而不是进退两难。我不希望在模块外部提供聚合数据,但我仍然希望测试它。

在此之外,我倾向于把“meatier”函数放在上面,下面的函数更小。如果我有一个特定于模块的实用函数,一个我在多个地方使用的函数,但只在模块中使用,我将把它们放在底部。基本上,我的命令是:复杂性+使用。

所以顺序是:

  1. 核心逻辑函数——由顶级导出使用的函数。

  2. 更简单/更小的函数——核心逻辑函数使用的函数。

  3. 实用函数——模块周围多个地方使用的小函数(但不导出)

Core-logic functions 核心逻辑函数就像我导出的函数的“sub-glue”。根据模块的复杂性,这些可能存在,也可能不存在。功能的分解不是必需的,但是如果一个模块足够大,核心逻辑函数就像主函数中的步骤。

如果你写的是反应或角度,这些你的组件将是我上面提到的出口函数。但是,核心逻辑函数将是各种侦听器或数据处理器的实现。用Express,这些将是您的特定路线。在一个Redux减速器中,这些将是在链条上足够远的单独的减速器,没有一个开关/case语句。

如果您处于角度,那么在类中组织这些函数而不是在整个文件的范围内是完全公平的。

Simpler/Smaller functions

这些函数通常是核心逻辑和纯实用程序之间的中间步骤。你可能会用到它们,或者它们可能只是一个比效用函数更复杂的tad。我可能会删除这个类别,并说“写你的功能,以减少复杂性或工作量”。

这里没有提及。也许onHandleInput事件监听器需要一些逻辑来处理$event数据,所以如果它是纯的,您可以将它从类中删除,如果不是,您可以在类中保留它:

Utility functions

最后,效用函数。我倾向于组织最接近我使用它们的工具。在相同的文件中,或者相同的文件夹(必要时),相同的模块,等等。每次使用从文件中扩展到项目的根或它自己的NPM模块时,我都将函数移出一个级别。

在我看来,效用函数应该始终是一种纯方法,这意味着它不应该在它的范围之外访问变量,而且应该只依赖传递到它的数据,而且不需要任何形式的副作用。除非使用实用程序函数来访问API或访问数据库。因为这些被认为是副作用,所以我认为它们是唯一的例外。

Anything else? 当然!我认为每个人都有自己独特的书写方式。上面所描述的结构在我多年来每天编写大量代码的过程中非常有效。最终,许多细微的差别开始出现,我发现自己编写代码更快,更喜欢它,并且更容易调试和测试。

在我完成这篇文章之前,我想和大家分享一些我已经习惯了的编码,它们与文档结构的关系更少,更多的是在编写实际代码时更倾向于使用小的偏好。

早期的回报

当我发现早期的回报时,那是一瞬间。当您可以提前返回时,为什么要在else语句中封装大量代码?

我的经验法则是,如果早期的返回条件小于其余的代码,那么我将写早期的返回,但是如果不是,我将颠倒代码,这样较小的代码块总是提前返回。

早期的回报在交换机上也很出色,我是Redux的超级粉丝。

分号块

虽然我不再使用它(没有更漂亮的支持),但我总是用分号来终止函数链接,在一个单独的行上,一个缩进到链的缩进的左边。这就创建了一个整洁的代码块,代码不只是挂在那里。

当然,这意味着我也喜欢用分号来代替。

Or better written, it might look like this:

总结

使用的是有道在线翻译,如有不正确之处还望大佬指出。