JavaScript 项目最佳实践指南

在开发一个新项目时,就像在一个绿色场地上来回翻滚,维护它对其他人来说可能是一个潜在的黑暗而扭曲的恶梦。以下是我们发现、编写和收集的指南列表(我们认为)在 hive 上的大多数 JavaScript 项目都能很好地工作。如果你想分享最佳做法,或者认为应该删除这些指南之一,请随时与我们分享。

  • Git
  • 文档
  • 环境
  • 依赖
  • 测试
  • 结构和命名
  • 编码风格
  • 日志
  • API 设计
  • 协议

1. Git

1.1 一些 Git 相关的规则

有这样一些规则需要牢记。

  • 开一个独立的 feature 分支写代码为什么:

    因为这样就可以在一个专用的分支上把所有的开发工作都做完,而不会动主分支。你就能提交多次推送请求而不致把事情搞乱,不会因为写了潜在的不稳定、未完成的代码而搞坏了 master 分支。了解更多…

  • 分支要同 develop 分支独立出来为什么:

    这样你就可以确保 master 分支里面的代码总是可以无误的通过构建, 并且总是可以直接用来做发布 (这一点对于有些项目而言可能有点矫枉过正了)。

  • 永远不要向 develop 或者 master 分支推送,而是要发起 Pull Request。为什么:

    这样做会通知团队成员说他们已经完成了一项功能的开发。同时也能很容易就进入同行代码评审的流程,并且能有一个把拟提交功能拿到论坛进行充分讨论的过程。

  • 在推送你开发的功能并发起一次 Pull Request 之前,要更新你的 develop 分支然后做一次代码库重设。为什么:

    重设会在所请求的分支( master 或者 develop )中进行代码合并,并将你在本地所做的提交应用到顶层的历史记录之上,而无需再去创建一次合并提交(假设写的代码没冲突)。这样做能维持一个整洁干净的历史记录。了解更多 …

  • 在进行重设并发起 Pull Request 之前先解决潜在的代码冲突。
  • 在合并之后要删掉本地和远程的 feature 分支。为什么:

    已经没用的分支会把你的分支列表搞得乱七八杂。这样做可以确保你只会讲分支合并到 master 或者 develop 中一次。Feature 分支应该只存在于开发工作还在进行的那段时间。

  • 在发起一次 Pull Request 之前,要确保 feature 分支可以成功构建并且可以通过所有的测试(包括代码风格的检查)。为什么:

    现在是你要把代码添加到一个稳定的分支上。如果你的 feature 分支测试失败了,就有极高的风险导致目标分支也构建失败。此外你还需要在发起一次 Pull Request 之前进行一下代码风格检查。这样做有助于提高代码可读性,并减少在实际的修改中所进行的格式化修复工作变乱。

  • 使用这个 .gitignore 文件为什么:

    这个文件里面已经有了一份列表,列出来那些不应该同你的代码一起被发送到远程代码库的系统文件。此外,它还排除了哪些最常被使用的编辑器的设置目录以及文件,还有最常用的依赖目录。

  • 保护你的 develop 和 master 分支。为什么:

    这样做可以让你的准生产分支不受不可预期且不可回退的修改的破坏。从 Github 和 Bitbucket 了解更多。

1.2 Git 工作流程

