前端开发中的流程自动化与提效实践

标签: Document 自动化 提效 前端 CHANGELOG | 发表时间:2022-07-07 08:00 | 作者:Joe Jiang
出处:https://hijiangtao.github.io/

随着前端的发展,越来越多的工具库、方法被用在日常研发流程中,这大大提升了业务开发的效率,而随着各类自动化流程的建设,开发同学也不再需要关注到每一个细节。前段时间项目阶段性交付,在推进的过程中也做了不少尝试,虽然从长期看,这类工作最后可能都该收敛到基础设施部门或者标准的自动化流程中去,但并不妨碍我通过实践来落实一些对项目开发的思考和想法。

如果你是一名有经验的开发者,可以直接跳到文章末尾,「总结」一章有对全文内容的精简描述。

接下来,我来分享下在项目开发中尝试的一些自动化和提效实践。本文在撰写中涉及的 UI 框架主要以 create-react-app 所产生的 React 项目为主,会辅以部分其他框架的解决方案以说明。

新建项目第一步:脚手架

如果你的项目选型是 Angular 的话,那么选择不多可以直接上 Angular CLI;如果是 React 或 Vue 的话,那么会有不少脚手架可以选择,国内很多开发者都有开源不同方案。其中,大多方案会有一些组件库、开源库的绑定,如果你希望一个更加自由的框架搭建,官方脚手架 create-create-app (CRA) 肯定会是第一选择。

通过 npx 执行如下命令,我们可以通过 CRA 快速创建一个 TypeScript 项目:

    npx create-react-app project --template typescript

但随着 CRA 的发展,官方脚手架也将原来暴露在模版中的越来越多细节封装到 react-scripts 包里,简单举个例子,比如你想修改项目 webpack 构建流程,用官方模版无法直接上手。

官方模版提供了 npm run eject 命令,执行这个命令会将潜藏的一系列配置文件和一些依赖项都“弹出”到项目中,然后就可以由你自己完全控制增删,但是该操作是不可逆的。在配置文件被“弹出”后,你后续将无法跟随官方的脚步去升级项目所使用的 react-script 版本了。

那么,如果你想要一个可以覆盖配置,但又与官方版本保持同步的方案,则可以试试 craco https://github.com/dilanx/craco

样式隔离方案:CSS Modules

这一部分接着上文继续。这可能是一个已经被大家熟知且默认开启的选项了,其起源也在于对组件样式的隔离需求,且如果你的项目是一个CRA 官方其实已经支持对 CSS Modules 的启用,按照规定的格式命名你的样式文件,CRA 便可自动对样式进行解析处理。

但如前文所述,如果你想做一些样式 loader 的修改,就比如默认的解析方式不支持样式变量的驼峰式命名。要达到目的,你可以在集成 craco 后在配置 craco.config.js 中这么写,扩充 css loader 配置选项:

    module.exports = {
  style: {
    css: {
      mode: "extends",
      loaderOptions: {
        modules: {
          auto: true,
          exportLocalsConvention: 'camelCaseOnly',
        },
      },
    },
  },
};

当然,当我们在 TypeScript 文件中引用 CSS Modules 变量时,由于 TypeScript 并不知道除了 .ts 以及 .tsx 文件外的文件内容,为了防止 IDE 在语法检查上报错,我们还需要针对特定文件后缀声明下环境变量。针对 CRA 新建的项目,你可以简单建立一个 react-app-env.d.ts 文件来补充上如下说明:

    /// <reference types="node" />
/// <reference types="react" />
/// <reference types="react-dom" />
/// <reference types="react-scripts" />

declare module '*.module.css' {
  const classes: { readonly [key: string]: string };
  export default classes;
}

declare module '*.module.scss' {
  const classes: { readonly [key: string]: string };
  export default classes;
}

declare module '*.module.sass' {
  const classes: { readonly [key: string]: string };
  export default classes;
}

此外,通过 craco 你还可以修改些其他内容,比如配置 babel 不对 ES6 模块做转译、修改打包路径、自定义热更新方案等等。

