React-Native (JavaScript)代码规范及检测工具概述

在RN工程中,使用ESLint进行代码规范扫描。

Why-- 为什么需要代码规范

JavaScript的应用场景由最初的用在Web页面中实现一些简单交互,发展到处理复杂的网站功能交互等,甚至通过NodeJS来跑在服务端。应用的场景越来越多,越来越复杂,需要应对不同的运行的环境等,对代码的稳定性和兼容性要求越来越高。

加上JavaScript语言本身存在一些设计缺陷,例如既是优点又是缺陷的弱类型特性、例如var变量的作用域问题等等,这些特性会带来非常多的安全隐患。代码不严谨可能就会触发一些神奇的错误甚至一些低级错误,这些错误往往在编译期间很难发现,通常只有在运行期间才会暴露出来,这样很容易把问题带到线上,导致面临巨大的风险。以上问题,需要通过一套代码规范机制来约束代码风格、尽量避免低级错误的产生。

同时在企业的项目开发中,制定一套完善有效的编码规范也是极为必要的。好的编码习惯,规范的代码风格对于降低团队成员开发时的沟通成本、提升codeReview效率、提高代码质量、提升系统稳定性等都是有巨大好处的。

在前端开发中有一些辅助工具来解决这个问题,例如JSLint、JSHint、ESLint等,其中现在比较流行且对JS新特性兼容比较好的是ESLint。这套机制同样适用于React Native的开发.

What-- 规则、自动检测工具(ESLint)

规则

工程目录结构

以下目录结构示例中只展示js与静态资源,不包含原生代码。

├── index.ios.js
├── index.android.js
└── js
    ├── component           可复用的组件(非完整页面)
    ├── page                完整页面
    ├── config              配置项(常量、接口地址、路由、多语言化等预置数据)
    ├── util                工具类(非UI组件)
    ├── style               全局样式
    └── image               图片

在component和page目录中,可能还有一些内聚度较高的模块再建目录

page/component
├── HomeView.component.js
├── HomeView.style.js
└── MovieView
    ├── MovieList.component.js          
    ├── MovieList.style.js          
    ├── MovieCell.component.js          
    ├── MovieCell.style.js              
    ├── MovieView.component.js          
    └── MovieView.style.js              

代码风格、规范

JavaScript基础语法部分的代码规范

ESLint内置的一套代码规范(eslint:recommended):https://cn.eslint.org/docs/rules/

前端比较流行的一套代码规范:Airbnb JavaScript Style Guide

React/JSX 相关的代码规范

Airbnb React/JSX 风格指南:https://github.com/lin-123/javascript/tree/cn/react

自动检测工具

JSLint、JSHint、ESLint对比

JSLint

2002 年,Douglas Crockford 开发了可能是第一款针对 JavaScript 的语法检测工具 —— JSLint,并于 2010 年开源。

JSLint 面市后,确实帮助许多 JavaScript 开发者节省了不少排查代码错误的时间。
但是 JSLint 的问题也很明显:

  • 几乎不可配置,所有的代码风格和规则都是内置好的;
  • 再加上 JSLint 作者冷处理方式,不会向开发者妥协开放配置或者修改他觉得是对的规则。
JSHint

于是 Anton Kovalyov 吐槽:「JSLint 是让你的代码风格更像 Douglas Crockford 的而已」,并且在 2011 年 Fork 原项目开发了 JSHint。

JSHint 的特点就是:

  • 可配置,
  • 同时文档也相对完善,
  • 而且对开发者友好。

很快大家就从 JSLint 转向了 JSHint。

ESLint
ESLint 的诞生

起初几年,JSHint 一直是前端代码检测工具的首选,包括 Nicholas C. Zakas 也是 JSHint 的用户。但在 2013 年,Zakas 大佬发现 JSHint 已经无法满足自己定制化规则的需求,而且和 Anton 讨论后达成共识这根本在不可能在 JSHint 上实现。同时 Zakas 还设想发明一个基于 AST 的 lint,可以动态执行额外的规则,同时可以很方便的扩展规则。

2013 年的 6 月份,Zakas 发布了全新的 lint 工具——ESLint。

ESLint的特点:

  • 可扩展
  • 每条规则独立
  • 可自定义规则

