前端開發規范 從制定到實施
在我們開始這個題目的時候,我們應該思考下面幾個問題:
- 前端開發為什么需要 開發規范?
- 前端開發規范是什么?
- 什么時候需要這個規范?
- 如何制定這個規范?
- 如何落實這個規范?
一、前端開發為什么需要 開發規范?
why?
前端還在 刀耕火種的時代的時候,前端是沒有 工程
這個概念的。更多是切圖仔
這個概念,(將設計師設計出來的 web 、app、h5 的 UI
通過 PS 切圖
然后再通過 HTML、CSS 實現出來)的這么一個工種。那么隨着 互聯網興起到后來的移動互聯網發展,To C 產品需求 精細化, 用戶對於 C 端產品越來越挑剔也促使着 前端工程化越來越規范化了。 漸漸的前端工程
形成,那么隨之而來的就是 工程化 帶來的 規范化
。
回到 why
的問題上來,我們先舉一個簡單的例子來說,
可能 工程化 這個名詞在很多的后端語言中早早的就已經形成了,但前端 這么一門到現在大學課程都沒開課的工種,全靠大學畢業后工作中自學積累而來,那么我們是否可以說,現階段前端搞的好的人,Ta 的自學能力一定不會差。
二、前端開發規范是什么?
what?
什么是前端開發規范?
HTML\CSS\Javascript\TypeScript 的代碼編寫規范,這里我們着重講一下 JS 的 編碼規范
1、Types
javascript 的數據類型
1.1 基本數據類型
- string
- number
- boolean
- null
- undefined
- symbol
const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
1.2 引用數據類型
- object
- array
- function
const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
以上則 是 基礎數據 在 coding
的時候的基本規范。
2、References
變量、常量、定義規范
- 2.1 eslint:
prefer-const
,no-const-assign
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
建議使用 const 定義常量,可以阻止一些 重復定義導致的 bug
- 2.2 eslint:
no-var
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
推薦使用 let 來定義變量,形成塊級作用域,減少因為變量提升導致的bug
3、Objects
對象 定義規范
- 3.1 eslint:
no-new-object
// bad
const item = new Object();
// good
const item = {};
推薦使用 對象字面量來定義空對象,而不是通過 new 實例化進行操作
- 3.2 動態屬性名 使用 計算屬性
// bad
const obj = {
id: 5,
name: 'San Francisco',
};
obj[getKey('enabled')] = true;
// good
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
};
- 3.3 eslint:
object-shorthand
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
推薦 在給對象添加方法的時候,使用 簡化符號
- 3.4 eslint:
object-shorthand
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
方法名如果和對象的屬性名稱相同的時候,推薦使用簡化符號編寫
- 3.5 在使用
object-shorthand
的時候,推薦先寫出簡化符號的屬性或者方法
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
episodeOne: 1,
lukeSkywalker,
anakinSkywalker,
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
};
- 3.6 eslint:
quote-props: as-needed
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
推薦在 給對象屬性不要輕易加引號,除非必要的情況下,比如屬性的key帶有 符號等
- 3.7 eslint:
no-prototype-builtins
不使用 prototype 的內置命令
// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
/* or */
import has from 'has'; // https://www.npmjs.com/package/has
// ...
console.log(has.call(object, key));
推薦理由是:這些方法可能被有關對象上的屬性所遮蔽
- 3.8 對象擴展符 比使用
Object.assign
好
// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this
// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
推薦原因就是, 對象擴展符 比 Object.assign 看着舒服10倍吧
4、Arrays
- 4.1 eslint:
no-array-constructor
/*推薦使用通過字面量創建對象、數組*/
// bad
const items = new Array();
// good
const items = [];
- 4.2 Use
Array#push
/*推薦使用 push 來進行 stack add */
// bad
someArr[someArr.length] = 'xxxxsssswwww'
// good
someArr.push('xxxxsssswwww')
- 4.3 Use array spreads ... to copy arrays
/*推薦使用 `...` 進行數組 復制 */
// bad
for(i=0; i<arr.length; i++) { newArr[i] = arr[i] }
// good
let newArr = [...arr]
- 4.4 use spreads ... instead of Array.from
/*推薦使用 `...` 將可迭代對象轉換為數組 */
// bad
const foo = document.querySelectorAll('.foo'); nodes = Array.from(foo) // array[]
// good
let nodes = [...foo]
- 4.5 Use Array.from for converting an array-like object to an array
/*推薦使用 `Array.from` 將 array-like[類數組] 轉換為對象 */
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// bad
const arr = Array.prototype.slice.call(arrLike);
// good
const arr = Array.from(arrLike);
- 4.6 Use Array.from instead of spread ... for mapping over iterables
/*推薦使用 `Array.from` 實現可迭代數組方法而不是通過 ... */
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, fn);
- 4.7 eslint:
array-callback-return
/*推薦使用 `Array.from` 實現可迭代數組方法而不是通過 ... */
// good
[1, 2, 3].map(x => x + 1);
// 優化寫法、代碼簡潔易懂
- 4.8 Use line breaks after open and before close array brackets
/* 數組換行格式 優化 */
// bad
const arr = [
[0, 1], [2, 3], [4, 5],
];
// good
const arr = [[0, 1], [2, 3], [4, 5]];
5、Destructuring - 解構
- 5.1 eslint:
prefer-destructuring
對象結構
/* 對象結構 帶來的更加簡潔的代碼 */
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}
- 5.2 eslint:
prefer-destructuring
函數參數結構
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
- 5.3 eslint:
prefer-destructuring
數組結構
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
6、 String
...
7、 function
...
8、Classes & Constructors
這里的核心就是減少構造函數的使用、多使用 通過 class 來進行 類的創建、constructor、extends 實現繼承、構造函數等 原始的方法。
// bad
function Queue(contents = []) {
this.queue = [...contents];
}
Queue.prototype.pop = function () {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
};
// good
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}
9、Modules
模塊化的 導入、導出。
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
10、space
...
由於篇幅有限,這里就不做過多的描述了,詳情可以查看 Airbnb eslint 規則
三、什么時候需要這個規范?
我覺得任何時候都需要這個規范!!!
無論是一個人,還是 前端小組,甚至全公司的 大前端開發團隊 都是需要的!!!
四、如何制定這個規范?
如何定制這個規范
1、 CODE
代碼層面實現
借助ESLint的autofix功能,在保存代碼的時候,自動將拋出error的地方進行fix。因為我們項目是在webpack中引入eslint-loader來啟動eslint的,所以我們只要稍微修改webpack的配置,就能在啟動webpack-dev-server的時候,每次保存代碼同時自動對代碼進行格式化。
// webpack.config.base.js or webpack.config.dev.js
const path = require('path')
module.exports = {
module: {
rules: [
{
test: /\.(js|vue|jsx)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [path.join(__dirname, 'src')],
options: {
fix: true
}
}
]
}
2、lint
lint 規則層面:
秉承着 一個原則, 漸進式 規則完善,從最基本的 規范到 逐步 健全的規范落實,結合 code review 逐漸完善。
五、如何落實這個規范?
對了,關鍵點就是 如何落實這個規范!!!
1、IDE 的選擇
前端IDE開發的選擇,從大學階段的 DW
(Adobe Dreamweaver)、Notepad++
等等,再到后面的 Sublime
、Webstorm
再到后面的 Atom
、Visual Studio Code (VScode)
。
IDE 不斷的變化過程中也給了我們 更加高效編程的選擇
2、IDE 的 本地設置文件 setting.json
在 vscode 中的設置中配置(新舊不同版本的 vscode setting.json 的展現形式是不一樣的)
但是這個 格式化 往往只是最最進本的格式化,在前端如此多的 語言中很顯然是不夠用的,下面,我們就主要介紹下 `Prettier`
3、Prettier - Code formatter 格式化插件的使用
在 vscode 的應用商店進行 搜索(Prettier - Code formatter )下載安裝
在項目的根目錄創建 Prettier 的配置文件 `.prettierrc`
Prettier 格式化的配置文件文檔地址: https://prettier.io/docs/en/options.html
基本的配置文件格式如下:
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 140,
"semi": true,
"bracketSpacing": true,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}
那么,我們來看下 Prettier 做了哪些事情
能支持jsx
也能支持css
4、ESLint 與 Prettier配合使用
首先肯定是需要安裝prettier,並且你的項目中已經使用了ESLint,有eslintrc.js配置文件。
4.1 配合ESLint檢測代碼風格
eslint-plugin-prettier插件會調用prettier對你的代碼風格進行檢查,其原理是先使用prettier對你的代碼進行格式化,然后與格式化之前的代碼進行對比,如果過出現了不一致,這個地方就會被prettier進行標記。
//.eslintrc
{
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error"
}
}
這樣就 可以通過 eslint 來 extend prettier 的規范,最后結合 webpack 的 module 中 對於 js、vue、jsx 文件的 loader 處理,來實現 實時的 lint 。
5、項目架構中 應該具備的 配置文件
那么 回歸到最后,我們去架構這個項目的時候,從 前端編碼規范層面去考慮的話,我們的項目 最少需要的幾個 配置文件是這樣子的:
5.1 、webpack.config.js
webpack
是 構建根本,結合各類插件使用
5.2 、prettierrc
結合 vscode
終極格式化 我們的代碼,一鍵化操作帶你飛~
5.3 、eslintrc
eslint
結合 各類 eslint 的規則。 進行 強/弱 類型提示 (0、1、2)
5.4 、babelrc
{
"presets": ["es2015", "stage-1", "react"],
"plugins": ["transform-runtime", "transform-decorators-legacy"],
"env": {}
}
結合 babel
的各類 loader 進行 ES 的語法預編譯處理,這里由於時間關系就不仔細去闡述了。
六 、總結
一套好的規范,可以解決不想遇到的意外的bug、可以規范自己的編碼習慣、可以讓 code Review 更加簡單。
好處多多,有待不斷摸索,前期自然回遇到一些困難,但結果是值得期待的~
GitHub 地址:(歡迎 star 、歡迎推薦 : )