ESLINT 代码检查:两个自定义 lint 场景分享

为了保证统一一致的代码风格,比如在项目中避免使用  var,我们可以引入 eslint rules 对提交的代码进行规范。当然,如今大多数脚手架在新建项目时,应该都替你集成好了 eslint,要么是 .eslintrc.json、.eslintrc.js 或者直接在 package.json 中添加 eslintConfig 属性开干。

除了开启默认的规则集外,在开发过程中,为了让项目在多人之间能够更高效地推进协作开发,肯定有不少细节需要处理,这里我简单分享两例。

第一点,当我们对代码做了一些增删操作,就可能产生冗余的 import 声明,这个时候我们当然希望它在提交时被删除。

在 eslint 中,插件可以暴露额外的规则以供使用。如果要达到我们刚刚说的目的,这个时候可以引入 unused-imports 插件对 es6 imports 进行代码检查(比如对于未使用到的 import 声明进行 error 提示):

    {
	"plugins": ["unused-imports"],
  "rules": {
    "no-unused-vars": "off",
    "unused-imports/no-unused-imports": "error",
    "unused-imports/no-unused-vars": [
      "warn",
      { "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" }
    ]
  }
}

借助 IDE 或者 IDE 插件,我们还可以前置到每次保存时执行对 import 引用的清理。

多人协作中还容易出现一类问题,那就是代码冲突。当多人同时修改一个文件时,可能会同时引入新的模块,如果不对代码进行统一风格管理,那么很容易出现“import 冲突”。为了解决这个问题,可以引入 simple-import-sort 插件,插件还支持对 export 进行排序处理:

    {
  "plugins": ["simple-import-sort"],
  "rules": {
    "simple-import-sort/imports": "error",
    "simple-import-sort/exports": "error"
  }
}

这样,在代码提交前执行一遍 eslint —fix 便可完成代码的检查与修复,关于这部分我们在后面 git hook 一章中详细介绍。

其他还有一些与框架相关的,比如从 React 17 开始,我们可以通过配置 eslint rules 达到无需在代码中引入 React 的目的,关于这一部分的实现可以参考 React 官方指导,以下为一段简单的前后代码对比:

    // 以前
import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}

// 现在
function App() {
  return <h1>Hello World</h1>;
}

自动化环境配置:git hook 钩子定义

husky 是一个为 git 客户端增加 hook 的工具,可以被用于我们配置本地自动化环境。我们可以安装 husky 并定制我们需要的 git 钩子及具体需要执行的任务,这样可以方便我们在对代码执行 git 操作时,在特定时机对代码进行特定的检查与处理,常见的钩子有如下两个:

  • commit-msg - 提交信息钩子,在执行 git commit 或者 git merge 时触发
  • pre-commit - 预先提交钩子,在执行 git commit 时触发

如下为一段 husky 安装和初始化代码:

    npm install husky -D
npx husky-init && npm install 

// 添加任务一条 commit-msg 钩子
npx husky add .husky/commit-msg './node_modules/.bin/commitlint --from=HEAD~1'

举个例子,比如我们可以结合 commitlint 工具,在 commit-msg 阶段针对 commit 信息进行检查和处理(如上代码所示),又或者在 pre-commit 阶段对将要提交的代码进行格式化操作。

自定义命令调用:代码风格统一与 commit 信息规范

不仅是对于开发代码,对于 commit 信息来说,五花八门的书写风格,也十分不利于阅读和维护,比如,当我们需要翻阅历史提交来定位具体 commit 所带来的代码变动时,这依赖于规范的 commit 信息。借助 commitlint 可以帮助我们达到这一点。

commitlint 需要配合 husky 一起使用,具体来说,通过 husky 来保证针对具体钩子的命令配置,通过 commitlint 保证 命令执行能够对 commit msg 信息进行特定检查。一个简单的 commitlint 安装和配置如下:

    # 安装
npm install --save-dev @commitlint/{config-conventional,cli}

