前言
我是一名從事低代碼平臺研發的前端CV程序猿,有幾十名像我一樣的小伙伴協同研發。在長期的多人協作和滾動迭代中,不出意外,代碼中會充斥各種“壞味道”,如代碼風格不統一、擴展性和靈活性降低等問題。我們是如何解決這些問題的呢?今天介紹的法寶之一就是大名鼎鼎的ESLint。
ESlint是何方神圣?
ESLint?是一個用于?JavaScript?代碼靜態分析的工具,它可以幫助開發團隊遵循一致的代碼風格和最佳實踐。ESLint?解決了以下幾個痛點:
1.代碼風格統一:在多人協作的項目中,不同的開發者可能有不同的編碼風格習慣,導致代碼風格不一致。ESLint?可以定義代碼規則,并對代碼進行檢查和修復,以確保整個項目的代碼風格保持一致,提高代碼的可讀性和可維護性。
2.發現潛在的錯誤和問題:ESLint?通過靜態分析代碼,可以發現潛在的錯誤、漏洞和常見的編碼問題。例如,未聲明的變量、不推薦使用的語法、潛在的邏輯錯誤等。通過及早發現這些問題,可以提高代碼質量和可靠性。
3.支持最佳實踐:ESLint?提供了一系列的規則,可以幫助開發者遵循最佳實踐和行業標準。例如,強制使用嚴格模式、禁止使用已廢棄的?API、強制使用代碼塊等。這些規則可以幫助開發者編寫更健壯、可靠的代碼。
4.可擴展性和靈活性:ESLint 具有高度的可配置性和可擴展性。開發者可以根據項目的需求,自定義規則和配置,以適應不同的開發環境和項目要求。此外,ESLint?還支持插件系統,可以集成其他工具或規則集,以滿足特定的需求。
ESLint的安裝使用
ESLint在以下情況下特別有用:
1.項目初始化:?在創建新項目時,使用ESLint 可以確保從一開始就保持良好的代碼質量。
2.團隊協作:?當多個開發者合作時,ESLint 有助于維持一致的代碼風格,減少代碼審查時的沖突。
3.持續集成:?將ESLint集成到持續集成工具中,可以確保每次提交都符合項目的代碼規范。
1.拓展安裝:vscode編輯器
在編輯器內安裝ESLint插件,并且確保項目路徑下.vscode/settings.json中?"editor.codeActionsOnSave"."source.fixAll.eslint"?已設置為true,具體效果為在vue或者js中沒對齊js代碼時,保存可以自動完成對齊格式化
2.拓展安裝:非vscode工具
如idea,內部提供了根據eslint格式化的功能,在完成了編碼后,可以自行使用該功能進行格式化,或者嘗試設置為保存,即馬上格式化
3.項目安裝
npm i eslint -D
4.創建規則配置文件-.eslintrc.js
文章最后附帶彩蛋!!!!
off/0:關閉規則
warn/1:?打開規則,并且作為一個警告(不影響exit?code)
error/2:打開規則,并且作為一個錯誤(exit?code將會是1)
// 也可通過 npx eslint --init 來自動化生成對應配置文件
// 根目錄創建.eslintrc.js
module.exports = {root: true,parserOptions: {parser: 'babel-eslint'},env: {browser: true},extends: ['plugin:vue/essential','standard'],// required to lint *.vue filesplugins: ['vue'],// add your custom rules hererules: {// 禁止函數圓括號之前有一個空格'space-before-function-paren': ['error', 'never'],......}
}
5.按需使用.eslintignore
// 根目錄創建.eslintignore
/build/
/config/
/dist/
6.檢查本地項目代碼
// pacakage.json
"scripts": {"lint": "eslint --ext .js,.vue src ..."
}// 控制臺
npm run lint// 批量規范格式化的指令為npm run lintFix
// 如果當前需求修改內容較多,且此前沒有進行過格式化的話,需要執行此指令進行全量格式化
7.異常處理
當前eslint會通過git提交前校驗執行,即當使用git?commit?命令時,【husk】就會自動調用eslint的全局檢查(后面會出文章會講解如何配置),出現問題時會拋出異常,此時需要各位開發者認真檢查下報錯信息,如果是規范問題的話,可以直接根據報錯信息定位到對應的文件。原則上不允許隨意使用跳過命令來躲避檢查,如果有不知道如何解決的情況,請咨詢你的導師,旨在通過完善編碼規范和知識庫來統一思想,并通過自動化手段來約束團隊。
8.可參考的配置文件(實踐中)
rules: {
// 關閉有關生成器 空格的規則
'generator-star-spacing': 'off',
// 關閉消除未使用的變量,函數和函數的參數
'no-unused-vars': 0,
// 不允許在對象文字中的鍵和冒號之間使用空格
'key-spacing': ['error', { beforeColon: false }],// 括號內的空格限制
'array-bracket-spacing': ['error', 'never'],
### 正確示例
let a,b
### 錯誤示例
let a, b// 聲明變量時強制換行
'one-var-declaration-per-line': ['error', 'always'],// 禁止函數圓括號之前有一個空格
'space-before-function-paren': ['error', 'never'],
### 正確示例
function() {}
### 錯誤示例
function () {}// 除了生產環境,允許debugger
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',// 關閉禁用不必要的return await
'no-return-await': 0,// 限制最大長度不超過140個字符
**目前注釋的原因是:在設置限制行數140臨近幾個數字,項目運行時候會卡在70%進度不動。設置在145以后就沒有問題,錯誤數不多**
// 'max-len': [
// 'error',
// {
// code: 140,
// ignoreComments: true, // 忽略所有拖尾注釋和行內注釋
// ignoreUrls: true, // 忽略含有鏈接的行
// ignoreStrings: true, // 不忽略字符串的行
// ignoreTemplateLiterals: true // 忽略模板字符串的行
// }
// ],// 強制限制駝峰法
camelcase: ['error', { properties: 'never' }],
### 正確示例
let isExamle = ''
### 錯誤示例
let is_example = ''// 關閉callback必須有參數,關閉回調報錯(callback(true/false))
'standard/no-callback-literal': 'off',// 允許-在字符串中出現了Control的字符
'vue/no-parsing-error': 'off',// 關閉計算的屬性括號內強制執行一致的間距
'standard/computed-property-even-spacing': 'off',// 運算符放到后面
'operator-linebreak': [
'error',
'after',{ overrides: { '?': 'before', ':': 'before' } }
],
### 正確示例
let a +b +c
### 錯誤示例
let a + b + c// 逗號放在后面
'comma-style': ['error', 'last'],
### 正確示例
let a,b,c
### 錯誤示例
let a ,b ,c// 要求條件語句需要大括號
curly: ['error', 'all'],
### 正確示例
if() {return}
### 錯誤示例
if() return// 句末不出現分號
semi: ['error', 'never'],// 行注釋必須另起一行
// 'line-comment-position': ['error', { position: 'above' }],
### 正確示例// 引入font-icon
@import '~@/assets/icon/iconfont';
### 錯誤示例
@import '~@/assets/icon/iconfont'; // 引入font-icon// 要求在注釋之前有一個空格
'spaced-comment': ['error', 'always'],
### 正確示例// 引入font-icon
### 錯誤示例
//引入font-icon// 強制單引號和反勾號
quotes: ['error', 'single', { allowTemplateLiterals: true }],// 建議使用let代替var(由于是建議,用警告)
'no-var': ['warn'],// $emit不校驗kebab-case,自定義事件名使用短橫線方式
'vue/custom-event-name-casing': 0,
### --->vue /對自定義事件名稱強制使用特定的大小寫:https://eslint.vuejs.org/rules/custom-event-name-casing.html// prop類型不能被假定為構造函數
'vue/require-prop-type-constructor': 0,
### --->vue /要求prop類型是構造函數:
https://eslint.vuejs.org/rules/require-prop-type-constructor.html// 對象字面值屬性名稱引號
'quote-props': 0,// 不要在子組件內部改變 prop
'vue/no-mutating-props': 0,
### --->vue /無變異道具:
https://eslint.vuejs.org/rules/no-mutating-props.html#rule-details// 不允許v-if和v-for一起用
'vue/no-use-v-if-with-v-for': 0,
### --->vue /禁止在同一元素上使用v-if和v-for:
https://eslint.vuejs.org/rules/no-use-v-if-with-v-for.html// 強制 計算屬性(computed)有return
'vue/return-in-computed-property': 0,
### --->vue /計算屬性返回-->強制在計算屬性中存在return語句:
https://eslint.vuejs.org/rules/return-in-computed-property.html// 禁止直接使用 Object.prototypes 的內置屬性
'no-prototype-builtins': 0,
### 正確示例
var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar");
### 錯誤示例
let hasBarProperty = foo.hasOwnProperty("bar");// 在promise使用async
'no-async-promise-executor': 0,
### --->禁止將異步功能用作Promise執行器:
https://cn.eslint.org/docs/rules/no-async-promise-executor// 檢查每個prop的默認值對于給定類型是否有效
'vue/require-valid-default-prop': 0,
### --->vue /要求有效默認屬性:
https://eslint.vuejs.org/rules/require-valid-default-prop.html
// 禁用不必要的轉義字符
'no-useless-escape': 0,
// 禁止使用未注冊的組件
'vue/no-unused-components': 0,
### ---> Vue /沒有使用的組件:https://eslint.vuejs.org/rules/no-unused-components.html// 未使用的scope等var
'vue/no-unused-vars': 0,
### ---> Vue /沒有使用過的變量:https://eslint.vuejs.org/rules/no-unused-vars.html// 縮進代碼 2
"vue/html-indent": 2,// 強制每行的最大屬性數,默認是 1"vue/max-attributes-per-line": 2// 執行完當前判斷里的內容,立馬執行緊跟著的下一個里面的內容 例如switch的 case 沒有break,緊接著執行下一個case
'no-fallthrough': 'off',// 禁止在強制數組方法的回調函數中要return, 因為大部分時候使用循環是執行其它操作
'array-callback-return': 0
}
團隊編碼規范
經過一段時間的實踐,我們總結出了一套完整的編碼規范,下面介紹與主題相關的部分實踐。
HTML
● 屬性名全小寫,用中劃線做分隔符
● 不要在自動閉合標簽結尾處使用斜線(HTML5?規范?指出他們是可選的)
● 不要忽略可選的關閉標簽
減少標簽數量
● 在編寫HTML代碼時,需要盡量避免多余的父節點
● 例如:在書寫vue的template不書寫多余的div
● 很多時候,需要通過迭代和重構來使HTML變得更少
● 任何時候都要用盡量小的復雜度和盡量少的標簽來解決問題
JavaScript
1.【強制】使用2個空格做為一個縮進層級
2.【強制】switch?下的?case?和?default?必須增加一個縮進層級。
3. 空格類部分已經由工程中eslint規則進行定義
如:二元操作符左右兩邊都必須存在一個空格
一元操作符與變量名間不允許存在空格
定義對象時,屬性名和屬性值之間的冒號后必須存在一個空格
運算符處換行時,運算符必須處于行末尾
注釋符號//與注釋內容之間必須存在一個空格
.......
4.在if?/?else?/?for?/?do?/?while語句中,{}內部語句必須另起一行
5.語句末尾一律省略分號
6.注釋必須另起一行
7.【強制】函數/方法注釋必須包含函數說明,有參數和返回值時必須使用注釋標注,參考下面樣例:
正例:
/**
* 函數描述 *
* @param {string} p1 參數1的說明
* @param {string} p2 參數2的說明,比較長
* 那就換行了.
* @param {number=} p3 參數3的說明(可選)
* @return {Object} 返回值描述
*/
function foo(p1, p2, p3) {var p3 = p3 || 10return {p1: p1,p2: p2,p3: p3}
}
8.【強制】使用類型嚴格的===進行條件判斷
9.【建議】若條件表達式過長,建議使用定義布爾值變量代替
正例:
var condition1 = arr.indexOf(a) >= -1
var condition2 = b === 2 || c === 3
var condition3 = b !== 2 || d === 4
if (condition1 && condition2 && condition3) {
// …
}
反例:
if (arr.indexOf(a) >= -1 && (b === 2 || c === 3) && (b !== 2 || d === 4)) {
// …
}
常量命名
const CON_NUM = 10
let row = Math.ceil(num/CON_NUM)
變量命名
● 命名控件使用lowerCamelCase風格
● 標準變量:采用小寫駝峰式命名,前綴應當是名詞。(函數的名字前綴為動詞,以此區分變量和函數)
● 'ID'在變量名中全大寫
● 'URL'在變量名中全大寫
● 'Android'在變量名中大寫第一個字母
● 'iOS'在變量名中小寫第一個,大寫后兩個字母
● 組件變量:大駝峰式命名法,首字母大寫,例:TableComponent
● 常量:名稱全大寫,用下劃線連接,例:MAX_COUNT
● 命名建議:盡量在變量名字中體現所屬類型,如:length、count等表示數字類型;而包含name、title表示為字符串類型,布爾型變量使用is、has開頭命名。
● 【強制】字符串類型變量必須使用單引號,不允許使用雙引號
● 變量盡量即聲明即用,而不是統一聲明所有變量
● 在字符串中引用變量時,使用模板字符串代替使用+拼接。
函數命名
常規函數
● 小駝峰式命名法,前綴應當為動詞,call或者是handle等。
● 事件函數:以on開頭,例:onClick,onConfirm
(注:事件包括click事件,change事件,emit自定義事件,頁面跳轉事件)
命名建議:可使用常見動詞約定如下:
**動詞** | **含義** | **返回值** |
can | 判斷是否可執行某個動作(權限) | 函數返回一個布爾值。true:可執行;false:不可執行 |
has | 判斷是否含有某個值 | 函數返回一個布爾值。true:含有此值;false:不含有此值 |
is | 判斷是否為某個值 | 函數返回一個布爾值。true:為某個值;false:不為某個值 |
get | 獲取某個值 | 函數返回一個非布爾值 |
set | 設置某個值 | 無返回值、返回是否設置成功或者返回鏈式對象 |
load | 加載某些數據 | 無返回值或者返回是否加載完成的結果 |
類&構造函數
● 大駝峰式命名法,首字母大寫,前綴為名稱。
類的成員
● 公共屬性和方法:跟變量和函數的命名一樣
● 私有屬性和方法:前綴為_(下劃線),后面跟公共屬性和方法一樣的命名方式
注意事項:
1.【強制】不要在內置對象的原型上添加方法,如Array,?Date
2.【強制】不要在循環內部聲明函數
3.【強制】不要有空的代碼塊
CSS
【強制】class?必須單詞全字母小寫,單詞間以?_?分隔。
/* 正例 */
.sidebar {}
.sidebar_left {}
.sidebar_left_xx_xx {}
/* 反例 */
.Sidebar {}
.sidebarLeft {}
/* 正例 */
.foreign_dialog_footer {}
/* 反例 */
.footer {}
編碼順序【可使用stylelint】
[// 布局屬性'display','overflow','visibility','scroll-behavior','scroll-snap-align',// 布局屬性:定位'position','top','right','bottom','left','z-index',// 布局屬性:浮動'float','clear',// 布局屬性:列表'list-style','list-style-type','list-style-position','list-style-image',// 布局屬性:表格'table-layout','border-collapse','border-spacing','caption-side','empty-cells',// 布局屬性:彈性'flex-flow','flex-direction','flex-wrap','justify-content','align-content','align-items','align-self','flex','flex-grow','flex-shrink','flex-basis','order',// 布局屬性:多列'columns','column-width','column-count','column-gap','column-rule','column-rule-width','column-rule-style','column-rule-color','column-span','column-fill','column-break-before','column-break-after','column-break-inside',// 布局屬性:格柵'grid-columns','grid-rows',// 尺寸屬性'box-sizing','width','min-width','max-width','height','min-height','max-height','margin','margin-left','margin-right','margin-top','margin-bottom','padding','padding-left','padding-right','padding-top','padding-bottom','border','border-width','border-style','border-color','border-colors','border-left','border-left-width','border-left-style','border-left-color','border-left-colors','border-right','border-right-width','border-right-style','border-right-color','border-right-colors','border-top','border-top-width','border-top-style','border-top-color','border-top-colors','border-bottom','border-bottom-width','border-bottom-style','border-bottom-color','border-bottom-colors','border-radius','border-top-left-radius','border-top-right-radius','border-bottom-left-radius','border-bottom-right-radius','border-image','border-image-source','border-image-slice','border-image-width','border-image-outset','border-image-repeat','overflow-x','overflow-y',// 界面屬性'appearance','outline','outline-width','outline-style','outline-color','outline-offset','outline-radius','outline-radius-topleft','outline-radius-topright','outline-radius-bottomleft','outline-radius-bottomright','background','background-color','background-image','background-repeat','background-repeat-x','background-repeat-y','background-position','background-position-x','background-position-y','background-size','background-origin','background-clip','background-attachment','bakground-composite','mask','mask-mode','mask-image','mask-repeat','mask-repeat-x','mask-repeat-y','mask-position','mask-position-x','mask-position-y','mask-size','mask-origin','mask-clip','mask-attachment','mask-composite','mask-box-image','mask-box-image-source','mask-box-image-width','mask-box-image-outset','mask-box-image-repeat','mask-box-image-slice','box-shadow','box-reflect','backdrop-filter','mix-blend-mode','filter','opacity','object-fit','clip','clip-path','resize','zoom','cursor','pointer-events','touch-callout','user-modify','user-focus','user-input','user-select','user-drag',// 文字屬性'font','font-family','font-style','font-stretch','font-weight','font-variant','font-size','font-size-adjust','line-height','line-clamp','vertical-align','direction','unicode-bidi','writing-mode','ime-mode','text-overflow','text-decoration','text-decoration-line','text-decoration-style','text-decoration-color','text-decoration-skip','text-underline-position','text-align','text-align-last','text-justify','text-indent','text-stroke','text-stroke-width','text-stroke-color','text-shadow','text-transform','text-size-adjust','src','color',// 內容屬性'tab-size','overflow-wrap','word-wrap','word-break','word-spacing','letter-spacing','white-space','caret-color','quotes','content','content-visibility','counter-reset','counter-increment','page','page-break-before','page-break-after','page-break-inside',// 交互屬性'will-change','perspective','perspective-origin','backface-visibility','transform','transform-origin','transform-style','transition','transition-property','transition-duration','transition-timing-function','transition-delay','animation','animation-name','animation-duration','animation-timing-function','animation-delay','animation-iteration-count','animation-direction','animation-play-state','animation-fill-mode',// Webkit專有屬性'-webkit-overflow-scrolling','-webkit-text-fill-color','-webkit-tap-highlight-color'
]
&:deep(.el-input) {width: 60px;
}
Vue
Template
參考<VUE風格指南>
關于$refs的使用
為了維護的方便,防止出現修改原先屬性致出現全局屬性的錯誤的情況,?編碼過程中不允許出現使用$refs方式去引用變量的情況
// 組件A
export default {data() {return {propTest: {}}},methods: {getPropTest() {return this.propTest}}
}// 組件B中
export default {mounted() {console.log(this.$refs.propTest) // X 不允許使用console.log(this.$refs.getPropTest()) // √}
}
小結
總結起來,使用?ESLint?的步驟包括安裝?ESLint、初始化配置文件、運行?ESLint?來檢查代碼,并根據需要修復問題。通過配置和使用規則,你可以定制?ESLint?來確保項目中的代碼始終保持一致和高質量,并遵循最佳實踐。在持續集成和團隊協作中,ESLint是一個不可或缺的工具,助力開發者提升代碼的質量和效率。
作者介紹:
道一云,成立于2004年,是中國低代碼領域的領導廠商、騰訊戰略投資企業、騰訊生態核心合作伙伴。擁有自主知識產權管理軟件產品百余項,涵蓋數字化應用構建低代碼平臺-七巧、全場景智能業務分析BI-七析、千人千面、數智化辦公企業級門戶-七星以及30多款開箱即用的場景應用。
歡迎關注:
公眾號:道一云低代碼(do1info)
官網:道一云七巧 - 可視化、智能化、數字化應用構建
免費體驗:道一云產品免費試用