技术杂谈:使用 Babel、Flow、ESLint 改善网页或 JavaScript 程序项目

PUBLISHED ON OCT 28, 2018

    JavaScript 是网页前端必备的技术,也可以在网页后端或其他领域使用,有着丰富的生态系;然而,JavaScript 语言本身的缺陷也一直为人诟病 (参考这里)。以继续留在 JavaScript 生态圈的前提下改善这些缺点,使用 JavaScript 转译器就成了一个常见的选项 (参考这里)。本文介绍以 Babel 为中心的工具组合,用来改善 JavaScript 的 code base。

    为什么使用 Babel、Flow、ESLint

    我们来看一下这三个项目的核心功能如下:

    • Babel: 可以用 ES6 (ECMAScript 2015) 以后的新语法写 JavaScript 程序代码,并将其转为等效的 ES5 (EMCAScript 2009) 程序代码
    • Flow: 在 JavaScript 程序代码中进行类型检查,可搭配 Babel 或单独使用
    • ESLint: 可对 ES6 (ECMAScript 2015) 后的 JavaScript 程序代码进行静态程序代码检查

    简而言之,这个工具组合用新的 JavaScript 语法特性改善现有的 JavaScript 程序代码;透过这个工具组合,我们可以用更好的 JavaScript 语法来写应用程序,而将原有的 ES5 视为一种执行环境 (runtime environment)。现在主流的浏览器都已经实现 ES5 的特性,并且逐渐实现 ES6 以后的功能,不用担心兼容性的问题。

    笔者先前也尝试过 CoffeeScript 或 Dart 等 JavaScript 转译器,但后来偏好 Babel。这是因为 CoffeeScript 等大部分的 JavaScript 转译器的语法和 JavaScript 本身不兼容,可以用来撰写新的项目,但无法直接套在原有的项目上;相较来说,只要透过几行指令,就可以把 Babel 套用在现有的项目上,可以在不破坏现有的 code base 的前提下渐进式引入新的语法;此外,如果要用 Babel 写新的项目也是可以的。

    为什么不使用 TypeScript

    TypeScript 是另一个知名的 JavaScript 转译器,语法上和 JavaScript 兼容,流行度更胜 Babel。然而,我们不用 TypeScript 而用 Babel,这比较属于个人偏好而没有绝对的对错,我们也可以用和本文相同的思维,把项目中有关 JavaScript 的 code base 逐渐转为 TypeScript。那么,要如何选择呢?TypeScript 支援更多的语法特性而 Babel 更贴近原生的 JavaScript,所以,依照自己喜好的风格来选择即可。

    建立 NPM 设定档

    一开始要先用 npm (Node.js 套件管理工具) 初始化一些设定档,程序会询问我们一些问题:

    $ npm init
    (省略一些讯息 )
    
    Press ^C at any time to quit.
    package name: (myapp)
    version: (1.0.0)
    description: My demo app.
    entry point: (index.js)
    test command:
    git repository:
    keywords: myapp
    author: Michael Chen
    license: (ISC)
    

    不用太纠结于如何回答这些问题,这些填入的内容事后都可以在项目设定档中修改。

    npm init 原本是要用来建置 Node.js 套件或 Node.js 应用程序项目的指令,但不代表我们一定要用 Node.js 写网页程序。基本上 npm init 指令只是在项目中多加入几个驱动 Node.js 相关的设定档,包括 package.jsonpackage-lock.json 等,但和项目本身使用的语言无关。

    npm init 初始化项目后,我们就可以在项目中加入一些 Node.js 套件。本文许多指令也是在项目中加入额外的 Node.js 套件,用这些套件来改善项目中 JavaScript 部分的程序代码。

    加入 Babel 支援

    Babel 是支援 ES6+ 的 JavaScript 转译器。透过以下指令在项目中加入 Babel 套件:

    $ npm install --save-dev babel-cli
    

    设定 *package.json*,用 Babel 转换 JavaScript 程序代码:

    "scripts": {
        "build": "babel src -d lib",
        ...
    },
    

    在这个设定下,我们输入 npm run build 时,NPM 会将 src 中的 ES6+ 程序代码转成等效的 ES5 程序代码,并放在 lib 中;文件名称不会更动,例如, src/app.js 会转为 lib/app.js 。实际在使用 Babel 时,我们会将现有的 JavaScript 程序代码从 lib 搬移到 src ,之后编辑位于 src 的程序代码,将 lib 内的程序代码视为自动产生的静态 assets;由于文件名称不会更动,我们不需要更动项目其他的部分。

    Babel 默认情形下会将程序代码原封不动地拷贝到目标位置,实际转换程序代码的功能会透过外挂 (plugins) 来达成。babel-preset-env 是一个 Babel 的外挂的套组 (set),会自动引入最新版的 JavaScript 语法。输入以下指令来安装该套件:

    $ npm install --save-dev babel-preset-env
    

    另外要设置 .babelrc 以启用该 preset:

    {
        "presets": ["env"]
    }
    

    实际上线的 JavaScript 程序会经过压缩 (minification) 的动作,主要用意是缩小程序代码所占的空间,传输上更快一些;另外可以让程序代码相对更难追踪。透过 babel-minify 可达成这项功能;透过以下指令来安装该套件:

    $ npm install babel-minify --save-dev
    

    由于程序代码经压缩后会变得较难追踪,在开发阶段不会急着把程序压缩,最后程序要上线前再压缩即可。可参考以下内容来设置 .babelrc

    {
        "presets": ["env"],
        "env": {
            "production": {
              "presets": ["minify"]
            }
        }
    }
    

    在类 Unix 系统系统上输入以下指令即可转换并压缩 JavaScript 程序代码:

    $ BABEL_ENV=production npm run build
    

    在 Windows 上则要略为修改指令如下:

    C:\path\to\project>SET BABEL_ENV=production&&npm run build
    

    (选择性) 加入 Flow 支援

    Flow 的用意是在 JavaScript 中加入类型检查,由于这个功能是选择性的,读者可自行决定要不要在项目中加入 flow。Babel 默认不会辨识 flow 相关的程序代码,要透过额外的 preset:

    $ npm install --save-dev babel-cli babel-preset-flow
    

    同时要设置 .babelrc 以启用 flow 相关的 preset:

    {
        "presets": ["env", "flow"]
    }
    

    上述套件会解析 flow 相关的代码,在产出的 assets 中将其抹除,因为实际上线的 JavaScript 环境无法辨识 flow 的类型注解相关语法。TypeScript 程序在上线时也会将类型注解的部分抹除,道理是相同的。

    上述的 preset 不会检查类型,要另外要安装 flow-bin 才有实际检查类型的功能:

    $ npm install --save-dev flow-bin
    

    同时要设置 package.json

    "scripts": {
        "flow": "flow",
        ...
    },
    

    第一次使用 flow 时要在项目中初始化:

    $ npm run flow init
    

    之后就可以直接用 flow 检查代码:

    $ npm run flow
    

    本文篇幅较短,故未介绍 flow 的语法,请各位读者自行参考这里的说明。

    (选择性) 使用 ESLint 检查 JavaScript 语法

    ESLint 是一个 JavaScript 的静态程序代码检查软件 (linter),对于补强 JavaScript 的工程性有相当帮助。ESLint 的好处是可辨识 ES6+ 代码;此外,ESLint 不会有太强烈的风格上的建议,可用在各类型的项目上。

    透过以下指令安装 ESLint:

    $ npm install --save-dev eslint babel-eslint
    

    第一次使用 ESLint 时要先初始化,按照自己的使用情境来回答即可:

    $ node_modules\.bin\eslint --init
    ? How would you like to configure ESLint? Answer questions about your style
    ? Which version of ECMAScript do you use? ES2015
    ? Are you using ES6 modules? No
    ? Where will your code run? Browser
    ? Do you use CommonJS? No
    ? Do you use JSX? No
    ? What style of indentation do you use? Spaces
    ? What quotes do you use for strings? Double
    ? What line endings do you use? Windows
    ? Do you require semicolons? Yes
    ? What format do you want your config file to be in? JavaScript
    

    由于我们采局部 (local) 安装,故我们从 node_modules 中调用 ESLint 的终端机指令。

    package.json 中启用 ESLint:

    "scripts": {
        "lint": "eslint src/*.js",
        ...
    },
    

    ESLint 的用法较多,或许有机会日后会在其他文章介绍。

    Git 或其他版本控制的使用者

    记得要在项目中加入相关的 .gitignore 或其他同性质文件,以免引不不必要的文件。