# 配置
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

配置了 commitlint 之后,若是使用默认配置,那么只有当我们的 commit msg 符合如下规范时,commit 操作才能正常完成执行,否则中断(其中 scope 为可选):

    <type>(<scope>): description

如果需要自定义规则,则需要我们更改 commitlint.config.js 文件,为其增添 rules,关于这一部分可以参考官方文档 https://commitlint.js.org/

此外,在添加了 commitlint 配置文件后,我们可能会看到 IDE 对这个文件的检查标红,由于在项目构建中我们并不关注该配置文件的格式等内容(因为他只是对 commitlint 的简单配置),所以我们可以在 eslint 配置文件中将其忽略掉。同样的,如果有其他的文件属于类似的定位,你也可以一并将其加入:

    {
  "ignorePatterns": ["commitlint.config.js"]
}

说完 commit 规范,我们的代码格式本身也需要规范,比如针对 TypeScript 代码会有变量命名和排序的规则,针对 html 模版会有缩进、标签闭合等规则,这些也可以利用工具结合 git hook 来实现,此处,我们介绍下 linter 工具:lint-staged。

lint-staged 是一个在 git 暂存文件上运行 linters 的工具,通过 lint-staged 定制各类文件格式化的操作(主要通过 eslint 和 prettier 保证执行)。结合 pre-commit git hook,我们在此钩子被触发时执行 lint-staged,便能完成对相应文件的格式化处理。如何让工具针对不同类型的文件区分处理呢?这里可以通过配置达到目的,即在 pakage.json 中对 lint-staged 字段进行声明:

    {
  ...,
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint -c ./.eslintrc.json --fix"
    ],
    "(*.json|.eslintrc|.prettierrc)": [
      "jsonlint --in-place"
    ],
    "*.{s,}css": [
      "prettier --write"
    ],
    "*.{html,md}": [
      "prettier --write"
    ]
  }
}

如上代码表明,lint-staged 可以针对 JavaScript/TypeScript 类文件执行 eslint 处理,针对 json 类文件执行 jsonlint 处理,而对样式文件和 html 文件都用 prettier 处理。

本地开发代理环境

从本地开发的场景来看,最需要完善的一个功能就是转发 API 请求了,用以规避可能存在的 CORS 错误,或者绕开一些请求限制,形式上可以是针对特定请求变更 header 信息,也可以是针对特定请求变更实际请求的域名与路径。但无外乎都需要在本地建立一个代理环境。前端项目在本地开发时,我们可以通过一个叫做 http-proxy-middleware 的库来增强本地 dev server 的能力。

通过文档介绍,我们知道可以通过调用 createProxyMiddleware API 来构造一个中间件,以供 Node.js 项目使用,针对 express 应用可以通过如下配置完成代理服务器中间件的配置:

    import * as express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';

const app = express();

app.use(
  '/api',
  createProxyMiddleware({
    target: 'http://www.example.org/api',
    changeOrigin: true,
  })
);

app.listen(3000);

而对于通过 CRA 构建的项目,由于 CRA 已经做了一些封装工作,于是在本地开发时,我们不再需要显式地起一个 Node.js 应用,而如上对代理中间件地调用,也可以在指定文件中按照规范书写(文件名以及方法签名需要规范,此处不列举,CRA 官方文档有说明)。

代码复用:组件模版与 code snippets

当我们新建一个项目时,我们会找脚手架替我们搭建一个合适的架子,然后我们往里面填充组件、页面、服务等等,而针对更细粒度的代码,我们同样可以考虑通过代码复用来提升我们的开发效率,这里的场景主要可以分为两类:

  1. Code snippets 类代码片段
  2. 组件粒度的文件代码

针对第一类场景,我们在 IDE 中安装的不少插件都替我们达到了这个目的。当我们在特定类型的文件中敲出几个字母,然后一回车,就能快速生成一段代码片段。举个例子,比如当我们使用 RxJS 时经常需要定义 BehaviorSubject 及其 getter & setter,要是输入 bgs 就能出现如下代码,便能提高我们在写一些初始化代码时的效率:

    /* TODO: 数据流定义 */