大多是由于上述的原因,我们使用带有主动重设功能特性分支开发工作流(Feature-branch-workflow还有 Gitflow (命名并拥有一个 develop 分支)的一些元素。主要的步骤如下:

  • 检出一个新的 feature/bug-fix 分支
  • 修改代码

    为什么:

    git commit -a 会启动一个编辑器,让你能将代码的修改的主题从代码中分离出来。在章节 1.3 中可以了解到更多

  • 与远程进行同步,获取本地错过的那些修改。

    为什么:

    这样做让你可以在(稍后)进行重设的时候有机会在自己的机器上处理代码冲突,而不是创建出一个包含了冲突的 Pull Request。

  • 通过主动进行重设来从 develop 分支拿到最新的修改更新 feature 分支。

    为什么:

    你可以使用 –autosquash 来将所有的提交操作捆成一次提交。没有人会想要在 develop 分支中针对一个功能的开发做多次提交。了解更多…

  • 如果你的代码里并没有冲突,那就跳过这个步骤。如果有冲突,那就先处理掉它们然后再继续进行重设
  • 推送你的分支。重设会改变历史记录,所以你得使用 -f 来将修改推送到远程分支。如果另外还有一个人正在你的分支上写代码,就使用负面影响较小的 –force-with-lease 吧。

    为什么:

    当你在进行一次重设的时候,就是在对你的 feature 分支上的历史记录做变更。这样做的结果就是 Git 会拒绝一般的 git 推送,这时候你就需要用到 -f 或者 –force 标识了。了解更多…

  • 发起一次 Pull Request。
  • Pull request 将会被审查人接收,合并到主分支以后关闭。
  • 如果上述的工作都做完了,就可以删除你本地的 feature 分支了。

1.3 书写良好的 commit 信息

制定并坚持使用的良好的 commit 指导方针,能让 Git 与他人协作更容易。这里有一些经验原则(来源):

  • 将主题行与正文之间用换行符分开为什么:

    拥有正文部分可让您对代码审阅者进行有用的解释说明。如果您可以链接到相关联的 Jira ticket(bug 管理系统)、GitHub issue 、Basecamp to-do 等。大多数桌面 Git 客户端在其 GUI 中的主题行和正文之间都有明确的分隔。

  • 将主题行限制为50个字符
  • 对主题行进行大写
  • 不要用时间来作为主题行的结尾
  • 在主题行中使用必要的祈使语气(imperative mood
  • 将正文限制在72个字符以内
  • 正文应该是用来解释是什么,为什么,而不是怎么办

2. Documentation 文档

  • 使用此模板创建 README.md ,随意添加未覆盖的内容。
  • 对于具有多个存储库的项目,请在各自的 README.md 文件中提供它们的链接。
  • 随着项目的发展,保持 README.md 的更新。
  • 注释你的代码,尽可能使每个主要部分的目标尽可能清晰。
  • 如果在 github 或 stackoverflow 有关于你所使用的代码或方法的公开讨论,请在你的注释中包含对应链接。
  • 不要将注释作为糟糕代码的借口。 保持你的代码干净。
  • 不要使用纯净的代码作为借口,根本不注释。
  • 随着代码的发展,保证注释相关。

3. 环境

  • 根据项目规定,定义单独的开发、测试和生产环境。为什么:

    不同的环境可能需要不同的数据、令牌、API、端口等。您可能需要一种独立的开发模式,可以调用返回可预测数据的虚拟 API ,从而使自动化和手动测试更容易。或者您可能只想在生产环境加入 Google Analytics 等等。阅读了解更多…

  • 从环境变量加载您的特定配置,不要将它们作为常量添加到代码中,请查看此示例为什么:

    您配置的令牌、密码和其他有价值的信息,应该正确地与应用程序分开,就像代码可以随时公开一样。

    怎么做:

    使用 .env 文件来存储变量,并将它们添加到 .gitignore 中以便被 git 排除。提交一个 .env.example 作为开发人员的指南。对于生产环境,您仍然应该以标准方式设置环境变量。阅读了解更多

  • 建议您在应用程序启动之前验证环境变量。使用 joi 查看这个简单验证提供的值 示例为什么:

      总有一天它会拯救某人的故障排查。

3.1 开发环境一致性:

  • 在 package.json 中设置 node 版本为什么:

    它让别人知道项目工程的 node 版本。 阅读更多…

  • 另外,使用 nvm 并在您的项目根目录中创建一个 .nvmrc 。 不要忘了在文档中提及它为什么:

    任何使用 nvm 的人都可以使用 nvm 来切换到合适的 node 版本。 阅读更多…

  • 您还可以使用 preinstall 的脚本来检查 node 和 npm 版本为什么:

    某些依赖项可能会在较新版本的 node 中使用失败。

  • 使用 Docker 镜像,只要它不会使事情更复杂为什么:

    它可以在整个工作流程中为您提供一致的环境。不需要操心 libs 、依赖或配置。 阅读更多…

  • 使用本地模块安装,而不是使用全局安装的模块为什么:

    让你与你的同事分享你的工具,而不是期望在他们的系统上安装它。

4.依赖项

在使用包之前,请检查它的 GitHub 。 检查问题的数量,每日下载次数和贡献者数量以及上次更新软件包的日期。

  • 如果需要了解依赖项,请在使用之前与团队进行讨论。
  • 跟踪您当前可用的软件包:例如,npm ls –depth = 0。 阅读更多…
  • 查看您的任何包是否已被使用或不相关:depcheck。 阅读更多…
  • 检查下载统计信息以查看依赖项是否被社区大量使用:npm-stat。 阅读更多…
  • 检查依赖项是否具有良好的成熟版本发布频率与大量维护者:例如,npm view async。 阅读更多…
  • 始终确保您的应用程序适用于最新版本的依赖项,而不会被破坏:npm outdated。 阅读更多…
  • 检查软件包是否存在已知安全漏洞,例如 Snyk 。

4.1 一致性依赖:

  • 在 npm@5 或更高版本上使用 package-lock.json
  • 对于旧版本的 npm,在安装新的依赖关系时使用 –save -save-exact,并在发布之前创建 npm-shrinkwrap.json 。
  • 或者,你可以使用 Yarn ,并确保在 README.md 中提及它。你的 lock 文件和 package.js 在每次依赖关系更新后都应具有同样的版本。
  • 在这里阅读更多:package-locks | npm Documentation

5. Testing 测试

  • 如果可能,请使用测试模式的环境。
  • 将测试文件放在测试模块旁边,使用 * .test.js 或 * .spec.js 命名约定,如 module_name.spec.js
  • 将其他测试文件放入一个单独的测试文件夹中以避免混淆。
  • 编写可测试代码,避免副作用,提取副作用,编写纯函数
  • 不要写太多的测试来检查类型,而是使用静态类型检查器
  • 在进行任何 pull 请求开发之前,请先在本地运行测试。
  • 记录你的测试,并附上说明。

6. 目录结构与命名

  • 围绕产品特性/页面/组件来组织您的文件,而不是围绕角色

坏的做法

坏的做法

  • 将测试文件放在他们的实现旁边。
  • 将其他测试文件放在单独的测试文件夹中以避免混淆。
  • 使用 ./config 文件夹。使用的环境变量由提供配置文件。
  • 将脚本放在 ./scripts 文件夹中。这包括用于数据库同步,构建和捆绑等的 bash 和 node 脚本。
  • 将构建输出放在 ./build 文件夹中。并将 build/ 添加到 .gitignore 中。
  • 使用 PascalCase’ ‘camelCase 作为文件名和目录名。仅对组件使用 PascalCase。
  • CheckBox / index.js应该具有CheckBox组件,这是也是可以作为 CheckBox.js 的。但是CheckCountBox / CheckBox.js 或 checkbox / CheckBox.js ,这些都是冗余的。
  • 理想情况下,目录名称应与 index.js 默认导出的名称相匹配。

7. 代码风格

  • 为新项目使用 stage-1 和更高版本的 JavaScript(现代)语法。对于旧项目,与现有语法保持一致,除非您打算使项目升级。
  • 在构建过程之前包括代码风格检查。
  • 使用 ESLint – 可插入 JavaScript linter 来强制执行代码风格检查。
  • 使用 Airbnb JavaScript 风格指南阅读更多。使用 JavaScript 风格指南是您的项目和您的团队必须做的事情。
  • 使用 ESLint 的 Flow Type 风格检查规则。当使用 FlowType
  • 使用 .eslintignore 从代码样式检查中排除文件或文件夹。
  • 删除所有 eslint 禁用注释,然后再执行 Pull 请求。
  • 始终使用 // TODO:注释来提醒自己和他人关于未完成的工作。
  • 始终注释并保持与代码更改相关。
  • 尽可能删除注释的代码块。
  • 避免生产中的 js 警报。
  • 避免不相关或有趣的注释,日志或命名(源代码可能会交给另一家公司/客户,他们可能不会分享同样的幽默)。
  • 编写可测试代码,避免副作用,写纯函数。
  • 函数命名尽可能容易搜索到且有意义的,避免缩短名。对于函数使用长的描述性名称。函数名称应该是一个动词或动词短语,需要传达其意图。
  • 按照 step-down 规则在文件中组织您的函数。较高级别的函数应该在较低级别的上面。这样读取源代码就更为自然了。

8. Logging 日志

  • 避免在产品中输出客户端控制台日志
  • 生成可读的产品级日志。推荐在产品模式下使用日志库,例如 winston 或 node-bunyan 。

9 API 设计

遵循面向资源的设计。这有三个主要因素:资源、数据集合和 URL 。

  • 资源拥有数据,与其他资源的关系以及对其进行操作的方法
  • 一组资源称为集合。
  • URL 标识资源的在线位置。

9.1.1 URLs 的命名

  • /users 用户的集合(复数名词)。
  • /users/id 具有特定用户信息的资源。
  • 资源在 URL 中始终应该是复数。 将动词从资源网址中删除。
  • 对于非资源使用动词。在这种情况下,你的 API不 会返回任何资源。而是执行一个操作并将结果返回给客户端。 因此,你应该在 URL 中使用动词而不是名词来清楚地区分与资源相关的响应中的非资源响应。

9.1.2 字段的命名

  • 请求体或响应类型是 JSON 时,请遵循 camelCase 来保持一致性。
  • 公开资源,而不是数据库模式详细信息。你不必使用 table_name 作为资源名称。与资源属性相同,它们不应与你的列名称相同。
  • 仅在 URL 命名中使用名词,不要尝试解释其功能,只解释资源(优雅)。

9.2 资源上的操作

9.2.1 使用 HTTP 方法

仅在资源 URL 中使用名词,避免像 /addNewUser 或 /updateUser 这样的终端。同时避免发送资源操作作为参数。而是使用 HTTP 方法来实现相关功能:

  • GET 用于检索资源的内容。
  • POST 用于创建新的资源和子资源
  • PUT 用于更新已经存在的资源
  • PATCH 用于更新已经存在的资源。PATCH 仅更新所提供的字段,而不改动其他的字段。
  • DELETE 用于创建已经存在的资源

9.3 使用子资源

子资源用于将一个资源与另一个资源链接,因此使用子资源来表示关系。 API 应该是为开发人员提供的接口,那么使得资源可查看是自然的需求。如果存在类似雇员与公司联系的资源之间的关系,请在 URL 中使用 id :

  • GET /schools/2/students 应该从学校2获得所有学生的列表。
  • GET /schools/2/students/31 应该获得在学校2中就读的学生31的详细信息
  • DELETE /schools/2/students/31 应该删除在学校2就读的学生31的信息
  • PUT /schools/2/students/31 应该更新学生31的信息,仅在资源URL上使用PUT,而不是整个数据集合。
  • POST /schools 应该创建一个新的学校,并返回所创建学校的全部信息。请在集合URL中使用POST

9.4 API 版本化

当你的 API 对其他第三方公开时,升级 API 会出现一些破坏性的变化,也会导致破坏那些使用你的 API 的现有产品或服务。在URL中的使用版本化可以防止这种情况发生: http://api.domain.com/v1/schools/3/students

9.5 发送反馈

9.5.1 错误

响应消息必须是自描述的。好的错误消息响应可能看起来是这样的:

或用于校验错误的:

注意:尽可能保持安全异常消息的通用性。例如,你可以回复说“无效的用户名或密码”,并在不知不觉中通知用户用户名确实是正确的,只有密码不正确,而不是回复“错误的密码”。

9.5.2 使用 HTTP 消息码对齐你的反馈

客户端和 API 工作正常(成功 – 2xx响应码)

  • 200 OK 当前 HTTP 响应表示成功执行 GET, PUT 或 POST 请求
  • 201 Created 该状态码必须在一个新实例创建时返回。例如:在创建一个新实例时,使用 POST 方法,应该总是返回 201 状态码。
  • 204 No Content 表示请求已经被处理,但是没有返回任何内容。DELETE 就是对此的一个好示例。如果发生任何错误,响应码可能就不在2xx成功范围而是4xx客户端错误范围内。

客户端应用程序表现异常(客户端错误 – 4xx 响应码)

  • 400 Bad Request 表示客户端的请求并没有被处理,因为服务器无法理解客户端的请求。
  • 401 Unauthorized 表示请求缺乏必要的证书无法访问需要的资源,客户端需要重新使用证书发送请求。
  • 403 Forbidden 表示请求是合法的,客户端已经获得授权,但是该客户端因某种原因被禁止访问该页面和资源。
  • 404 Not Found 表示未找到被请求的资源。
  • 406 Not Acceptable 和定义在 Accept-Charset 和 Accept-Language 头中的可接受值列表匹配的响应无法提供。
  • 410 Gone 表示所请求的资源已经无法访问,并且被特意、永久移除。

API 处理异常 (服务器错误—— 5xx响应码)

  • 500 Internal Server Error 表示请求是有效的,但服务器因为某种未知原因无法完成该请求。
  • 503 Service Unavailable 表示服务器挂掉或者无法接收和处理该请求。多数情况下如果服务器处理维护或者短时过载可能会遇到该情况。

9.6 资源参数和元数据

  • 在响应中提供资源的总数
  • 还应考虑公开资源的数据量。API 使用者并不总是需要资源的完整表示。使用字段查询参数,该参数采用逗号分隔的字段列表来,包括:
  • 分页,过滤和排序不需要默认为所有资源提供支持。文档化那些提供了过滤和排序的资源。

9.7 API 安全性

9.7.1 TLS

为了保护你 Web API 身份验证,所有身份验证都应使用 SSL 。OAuth2 需要授权服务器和访问令牌的证书才能使用 TLS 。在 HTTP 和 HTTPS 之间切换会引入安全漏洞,最佳实践是默认使用 TLS 进行所有通信。 抛出非安全访问 API 网址的错误。

9.7.2 速率限制

如果你的 API 是公开的或拥有大量的用户,任何客户端都可以每小时调用你的 API 数千次。你应该尽早考虑实施限速。

9.7.3 输入校验

如果允许的值有限,很难实施大多数攻击。

  • 验证必填字段、字段类型(例如字符串,整数,布尔类型等)和格式要求。 返回 400 错误请求,其中包含有关错误或丢失数据错误的详细信息。
  • 转移那些可能成为 SQL 语句一部分的参数,以防止 SQL 注入攻击
  • 如前所述,在命名资源和定义响应时,不要暴露你的数据库方案

9.7.4 URL 校验

攻击者可以篡改 HTTP 请求的任何部分,包括 URL、查询字符串。

9.7.5 校验收到的 content-types

服务器不应该假定 Content-Type 。 缺少 Content-Type 头或意外的 Content-Type 头应导致服务器拒绝该内容,并返回 406 Not Acceptable 响应。

9.7.6 JSON 编码

JSON 编码器的一个关键问题是阻止在浏览器或服务器上的 node.js 中执行任意的 JavaScript 远程代码。在输入数据上使用 JSON 序列化器,以防止在浏览器/服务器上执行用户输入。

9.8 API 文档

  • 参考 README.md template 一节为 API 填写参考部分。
  • 用代码示例描述 API 验证方法
  • 解释 URL 结构(仅路径,无根 URL ),包括请求类型(方法)

对每个端点说明:

  • URL Params:如果 URL 参数存在,请根据 URL 部分中提到的名称来指定它们
  • 如果请求类型为 POST ,请提供一个可工作的示例代码。URL Params 规则也适用于此。将该部分分为“可选的”和“必需的”。
  • 成功的响应,状态码应该是什么,是否有返回数据?当人们需要知道他们的回调应该等待什么时,这是有用的!
  • 错误响应,大多数端点有许多方法可以导致失败。从未经授权的访问,到不正确的参数等。所有这些应该列在这里。 它看起来似乎是重复的,但它有助于防止默认假定。 例如

9.8.1 API 设计工具

有许多开源工具可用于好的文档设计,例如 API Blueprint 和 Swagger 。

10. 许可证(协议)

确保你有权使用该资源。如果你使用库,请记住使用 MIT、Apache 或 BSD ,但如果你修改了它们,请查看许可证中的详细信息。 有版权的图像和视频可能会导致法律问题。

1 收藏 评论

可能感兴趣的话题



直接登录
跳到底部
返回顶部