ESlint有一些内置的规则让你更容易上手,同时你可以随时加载自己的规则。

发展&契机

ESLint 的出现并没有撼动 JSHint 的霸主地位。由于前者是利用 AST 处理规则,用 Esprima 解析代码,执行速度要比只需要一步搞定的 JSHint 慢很多;其次当时已经有许多编辑器对 JSHint 支持完善,生态足够强大。真正让 ESLint 逆袭的是 ECMAScript 6 的出现。

ES2015

2015 年 6 月,ES2015 规范正式发布。但是发布后,市面上浏览器对最新标准的支持情况极其有限。如果想要提前体验最新标准的语法,就得靠 Babel 之类的工具将代码编译成 ES5 甚至更低的版本,同时一些实验性的特性也能靠 Babel 转换。这时 JSHint 就略尴尬,ES2015 变化很大,短期内无法完全支持。ESLint 可扩展的优势一下就体现出来了,不仅可以扩展规则,甚至连解析器也能替换。Babel 团队就为 ESLint 开发了 babel-eslint 替换默认解析器,让 ESLint 率先支持 ES2015 语法。

React&JSX

也是在 2015 年,React 的应用越来越广泛,诞生不久的 JSX 也愈加流行。ESLint 本身也不支持 JSX 语法。还是因为可扩展性,eslint-plugin-react[10] 的出现让 ESLint 也能支持当时 React 特有的规则。

至此,ESLint 完美躺赢,替代 JSHint 成为前端主流工具

How--在RN项目中如何利用ESLint实现高效的代码规范校验

ESLint的安装

你可以使用 npm 安装 ESLint,在JS工程的根目录下执行:

$ npm install eslint --save-dev

紧接着你应该设置一个配置文件(初始化):

$ ./node_modules/.bin/eslint --init

之后,你可以在任何文件或目录上运行ESLint如下:

$ ./node_modules/.bin/eslint yourfile.js

也可以在全局而不是本地安装 ESLint (使用 npm install eslint --global)。但是,你使用的任何插件或可共享配置都必须安装在本地。

ESLint的配置

运行 eslint --init 之后,.eslintrc 文件会在你的文件夹中自动创建(文件格式可以是“.js”、“.json”等)。例如一个.eslintrc.json文件内容如下:

{
    "env": {
        "browser": true,
        "es6": true,
        "react-native/react-native": true
    },
    "parser": "babel-eslint",
    "extends": [
        "eslint:recommended",
        "airbnb",
        "plugin:react-native/all",
        "plugin:flowtype/recommended"
    ],
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly"
    },
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 6,
        "sourceType": "module"
    },
    "plugins": [
        "react",
        "react-native",
        "flowtype"
    ],
    "rules": {
        "react/jsx-filename-extension": "off",
        "no-use-before-define": "off",
        "react-native/sort-styles": "off",
        "arrow-body-style": "off",
        "indent": "off",
        "react/jsx-indent": "off"
    }
}

parser:解析器

如前述,ESLint支持替换默认解析器。当代码中用到了一些比较新的语法特性(例如需要Babel转换的一些新特性或实验特性)时,就需要使用一些其他的解析器,例如babel-eslint。

当然使用的前提是先在本地安装对应的解析器,例如:

npm install eslint babel-eslint --save-dev

安装后在配置文件中,设置对应的parser字段为自定义的解析器。(这里设置的为babel-eslint)

"parser": "babel-eslint",

extends:继承

    "extends": [
        "eslint:recommended",
        "plugin:react/recommended",
        "airbnb",
        "plugin:react-native/all",
        "plugin:flowtype/recommended"
    ],

可以继承一些现有的规则配置(规则集合),例如ESLint内置的推荐规则集(eslint:recommended),还有一些流行的第三方配置(例如:airbnb)或者第三方插件中的配置(例如:plugin:react/recommended)等。

  • 可以指定继承多个配置
  • 继承的配置可以是内置配置,例如:"eslint:recommended"
  • 可以是第三方配置包中的配置(例如eslint-config-airbnb),可以省略包名的前缀 eslint-config-,例如:"airbnb"
  • 也可以是第三方插件中的配置, extends 属性值可以由以下组成:(例如"plugin:react/recommended")
    • plugin:
    • 包名 (省略了前缀,比如,react)
    • /
    • 配置名称 (比如 recommended)
  • 继承按顺序依次继承,如果有相同规则的设置,后面的会覆盖前面的