behaviorName$ = new BehaviorSubject<string>(initialValue);

get behaviorName() {
     return this.behaviorName$;
}

set behaviorName(value: string) {
     this.behaviorName$.next(value);
}

而我们要做的事情就是将 bgs 和上述代码(及特定占位符)连接起来,实现方案可以是 VS Code 插件,比如我之前写过一个 https://marketplace.visualstudio.com/items?itemName=hijiangtao.tutor-code-snippets,也可以是 WebStorm 插件或者 live templates。

针对第二类场景,我们需要的往往是指定路径下一套遵循我们命名规范的模版、样式和 TypeScript 逻辑代码。举个例子,在 Angular 项目中,当我们新建一个组件时,我们需要同时生成 HTML、JavaScript、CSS 文件并更新离其最近的 *.module.ts 文件:

    CREATE projects/src/app/demo/demo.component.css (0 bytes)
CREATE projects/src/app/demo/demo.component.html (19 bytes)
CREATE projects/src/app/demo/demo.component.spec.ts (612 bytes)
CREATE projects/src/app/demo/demo.component.ts (267 bytes)
UPDATE projects/src/app/app.module.ts (1723 bytes)

在 Angular 项目中,我们可以通过 Angular schematics 来很方便的实现这一目的;而针对 React 或者 Vue 项目,社区也有不少实现方案,比如 generate-react-cli

如果不借助社区或者官方方案,要实现第二类场景所需的工具,也可以自己开发一个 npm 包并暴露相应 bin 脚本,然后通过 npx 执行来达到目的,关于 npx 的介绍可以参考我之前写过的一篇博文 《记录一下 npx 的使用场景》

版本发布:发版与 CHANGELOG 自动化

每当我们需要上线一个新需求时,为了更好的记录变动,我们一般需要发个版本,并同时记录一些 CHANGELOG,如果能把这部分工作完全自动化,毋庸置疑可以提升我们的项目规范和发版效率。这里以 standard-version 举例。我们引入 standard-version 对自动打 tag 以及 CHANGELOG 生成进行规范,具体来说,是期望通过 standard-version 达到如下目的:

  • 根据指定规则自动升级项目不同级别(major、minor、patch)的版本并打 tag
  • 对比历史 commit 提交自动生成不同版本间的可阅读、分类的 CHANGELOG 日志

通过配置,我们还可以重命名上文提到的 commit 信息中的 type 字段在 CHANGELOG 中的标题展示,如下为一个示例配置:

    module.exports = {
    "types": [
        { "type": "feat", "section": "Features" },
        { "type": "fix", "section": "Bug Fixes" },
        { "type": "test", "section": "Tests" },
        { "type": "doc", "section": "Document" },
        { "type": "build", "section": "Build System" },
        { "type": "ci", "hidden": true }
    ]
}

