為了統一團隊的代碼規範,除了一紙規範說明之外,還需要引入工具進行限制。雖說工具并不能完全實作規範中的規則,但至少能夠在一定程度上緩解代碼不統一的局面。
相對于後端,前端代碼規範的品質檢查涉及到HTML, CSS,Javascript ,如今還涉及到SCSS,ES5,JSX, React,Vue,Angular等,更是複雜。
本文提供了在檢查工具方面的規則制定,在編輯器IDE中進行配置,在webpack中進行打包。讓開發小夥伴有所參考
相關規則可以在 webpack4項目demo 中看到,裡頭放了相關的規則連結注釋,歡迎圍觀~
1. 工具選取
筆者對常見的代碼檢查工具做了一番調研,結合規則支援度,配置方式,在編輯器Sublime于Webstrom這隻IDE上的支援度,在webpack打包的支援,最終确立了使用如下方案
HTML / tpl: HTMLHint
CSS / SCSS: StyleLint
JS / JSX: ESLint
對比參考: JavaScript 代碼靜态品質檢查 CSS 代碼靜态品質檢查 HTML代碼風格檢查工具對比
盡管如此,這三個插件也并不完美,有太多太多的坑踩遍了,如果你有更合适的套件,歡迎建議~
2. 規則制定
選取了工具之後,就需要确立相應的規則。
規則非常多,對我們這種沒經驗的小白是不可能一條一條自主去選取的,是以需要依據某些參考。但也隻能是參考,我們需要把這些通用的設定,結合到我們實際項目中,并一條條去了解規則,最終選出并摘錄進我們的規則集中。
ESLint規則
ESLint規則最多,參考自 eslint-config-alloy,再加入我們的自定義
// 自定義的規則
rules: {
// 必須使用 === 或 !==,禁止使用 == 或 !=,與 null 比較時除外
// @warn 在異步接口傳回時不确定參數是數值還是字元串,有時可利用這個類型轉換
'eqeqeq': 'warn',
// 禁止在 if 代碼塊内出現函數聲明
// @off 在for循環中會經常使用定義var for(var i = 0; i < 10; ++i)
'no-inner-declarations': 'off',
// switch 的 case 内有變量定義的時候,必須使用大括号将 case 内變成一個代碼塊
// @off 太嚴格
'no-case-declarations': 'off',
// 禁止使用 !! ~ 等難以了解的運算符
// @off 有些時候會用到 if (!!abc) '' + 100 +new Date() 等
'no-implicit-coercion': 'off',
// 禁止在全局作用域下定義變量或申明函數
// @off 太嚴格
'no-implicit-globals': 'off',
// 禁止使用沒必要的 {} 作為代碼塊
// @off 有時候需要用代碼塊做邏輯區分
'no-lone-blocks': 'off',
// 禁止出現 location.href = 'javascript:void(0)';
// @off 有時候需要用便捷的 javascript:;
'no-script-url': 'off',
// 對象字面量隻有一行時,大括号内的首尾必須有空格
// @off 沒有必要限制
'object-curly-spacing': 'off',
// 禁止對函數的參數重新指派
// @warn 警示即可
'no-param-reassign': 'warn',
// 檔案最後一行必須有一個空行
// @error 應該在檔案末尾保持一個換行
'eol-last': 'error',
// 代碼塊嵌套的深度禁止超過 10 層
// @warn 有些特殊情況會出現 警示即可
'max-depth': [
'warn',
10
],
// 禁止函數的循環複雜度超過 100
// @error 最大值可以寬松點
'complexity': [
'error',
{
max: 100
}
],
// 定義過的變量必須使用
// @warn 多檔案互相引用時 偶爾會出現無引用的情況
'no-unused-vars': [
'warn',
{
vars: 'all',
args: 'none',
caughtErrors: 'none',
ignoreRestSiblings: true
}
],
// 在ES5中需使用var
// @off 沒有必要限制
'no-var': 'off',
// 禁止使用未定義的變量 建議将相關變量在上方 globals 配置項中配置
// @warn 警示即可
'no-undef': 'warn',
// 函數的參數禁止超過10個
// @warn 警示即可
'max-params': ['warn', 10],
// 回調函數嵌套禁止超過 5 層
// @warn 警示即可
'max-nested-callbacks': ['warn', 5],
// 循環内的函數中不能出現循環體條件語句中定義的變量
// @warn 警示即可
'no-loop-func': 'warn',
// Promise 的 reject 中必須傳入 Error 對象
// @off 不需要限制
'prefer-promise-reject-errors': 'off',
// 變量聲明時盡量使用一個var聲明連續的多個
// @warn 警示即可
'one-var': [
'error',
'consecutive'
],
// 變量申明必須每行一個
// @error 指派時保證處于一行即可
'one-var-declaration-per-line': [
'error',
'initializations'
],
// 禁止使用已廢棄的 api
// @off 不需要限制
'react/no-deprecated': 'off',
// 禁止使用字元串 ref
// @warn 警告即可
'react/no-string-refs': 'warn',
// 必須使用 Class 的形式建立元件
// @warn 警告即可
'react/prefer-es6-class': [
'warn',
'always'
],
// 禁止在 componentDidUpdate 裡面使用 setState
// @warn 警告即可
'react/no-did-update-set-state': 'warn',
// 元件内方法必須按照一定規則排序
// @off 不需要限制
'react/sort-comp': 'off',
// jsx 的 props 縮進必須為四個空格
// @off 不需要限制
// 'react/jsx-indent-props': 'off',
}
StyleLint規則
ESLint規則也很多,以 stylelint-config-standard 為基礎,加入自定義
rules: {
// 顔色值避免直接使用顔色名
'color-named': [
'never', {
ignore: ['inside-function']
}
],
// 使用數字或命名的 (可能的情況下) font-weight 值
'font-weight-notation': 'numeric',
// 在函數的逗号之後要求有一個換行符或禁止有空白
'function-comma-newline-after': null,
// 在函數的括号内要求有一個換行符或禁止有空白
'function-parentheses-newline-inside': null,
// url使用引号
'function-url-quotes': 'always',
// 禁止小于 1 的小數的前導 0
'number-leading-zero': 'never',
// 字元串使用雙引号
'string-quotes': 'double',
// 要求選擇器清單的逗号之前有一個換行符
'selector-list-comma-newline-before': 'never-multi-line',
// 在媒體查詢的逗号之前禁止有一換行
'media-query-list-comma-newline-before': 'never-multi-line',
// 縮進
'indentation': 4,
// 禁止低優先級的選擇器出現在高優先級的選擇器之後
'no-descending-specificity': null,
// 禁止空源
'no-empty-source': null,
// 禁止缺少檔案末尾的換行符
'no-missing-end-of-source-newline': null
}
HtmlHint規則
HtmlHint的規則比較少,可以直接自定義
要注意的是它并不支援JS文法,需要使用JSON格式(在webpack中會強制按這個文法parse)
{
"_comment": [
"自定義的HTMLHint配置項",
"規則中文 @see https://segmentfault.com/a/1190000013276858",
"規則英文 @see https://github.com/yaniswang/HTMLHint/wiki/Rules",
"使用注釋自定義規則 @see https://github.com/yaniswang/HTMLHint/wiki/Usage#cli"
],
"_comment": "标簽名必須小寫",
"tagname-lowercase": true,
"_comment": "屬性名必須小寫",
"attr-lowercase": false,
"_comment": "屬性值必須放在雙引号中",
"attr-value-double-quotes": true,
"_comment": "屬性值一定不可為空",
"attr-value-not-empty": false,
"_comment": "屬性值一定不可重複",
"attr-no-duplication": true,
"_comment": "Doctype必須是 HTML 文檔的第一行",
"doctype-first": false,
"_comment": "标簽必須成對",
"tag-pair": true,
"_comment": "标簽必須自封閉",
"tag-self-close": false,
"_comment": "特殊字元必須轉義",
"spec-char-escape": false,
"_comment": "ID 屬性必須唯一",
"id-unique": true,
"_comment": "src 屬性一定不可為空",
"src-not-empty": true,
"_comment": "title 屬性必須出現在标簽中",
"title-require": false,
"_comment": "img 标簽必須包含 alt 屬性",
"alt-require": true,
"_comment": "Doctype 必須是 HTML5",
"doctype-html5": true,
"_comment": "ID 和 Class 的命名規則必須統一",
"id-class-value": false,
"_comment": "不該使用樣式标簽",
"style-disabled": false,
"_comment": "不該使用行内樣式",
"inline-style-disabled": false,
"_comment": "不該使用行内腳本",
"inline-script-disabled": false,
"_comment": "空格和制表符一定不可混合在行前",
"space-tab-mixed-disabled": "space4",
"_comment": "ID 和 Class 一定不可使用廣告關鍵詞",
"id-class-ad-disabled": false,
"_comment": "href 必須是絕對路徑或者相對路徑",
"href-abs-or-rel": false,
"_comment": "屬性值一定不可使用不安全字元",
"attr-unsafe-chars": true,
"_comment": "script 标簽不該使用在頭部",
"head-script-disabled": false
}
對于頁面中嵌入的CSS與JS,也需要進行檢查。
在ESlint中提供了 eslint-plugin-html 插件,然而對<style> 與 <script> 造成的縮進處理不當(配置失效的樣子),這個是比較難搞的
// 檢查html檔案(或tpl檔案)中的JS
plugins: [
'html'
],
settings: {
'html/html-extensions': ['.html', '.tpl'],
// 'html/indent': '+4'
},
在StyleLint中提供了 stylelint-processor-arbitrary-tags 插件,不過新版似乎内置了支援。然而也并算完美,至少能用就行
在Sublime,Webstorm或其他編輯器IDE中使用這些工具的前提:
安裝NodeJS,然後使用NPM在全局安裝以下依賴包
npm i -g eslint babel-eslint eslint-config-alloy eslint-plugin-html eslint-plugin-react stylelint stylelint-config-standard htmlhint
在項目根目錄下添加三個工具對應的檔案 (這三個檔案即為對應的檢查規則集),以便代碼編輯器在任何地方都能找到配置檔案,如
ESLint 和 StyleLint 工具提供了自動修複功能,可以修複簡單的錯誤如少了分号,多了空格,縮進不正确等
但要注意的是,自動修複某些時候可能會使代碼發生邏輯或文法錯誤,需謹慎使用(自動修複後一定一定一定記得比對代碼,確定無誤)
3. 在Sublime中的配置
sublime安裝對應的linter工具,以SublimeLinter工具為基礎進行配置
Ctrl+Shift+P 調出安裝插件層,輸入關鍵字 sublimelinter 進行搜尋安裝
再安裝相應的工具插件,SublimeLinter-eslint , SublimeLinter-stylelint, SublimeLinter-contrib-htmlhint
安裝 ESLint-Formatter 以支援自動修複檢查的錯誤
新增一個建構任務,可命名為,StyleLint-Fix.sublime-build 以支援自動修複檢查的錯誤
在其中填入以下内容,儲存在相應的位置即可
{
"shell_cmd": "stylelint $folder/node_modules/.bin/eslint --fix $file"
}
接下來就是配置 SublimeLinter
打開插件配置,在User部分填入以下内容并儲存即可
stylelint配置中的executable全局路徑需要設定好
// SublimeLinter Settings - User
{
"debug": true,
// "delay": 0.2,
"lint_mode": "manual",
// "syntax_map": {
// "html (django)": "html",
// "html (rails)": "html",
// "html 5": "html",
// "css": "css",
// "javascript (babel)": "javascript",
// "magicpython": "python",
// "php": "html",
// "python django": "python",
// "pythonimproved": "python"
// },
"styles": [
{
"scope": "region.yellowish markup.warning.sublime_linter",
"types": ["warning"]
},
{
"scope": "region.redish markup.error.sublime_linter",
"types": ["error"]
},
{
"priority": 1,
"icon": "dot",
"mark_style": "outline"
}
],
"linters": {
"eslint": {
// 讓eslint能夠識别html頁面中嵌入的JS
"selector": "source.js | text.html.basic"
},
// 下面三個sublimelinter預設都支援,為防止檢查幹擾,需要禁用它們
"scsslint": {
"disable": true
},
"csslint": {
// "disable": true
},
"htmllint": {
"disable": true
},
"stylelint": {
// 似乎Sublime的stylelint需要手動設定到全局路徑
// "executable": "C:\\Users\\e470\\AppData\\Roaming\\npm\\stylelint.cmd"
"executable": "/usr/local/bin/stylelint"
}
}
}
可以看到,在sublimelinter的配置中是以手動(manual)模式進行調用檢查的,可以防止某些檔案代碼量太大,頻繁檢查消耗性能
需要檢查的時候,在目前檔案打開指令即可,或者使用對應快捷鍵(如果看不到指令,就采用重新開機大法吧)
以下指令關鍵字都是在以 Ctrl+Shift+P打開指令層的前提下進行的
Lint This View ,執行檢查
SublimeLinter還支援檢查HTML或tpl檔案裡嵌入的JS和CSS, 但Webstorm不行唷~~
Show All Errors,在底部顯示錯誤清單
使用 ESlint-formatter進行自動修複JS
使用 StyleLint-Fix 進行自動修複CSS
這個需要調出建構任務清單層,或者使用快捷鍵 Ctrl+Shift+B,選擇我們的fix任務執行即可
HTMLHint的不提供自動修複功能
4. 在WebStorm中的配置
打開設定
啟用内置的ESLint檢查
啟用内置的StyleLint檢查
本地安裝 HTMLHint插件,下載下傳位址,注意 此插件僅可支援檢查HTML字尾檔案,不支援tpl,有興趣的可以給作者提PR
安裝之後,可能需要重新開機,在清單中可以看到插件配置入口
是以插件比較特殊,在windows下,bin中請使用 node執行程式的絕對路徑 全局 htmlhint的絕對路徑
其他環境下就慢慢試吧..
bin: D:\Program Files\nodejs\node.exe C:\Users\e470\AppData\Roaming\npm\node_modules\htmlhint\bin\htmlhint
path: .htmlhintrc
内置的ESLint與StyleLint不支援自動修複功能,是以我們需要手動建立 File Watcher
配置成手動執行可能會更好些
需要執行的時候,執行即可
5. 在webpack中的配置
參考我的webpack項目配置DEMO, 在 webpack.config.js 中傳入相應的參數
正式使用時autoFix會按需設定,建議修複。如果選擇修複,webpack将按子產品的設定進行批量修複,可能會有大量檔案被修改,是以需要做好代碼比對工作
另外,開啟自動修複可能會導緻webpack編譯無限循環的問題,對于這個我們可以引入一個新的插件 time-fix-plugin ,直接調用即可
new TimeFixPlugin()
在使用 htmlhint-loader的時候,webpack預設無法識别html資源,在以往我們可以直接使用 htmlWebpackPlugin來識别,因為它内置支援了ejs-loader
但現在這個代碼檢查插入之後,我們就需要手動設定好html文法的loader。
不能使用 html-loader ,使用之後會導緻無法識别我們的ejs文法,導緻htmlWebpackPlugin的資源插入失效
解決辦法也很簡單,使用 ejs-loader 即可,見下方配置
另外,在生産模式 npm run build:prod的時候,提供了将檢查結果輸出到檔案的功能(css的不支援),見 lint目錄
雖然有點錯亂,也夠搜尋存檔用了
而具體在webpack的核心配置檔案裡面,配置也是挺簡單的,雖然也有蠻多不如意
首先相關的npm包需要安裝好,使用 htmlhint-loader eslint-loader stylelint-webpack-plugin
配置核心部分
new HappyPack({
id: 'js',
use: configs.lint.js.open ? [{
loader: 'babel-loader',
options: {
// cacheDirectory: true
}
}, {
enforce: 'pre',
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
fix: configs.lint.js.autoFix,
cache: true,
emitWarning: !configs.lint.js.emitAsError,
failOnError: configs.lint.js.failOnError,
formatter: require('eslint-friendly-formatter'),
outputReport: {
filePath: cwdRalativeOutputPath + '/lint/js/[name].xml',
formatter: require('eslint-friendly-formatter')
}
}
}] : [{
loader: 'babel-loader',
options: {
// cacheDirectory: true
}
}]
}),
new HappyPack({
id: 'html',
use: configs.lint.html.open ? [{
loader: 'ejs-loader',
options: {
}
}, {
loader: 'htmlhint-loader',
enforce: 'pre',
exclude: /node_modules/,
options: {
configFile: configs.lint.html.configFile,
failOnError: configs.lint.html.failOnError,
outputReport: {
filePath: cwdRalativeOutputPath + '/lint/html/[name].xml'
}
}
}] : [{
loader: 'ejs-loader',
options: {
}
}]
}),
// stylelint檢查
if (configs.lint.css.open) {
commonConfig.plugins.push(new StyleLintPlugin({
fix: configs.lint.css.autoFix,
emitErrors: configs.lint.css.emitAsError,
failOnError: configs.lint.css.failOnError,
formatter: require('stylelint-formatter-pretty')
}));
}
[-_-]眼睛累了吧,注意勞逸結合呀[-_-]