plugins:插件

ESLint 支持使用第三方插件。在使用插件之前,你必须使用 npm 安装它。

在配置文件里配置插件时,可以使用 plugins 关键字来存放插件名字的列表。插件名称可以省略 eslint-plugin- 前缀。

注意:插件是相对于 ESLint 进程的当前工作目录解析的。换句话说,ESLint 将加载与用户通过从项目 Node 交互解释器运行 ('eslint-plugin-pluginname') 获得的相同的插件。

当你需要自定义一些ESLint没有提供规则的时候就需要用的插件,可以通过自定义或者使用别人开发好的插件来加入一些新的代码规则。

配置包(eslint-config-) VS 插件(eslint-plugin-)
相同点
  • 都是 npm 包
  • 都可以共享,通过npm可以安装别人分享的包
  • 都可以对ESLint默认的规则进行设置
  • 都可以提供规则集(配置)
不同点
  • 配置包(eslint-config-),只是对已有规则的修改、聚合等
  • 插件(eslint-plugin-),也可以提供配置,同时可以新增自定义规则

rules:规则

可以在rules中添加一些额外的规则(除extends继承的配置中启用的),或者对extends引入的规则进行二次定制。

  • 规则启用状态设置:要改变一个规则设置,你必须将规则 ID 设置为下列值之一:

    • "off" 或 0 - 关闭规则
    • "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
    • "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
  • 额外选项设置:如果一个规则有额外的选项,你可以使用数组字面量指定它们,比如:

    {
        "rules": {
            "quotes": ["error", "double"]
        }
    }
    

"quotes": ["error", "double"] 为规则 quotes 指定了 “double”选项(双引号)。数组的第一项总是规则的严重程度(数字或字符串)。

  • 配置定义在插件中的一个规则的时候,你必须使用 插件名/规则ID 的形式。比如:

    "rules": {
            "react/jsx-filename-extension": "off",
            "react/jsx-indent": "off"
     }
    

    注意:当指定来自插件的规则时,确保删除 eslint-plugin- 前缀。ESLint 在内部只使用没有前缀的名称去定位规则。

ESLint与开发环境(工具)集成

WebStorm

  1. 进入ESLint设置:Preferences -> Languages & Frameworks -> JavaScript-> Code Quality Tools -> ESLint
  2. 选择 Menual ESlint configuration
  3. ESlint package 设置为全局路径(如果是本地安装,选择本地路径):/usr/local/lib/node_modules/eslint
  4. Configuration file 路径手动设置为项目 eslint 配置文件的路径,例如:/Users/xxx/Documents/work_6/JD_Recharge/RN_Recharge/jdreact-jsbundle-jdreactrechargemodule/.eslintrc.json
webstorm设置

设置好以后,WebStorm就会自动检测工程中的代码,并会在发现的错误(或不符合规范)处进行波浪线标注,例如:

Tips

1.sourceType 是什么意思?

sourceType 有两个值,script 和 module。 对于 ES6+ 的语法和用 import / export 的语法必须用 module.

2. eslint --init

只有在选择 "To check syntax, find problems, and enforce code style" 才可以选择 airbnb, standard, recommended 标准。

3. 环境

有时候在前端项目中存在前端和 node 的代码共存的情况,只要在 env 中配置好 browser: true, node: true 就可以把兼容不同环境的全局变量兼容进来,例如 nodejs 中的 global, process 等等。

4. 常见规则强度

规则强度是 airbnb > standard > recommended. 看下图,

recommended 和 standard 大概有 88 出不同,主要是 recommended 很多都是 off, standard 是 error, 同时 standard 还有很多特有的规则。

左边是 recommended, 右边是 standard
standard 特有的规则

5. 关于自动修复

现在代码都比较严格,可能包含缩进是 2 个空格,是否在语句最后加逗号的情况。不可能自己手动去一个个修正。

eslint ./src --fix 

加上 --fix 可以自动修正一些明显的问题。

参考:

前端工具考 - ESLint 篇:ESLint的诞生和发展