如下为自动生成的 CHANGELOG 示例:

    # Changelog

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [1.11.0](https://git.woa.com/test/project/compare/v1.10.1...v1.11.0) (2022-06-28)

### Features

- 测试 feature 提交 @hijiangtao (merge request !65) ([a091125](https://git.woa.com/test/project/commit/xxx))

### [1.10.1](https://git.woa.com/test/project/compare/v1.10.0...v1.10.1) (2022-06-24)

### Bug Fixes

- 修改 XXX,并新增 YYY (merge request !63) ([e3a01ce](https://git.woa.com/test/project/commit/yyy))

在使用 standard-version 的时候,默认的配置可以满足绝大部分场景了,但更细粒度的控制仍需要我们修改命令或者配置。比如,当我们在 package.json 中指定了仓库 url 后,我们便可以在 commit 信息中通过 @ 符号指定对应用户、通过 # 引用对应 issue/PR,这些在生成 CHANGELOG 时都会被转成对应包含链接地址的超链接,如下列上一些我在开发中的版本自动生成所涉及的应用场景及注意事项:

    // 首次执行(不变更版本)
standard-version -a -- --first-release 

// 但是如果项目版本不符合规范,还是需要手动发布,因为需要保证项目从 v1.0.0 开始
// https://github.com/conventional-changelog/standard-version/issues/131
standard-version -a -- --release-as 1.0.0

// 在 package.json 中确保项目正确配置 repository 对象
repository.url

// 带通知具体 user 的 commit-msg 
git commit -m "fix: bug produced by @timojiang" 

// 带 issue 的 commit-msg
git commit -m "fix: implement functionality discussed in issue #2"

总结

本文从项目初始化选用脚手架开始、样式隔离、lint 规则与 git hook 再到模版工具和自动化版本日志,介绍了本人在开发过程中的一些实践和思考,旨在指出一个项目从代码初始化到交付上线过程中可能涉及到的不同流程中,存在的不同提交效率和自动化流程的工作,囿于文章篇幅,没有针对所有涉及到的流程和工具进行详细的 API 介绍,但你仍可以针对其中提到的各类关键词在互联网上进行搜索和查看。

本文在撰写时也尽量针对不同流程换用了普适的一些描述,以保证所提供的方案在替换了具体工具库后仍是长期可用的,防止文章内容受工具的时效性影响。

全文来看,主要有这么几点实践总结:

  1. 在脚手架的选择上,你可以使用 create-react-app 或者 umi 等社区方案,但如果想要更灵活的脚手架,当你使用 CRA 的时候,可以一并考虑 craco;
  2. CSS Modules 的作用无需多说,但同样需要注意 TypeScript 检查以及你在命名 CSS 变量写法上的兼容;
  3. ESLINT 现在已经是大部分项目的标配了,如果你的项目涉及多人协作,可以配置一些额外的 plugin 协助保持风格一致、减少代码合并冲突。当然,在统一代码风格上,还可以通过 husky 定制 git 钩子与具体需要执行的任务,比如:
    1. commit-msg
    2. pre-commit
  4. 其中通过 lint-staged 在 pre-commit 时机定制各类文件格式化的操作(主要用 eslint 和 prettier 保证执行),通过 commitlint 保证 commit msg 信息符合规范。
  5. 本地开发代理环境在很多团队是必须的,你可以选用一个中间件来增强你的 dev server。
  6. 我们同样可以考虑通过代码复用来提升我们的开发效率,这里的场景主要可以分为两类:code snippets 以及组件级别的文件修改。
  7. 每当项目上线,规范来说,都需要发版及记录日志变更,我们可以引入 standard-version 对自动打 tag 以及 CHANGELOG 生成进行规范。

相关 [前端 开发 自动化] 推荐:

前端开发流程自动化

- Lee - 《程序员》杂志官网
如今前端工程师需要维护的代码变得极为庞大和复杂,代码维护、打包、发布等流程上浪费的时间精力也越来越多. 精简流程、提高效率,是每一个前端团队都会遇到的问题. 大部分前端团队使用Ant脚本进行这一系列流程的自动操作. Ant主要用于代码构建、打包、部署的自动化操作. 早先主要用于Java开发,但由于它具有接口开放、便于配置、Java跨平台等特性,在前端流程自动化方面同样可以发挥强大的功用.

前端自动化开发工作流模板

- - 氪星人
每种项目都有自己特定的开发流程、工作流程. 从需求分析、设计、编码、测试、发布,一个整个开发流程中,会根据不同的情况形成自己独特的步骤和流程. 一个工作流的过程不是一开始就固定的,而是随着项目的深入而不断地改进,期间甚至会形成一些工具. 例如当年大神们在Linux写C语言,觉得每次编译好多文件好麻烦,就发明了makefile.

前端开发中的流程自动化与提效实践

- - Joe’s Blog
随着前端的发展,越来越多的工具库、方法被用在日常研发流程中,这大大提升了业务开发的效率,而随着各类自动化流程的建设,开发同学也不再需要关注到每一个细节. 前段时间项目阶段性交付,在推进的过程中也做了不少尝试,虽然从长期看,这类工作最后可能都该收敛到基础设施部门或者标准的自动化流程中去,但并不妨碍我通过实践来落实一些对项目开发的思考和想法.

前端自动化测试探索

- - FEX 百度 Web 前端研发部
测试是完善的研发体系中不可或缺的一环. 前端同样需要测试,你的css改动可能导致页面错位、js改动可能导致功能不正常. 由于前端偏向GUI软件的特殊性,尽管测试领域工具层出不穷,在前端的自动化测试上面却实施并不广泛,很多人依旧以手工测试为主. 本文试图探讨前端自动化测试领域的工具和实践. 一个项目最终会经过快速迭代走向以维护为主的状态,在合理的时机以合理的方式引入自动化测试能有效减少人工维护成本.

Puppeteer前端自动化测试实践

- - SegmentFault 最新的文章
本篇内容将记录并介绍使用Puppeteer进行自动化网页测试,并依靠约定来避免反复修改测试用例的方案. 主要解决页面众多时,修改代码导致的牵连错误无法被发现的运行时问题. 目前我们在持续开发着一个几十个页面,十万+行代码的项目,随着产品的更迭,总会出现这样的问题. 在对某些业务逻辑或者功能进行添加或者修改的时候(尤其是通用逻辑),这些通用的逻辑或者组件往往会牵扯到一些其他地方的问题.

前端优化之图片优化自动化

- jinn - 搜索研发部官方博客
随着前端页面越来越复杂,尤其是一些社区型的页面中,图片成了页面中不可或缺的资源,并且随着产品功能的叠加图片大小越来越多. 以下是几个网站的图片所占的比重. 由于图片是二进制文件,并不能像js、css、html那些源代码文件一样可以通过gzip压缩大大减小文件的大小. 所以图片优化主要是选择合适的图片格式,在不降低图片质量的情况下去掉图片里的元数据信息.

GitLab-CI:从零开始的前端自动化部署

- - DockOne.io
gitlab-ci&&自动化部署工具的运行机制. 1、通过在项目根目录下配置.gitlab-ci.yml文件,可以控制CI流程的不同阶段,例如install/检查/编译/部署服务器. GitLab平台会扫描.gitlab-ci.yml文件,并据此处理CI流程. 2、CI流程在每次团队成员push/merge后之后触发.

前端工程师用Node.js + Appium实现APP自动化

- - 掘金 前端
最近在使用Appium做爬虫功能,网上全是python + Appium的教程,Appium也支持Node.js开发,作为前端工程师使用 Node.js + Appium实现了自动化爬虫功能. 零零散散折腾了小两天的时间,踩了一些小坑,把流程和坑点整理出来供大家参考,希望让大家少踩坑、快速实现功能.

关于前端测试bug分类和自动化技术选型的想法

- - Taobao QA Team
下面按照现象和成因分解了前端相关bug,方便后面进行定位. 一部分和前端后端都相关的问题也列举了进来. 这些bug都是实际遇到过的,前端问题五花八门,期待大家共同维护这个mm图. 2常用前端测试相关工具. Fiddler:无差别跟踪每一个请求,可以重定向方便调试;. IE调试器(companion.js等):安装时需要先安装 Microsoft Script Debugger 然后再安装 CompanionJS安装后需要将IE设置中的禁用脚本调试(Internet Explorer)禁用脚本调试(其他)这两个选项去掉.

前端开发大众手册

- Ran - FeedzShare
来自: xilo's blog - FeedzShare  . 发布时间:2009年03月09日,  已有 3 人推荐. 一直觉得前端开发缺个手册,这是个体力活. 今天闲来无事,把一些工具(online和client的)、常用网址、以及经验总结等罗列出来和大家分享下. 这个标题起地大了点,肯定会有很多地方没列到,包括类目的分法也可能考虑不周,所以还请大家积极补充指正,可以直接留言,也可以发邮件给我.