1. 程式人生 > >為什麼我的程式碼讓別人看起頭髮麻?

為什麼我的程式碼讓別人看起頭髮麻?

面試官:談談你們專案當中的前端程式碼規範吧?

自己先想一分鐘。

為什麼我的程式碼讓別人看起頭髮麻?
前面的話

有些同學在開發某個新功能時根據需求就哐哐哐(按照自己的程式碼風格)一頓擼。寫完發現,另一個地方也有這個模組功能,可能只是標題的顏色,字型大小不對。怎麼辦? 於是很雞賊的複製貼上過去,改吧改吧,提交程式碼,萬事大吉!自己倒是爽了,功能是按照需求如期完成了啊,沒毛病。可是你卻忽視了一件很重要的東西: 團隊 。

記住,你不是一個人在寫程式碼。

這篇文章有別於其他教程類的文章,不是教你如何制定程式碼規範,也不是告訴你這樣寫就是錯的亦或說是正確的。本文是我這些天從優化別人程式碼過程中的所見和所得,凝結成文。旨在分享給大家,對號入座,然後改之。 三人行,必有我師!;擇其善者而從之,其不善者而改之。 由於我是做前端的,所以只說前端程式碼規範,其他語言同樣適用!

目的

把一些常見的錯誤的不良的程式碼示例分享給大家,希望有的改之,無則加勉。看完之後,希望對你們有所幫助,提高自己的程式碼質量,每個人都能寫出一手漂亮的程式碼。這是這篇文章最大的目的了!

概述

本文將以我的親身專案經歷為例,來談談我們日常開發當中,就程式碼層面來講,我們應該注意的一些小細節。希望各位看客能吸取精華去其糟粕。主要涉及的方面有:

專案結構
檔案命名
路由
Vue 元件
JavaScript
Html
Css
Git 程式碼提交
我將會從以上幾個方面逐一列舉和大家分享討論。

列舉

  1. 專案結構

沒說之前,您不妨看下自己的專案結構是什麼樣的。目前我們的專案結構是這樣的:

my-project
├── .idea # 這個是編輯器生成的
├── build # Webpack 配置檔案放在這裡
├── config # Vue 基本配置檔案放在這裡
├── node_modules # 第三方依賴
├── src # 專案原始碼(核心檔案)
│ ├── assets # 資原始檔(js, css, scss)
│ ├── components # 所有元件
│ ├── js # 自己寫的 js,裡面各種工具類方法等
│ ├── mixins # 混合
│ ├── router # 路由
│ ├── vuex # 狀態管理
│ ├── App.vue # 根元件
│ └── main.js # 入口檔案
├── static # 靜態資源,一般放 img
├── theme # 主題檔案,修改的 Element-UI 主題
├── .babelrc # babel 編譯配置
├── .editorconfig # 程式碼格式
├── .gitignore # Git 提交忽略的檔案配置
├── .postcssrc.js # 轉換 css 的工具配置檔案
├── element-variables.css # Element 全域性定義的變數,不明白為啥放這兒
├── index.html # 主頁模板
├── package-lock.json # 用來鎖定依賴的版本號(NPM 自動生成)
├── package.json # 專案基本資訊
└── README.md # 專案介紹

複製程式碼
都是用vue-cli 生成的,目錄結構和命名規範也就沒啥可說的。可能隨著時間的推移,自己會在專案里加一些東西(檔案或資料夾)。拿上面我們的專案為例來說幾點吧:

根目錄下不要有 css 檔案 :比如 element-variables.css 檔案,它屬於 theme 資料夾下的東西,應該放它下面。
js 資料夾應該命名為 utils :因為它對外暴露的都是工具類方法,這樣更顯語義化。
關於專案結構,我發現的就這麼多。每個專案的目錄可能會不同,這個就看你們的規範了。

  1. 檔案的命名

它包含檔案的命名和資料夾的命名。依我們的專案為例,我重點說下 src/components 目錄下的命名,真的是五花八門:

2.1. 檔名不夠語義化

為什麼我的程式碼讓別人看起頭髮麻?
這個還算正常,但還是有些問題。這裡是一些問題清單:

這個模組的中文叫,是關於機器人學習的,叫 knowledgeBaseManagement 雖然很好的翻譯了中文意思,但總覺的有點長,叫 robot 會不會好些?
而且資料夾下的檔案命名也不夠語義化
下面是整理過後的樣子:

robot
├── addQuestion.vue
├── editQuestion.vue
├── index.vue
└── missedQuestion.vue
複製程式碼
為什麼我的程式碼讓別人看起頭髮麻?
這個我就不想說了,看的我頭皮發麻。從字面意思上來講,我就認識一個 TreeNode2.vue 。後面還加個 2 是什麼鬼?

2.2. 檔案目錄不統一

為什麼我的程式碼讓別人看起頭髮麻?
為什麼我的程式碼讓別人看起頭髮麻?
為什麼我的程式碼讓別人看起頭髮麻?
這屬於一類問題,即裡面太亂了,不統一,問題清單:

src/components/moduleName/ 下除了子模組外,儘量不要瞎放其他無關的資料夾,如上面的 src 、 component/common 、 top
callcenterList/src 下的圖片可以放到 static 下
如果是功能型元件(上面的 color 是一個顏色選擇器元件),儘量放到一個叫 package 或者 lib 的資料夾下。因為 src/components 下的模組都是系統模組,不要混淆
elvesSetting/top 如果是某幾個頁面頭部的公共部分儘量放到 components/common 下
2.3. 檔名過長

為什麼我的程式碼讓別人看起頭髮麻?
如果一個模組下就一個檔案,儘量寫成 index.vue 。這裡資料夾和檔案同名,路由是不是很長?你在其他檔案中 import 的時候是不是也不方便? 而且我發現 problemManagement 和 problemRetrieve 都屬於問題管理模組,完全可以合併到一個資料夾裡啊。還有,資料夾已經表明是問題管理模組了,所以檔案就不要再以 problem*** 開頭了。不覺得囉嗦嗎? 下面是整理過後的樣子:

problemManagement
│ ├── index.vue
│ ├── retrieve.vue
qualityCheckAppeal
└── index.vue

  1. 路由

我們系統裡的路由都是一級路由。舉個栗子:

userManagement
├── add.vue
└── update.vue

使用者管理下有增改兩個功能,不使用彈框去做的前提下,假如說 add 和 update 對應兩個路由是 /addUser , /updateUser 。我們系統位址列是這樣顯示的:

// 增加使用者
localhost:3030/addUser
// 修改使用者
localhost:3030/updateUser?id=1

雖然位址列路有短看起來雖然讓人舒服,但是模組多的話,就不容易區分,其實應該這樣做:

// 增加使用者
localhost:3030/user/add
// 修改使用者
localhost:3030/user/update?id=1

// 總結
localhost:3030/module/function?queryString

  1. Vue 元件

關於 Vue 元件開發規範可以參考官方的風格指南。下面是我們專案的一些問題清單和改正意見,我列舉一下作為對照:

不要在 App.vue 中直接修改第三方樣式(比如: ElementUI )。請使用外部檔案匯入:
App.vue 檔案:

<!-- incorrect -->
...
<style>
 .el-input__icon {
 cursor: pointer
 }
</style>
<!-- correct -->
...
<style>
 @import 'element-style-overwrite';
 ...
</style>

_element-style-overwrite.scss 外部樣式檔案:

.el-input__icon {
 cursor: pointer
}

給每個元件起個名字是個好習慣。例如 Dialog 元件:

// incorrect
export default {
 ...
}
// correct
export default {
 name: 'MyDialog', // 以大駝峰命名
 ...
}

給元件樣式設定作用域 scoped
如果你在某個子元件中修改了全域性樣式,本來只想在該元件中使用,沒想到造成了全域性汙染。等進行程式碼 review 的時候是很難排查的。

例如,使用者管理( UserManagement.vue )元件:

<style scoped>
...
</style>

元件名要麼單詞大寫開頭 (PascalCase),要麼橫線連線(kebab-case):

// incorrect
components/
└── mycomponent.vue
components/
└── myComponent.vue
// correct
components/
└── MyComponent.vue
// 或者
components/
└── my-component.vue

.vue 單檔案中的 、

也有人喜歡這樣寫:

<script>...</script>
<style>...</style>
<template>...</template>

如果你想寫,那好,不阻攔,拜託你統一下行不?別這個元件這個順序,那個元件那個順序。累不累? 這裡我強力推薦大家按照官方的寫法,即下面的順序來寫:

<template>...</template>
<script>...</script>
<style scoped>...</style>

使用兩個空格(space)進行縮排
這個放在全域性規範會比較好一些。為什麼是兩個空格? 大神們都是這樣做的!而且更重要的是,使用兩個空格開發專案,傳到 github 或者 gitlab 上排版會很好看。什麼?不會設定?百度啊!你用的什麼編輯器就查這個編輯器怎麼設定的。

一般是統一把全域性規範設定放到一個叫.editorconfig 的資料夾裡,有的編輯器支援這個檔案,比如: webstorm 。有的則不支援,對於不支援的編輯器,可以下載安裝 editorConfig 外掛,如: atom 、 sublime 、 vscode 等。

程式碼中不用的註釋都刪掉
除錯結束,把不用的 console.log(…) 及時刪掉,它會影響效能
data 中的屬性命名和初始化問題

// incorrect
export default {
 data () {
 return {
 text: 'wwwwwwww', // 這是啥?
 editBoxId: null, // 很明顯Id是String,這裡他初始化一個 null
 flag: '', // 這個表示的啥?看意思應該是個 Boolean 型別,為啥弄個 String ?
 pSize: 10, // pSize 是啥?
 cPage: 1, // cPage 是啥?
 popCsr:true, // popCsr 是啥,恐怕現在連那個開發者自己都不知道了吧
 callcenterAuthority: false, // 這麼長你告訴是一個 Boolean 型別的
 }
 }
}
// correct
export default {
 data () {
 return {
 text: '', // 'wwwwwwww' 沒卵用刪掉
 editBoxId: -1, // 它應該是個 Number 型別
 flag: false, // 它應該是個 Boolean 型別啊
 pageSize: 10, // pSize -> pageSize 多好
 currentPage: 1, // 完整寫法更易懂,不是嗎?
 isPopcsr: true, // Boolean 型別的總是前面加個 is
 isAuthority: false, // 是否授權。
 }
 }
}

其實還有好多問題,我就不一一列舉了。諸如此類的問題,希望各位看客們都能吸取精華,去其糟粕。

Props 中的屬性宣告要明確型別

// incorrect
export default {
 props: ['node', 'size']
}
// correct
export default {
 props: {
 node: Object, // 物件
 size: [String, Number], // 兩種型別都可以
 }
}

Vue 生命週期函式按順序放在 methods 之前
為什麼說這個呢? 我們專案中有的元件就 methods 中的程式碼就上千行。如果生命週期函式放在 methods 之後,拉來拉去非常不方便:

// incorrect
export default {
 ...
 created () {},
 methods: {
 // 省略 1000 行程式碼
 // ...
 },
 mounted () {},
 beforeDestroy () {},
 destroy () {},
}
// correct
export default {
 ...
 created () {},
 mounted () {},
 beforeDestroy () {},
 destroy () {},
 methods: {
 // 省略 1000 行程式碼
 // ...
 }
}

Vue 元件中的 this 賦值要統一
程式碼中,有時候我們需要把 this 賦給一個變數,你要麼統一賦值給變數 vm ,要麼統一賦值給變數 self 。別一個元件裡,變來變去。

// incorrect
export default {
 ...
 methods: {
 one () {
 let vm = this
 },
 two () {
 let self = this
 }
 }
}
// incorrect
export default {
 ...
 methods: {
 one () {
 let vm = this
 // 或者
 let self = this
 },
 two () {
 let vm = this
 // 或者
 let self = this
 }
 }
}

Vue 元件中 Html 如果過長,請換行

<el-input v-model=“ruleForm.maskInput” size=“small” class=“nodeIpt” :icon=“ruleForm.maskInput ? ‘circle-close’:’’” @click=“ruleForm.maskInput = ‘’” @keyup.enter.native=“nodesure($event,‘ruleForm’)”>

<el-input
v-model=“ruleForm.maskInput”
size=“small”
class=“nodeIpt”
:icon=“ruleForm.maskInput ? ‘circle-close’:’’”
@click=“ruleForm.maskInput = ‘’”
@keyup.enter.native=“nodesure($event,‘ruleForm’)”>

Vue 中監聽的事件記得垃圾回收
舉個例子,如果我們在 Vue 元件的 created 宣告週期鉤子中監聽了一個點選事件,那麼,當元件銷燬(beforeDestroy)之前記得把這個事件釋放,看程式碼:

export default {
 ...
 created () {
 document.addEventListener('click', this.handleClick)
 },
 beforeDestroy () {
 document.removeEventListener('click', this.handleClick)
 }
}

Vue 元件中不要直接操作非同步請求(axios)
把所有的非同步請求方法封裝成一個獨立 js 檔案,或者放到 Vuex 中,千萬不要耦合到 Vue 元件中。因為程式碼量太多,會加重元件的後期維護,各司其職不好嗎?

不好的範例:

// User.vue
export default {
 ...
 mounted () {
 this.getUsers()
 },
 methods: {
 getUsers () {
 this.axios(url, data, (response) => {
 // Do something
 }).catch(err => {
 console.error(err)
 })
 }
 }
}

如果專案比較小還好,我沒意見,如果專案較複雜,千萬別這麼幹。下面是推薦的做法:

// server.js
// 專門處理資料請求的檔案,也就是我沒常說的MVC中的 M 層
import axios from ‘axios’
export default {
/**

  • 獲取使用者列表

    */
    getUsers (url, data) {
    return axios.get(url, data)
    }
    }
    // User.vue
    import api from ‘@/api/server.js’
    export default {

    data () {
    return {
    users: null
    }
    },
    mounted () {
    api.getUsers((response) => {
    this.users = response.data.data
    }).catch(err => {
    console.log(err)
    })
    }
    }

  1. JavaScript

下面所有的錯誤程式碼示例都是從我們的專案中發現的,撿主要的列出來一些。希望犯同樣錯誤的你能及時改正哦~

變數命名
要語義化命名

// incorrect
var a = document.getElementById(this.lastid) // 這裡的 a
var aa = true // 這是啥你們知道嗎?
// corrent
let orderId = this.order.id
let currentTime = Date.now()

多個單詞要駝峰命名

// incorrent
vm.timedefault = timedvalue
vm.currentsessionid = id
// corrent
vm.timeDefault = timedValue
vm.currentSessionId = id

變數要加註釋
為什麼我的程式碼讓別人看起頭髮麻?
上面那一坨你們知道啥意思嗎?如果這個開發人員離職了,那可是坑了後來人了。所以,做開發不能自己爽了,做一個帥氣和程式碼於一身的工程師,難道不更好嗎?

不要重複使用 var 宣告變數

// incorrect
var name = 'test';
var age = 12;
var hobby = 'sport';
// correct
var name = 'test',
 age = 12,
 hobby = 'sport';

= 或 == 之間要保留一個空格
錯誤的範例:

// 變數
var name='test'
var arr=[]
var obj={
 id:1
}
// if 判斷
if(this.id==currentId){
 // Do something
}
// for 迴圈
for(let i=0;i<arr.length;i++){
 // Do something

上面三種情況是最常見的,其他雷同。下面是正確的範例:

// 變數
var name = 'test'
var arr = []
var obj = {
 id: 1
}
// if 判斷
if(this.id == currentId) {
 // Do something
}
// for 迴圈
for(let i = 0; i < arr.length; i++) {
 // Do something
}

右括號 ) 遇到 左大括號 { 時要空一格
下面是錯誤的範例:

// if
if(a === b){
 // Do something
}
// for
for(let i = 0; i < arr.length; i++){...}
// 函式
var T = function(params){
 ...
}

常見的幾種情況,其他情況不再列舉。下面是正確的範例:

// if
if (a === b) {
 // Do something
}
// for
for (let i = 0; i < arr.length; i++) {...}
// 函式
var T = function(params) {
 ...
}

一定要進行非空判斷處理
在我們專案裡,有人這樣寫:

// 假如 Vue 元件中有一個叫 userId 的 data 屬性
if (userId != '' || userId != null || userId != undefined) {
 // ...
}

真不知道怎麼想的,簡單一句話不就搞定了嗎?

if (userId) {
 // ...
}

物件宣告不當問題
不要用下面的方式之一去宣告一個物件:

// incorrect
var arr = new Array() // 陣列
var arr = '' // 雖然 js 是弱型別,也不能這樣宣告
var obj = new object() // 物件
var obj = ''

異常處理問題
我們在處理非同步請求的時候,一定要對 response 中的資料進行異常處理,不然控制檯回報 response.data is not undefined ,我們專案我看了下,有些地方沒做處理,結果在做測試的時候,瀏覽器控制檯一頓報錯。那叫一個難看啊!

// incorrect
this.axios(url, data, (response) => {
 let result = response.data.data
})
// correct
this.axios(url, data, (response) => {
 if (response.data && response.data.code === 1) {
 let result = response.data.data
 }
}).catch(err => {
 console.error(err)
})

如果這個取值過長且多次用到,請賦給一個變數

export default {
 ...
 methods: {
 handleClick (evt) {
 // incorrect
 evt.target.parentNode.innerHTML = 'test'
 evt.target.style.width = '100px'
 evt.target.style.height = '200px'
 
 // correct
 let target = evt.target
 target.parentNode.innerHTML = 'test'
 target.style.width = '100px'
 target.style.height = '200px'
 }
 }
}
6. HTML

正確的使用標籤
專案中我見有人寫個按鈕居然用 span 標籤,或者一個 div 。

下面是錯誤的範例:

// 用 div 當按鈕

搜尋 // 在 span 裡 巢狀 el-input 元件 // 這樣做的同學,肯定不知道 el-input 編譯後的程式碼是啥樣的! // 用 label 當標題 // label 標籤是配合表單使用的 // 加粗字型沒有用原生標籤 我是加粗字型

下面是改正後的範例:

// 用 H5 的 button
搜尋
// 如果要包含 el-input 元件請使用塊級元素,並加上合適的 class

// h1-h6 才是標題的正確開啟方式

標題

// 加粗字型請使用原生標籤 // 然後使用 class 控制字型樣式 我是加粗字型

所有的按鈕,超連結,滑鼠的 :hover 狀態都應該是手形。
a, button {
cursor: pointer
}

id 和 class 或者其他的屬性,命名要語義化
不要命個名只有你自己知道。這樣會帶來後期維護困難。

<!-- incorrect -->
<div class="dfdf">
 <el-form class="loginForm">...</el-form>
</div>
<!-- correct -->
<div class="login-form__wrapper">
 <el-form class="loginForm">...</el-form>
</div>

把程式碼縮排改成 2 個空格
Html 中的屬性之間保留一個空格距離

每個程式碼快儘量加上註釋
程式碼量少尚且不說,如果一個 .vue 檔案很長的話,找起來就很痛苦了。你還別說,我們專案裡就是這樣沒註釋。

<template>
 <div class="user-managerment__wrapper">
 <!-- Header -->
 <div class="header">...</div>
 
 <!-- User table -->
 <div class="user-table__wrapper">
 <el-table>...</el-table>
 </div>
 
 <!-- Add user dialog -->
 <div class="add-user__dialog">
 <el-dialog title="新增使用者">...</el-dialog>
 </div>
 </div>
</template>

7. CSS

{ 和選擇器保持一個空格距離
.selector {
 ...
}

給每個樣式模組加上註釋有助於區分

// Global style
html, body, a, div {
 margin: 0
}
// Login style
.login button {
 ...
}
// User manager style
.user-manager__wrapper {
 ...
}

每個獨立樣式間保留一行距離
見上面的示例

選擇器不要巢狀太多層級
巢狀太多層級會影響效能,儘量保證在三層以下:

// incorrect
.user-management .user-box .user-form .el-form-item .remark {
color: #42b983
}
// correct
.user-management .user-form .remark {
color: #42b983
}

  1. Git 程式碼提交

大家可以看我沸點。同事寫的註釋。希望有問題的同學可以及時改正哦。另外,關於 Git 如何正確的寫好註釋 。這裡有篇文章講的很好,大家可以看看。傳送門 ->

下面舉個例子,比如我這次在使用者管理模組中修改了兩個 bug。如何以清單的方式提交呢? 看程式碼:

add file

git add src/components/userManager/index.vue

commit

git commit -m 'fix: 使用者管理模組bug修改。
清單:

  • 修改了列表分頁的bug
  • 修改了當使用者點選編輯按鈕彈框無法顯示的bug

push code

git push

你千萬別用下面的方式之一去提交你的程式碼說明:

說一些毫無意義的內容

git commit -m “fix: ok!”

or 不加 fix、feat、refactor、doc、style等字首

為什麼要加這些字首呢?問得好!

是方便日後檢索,當我們以這些字首去搜索修改日誌的時候

是很容易的哦,微笑。

git commit -m “修改使用者模組bug”

總結

最後,給大家找了幾個大廠的團隊規範文件,希望你看完能夠受益多多:

Vue風格指南
騰訊團隊文件
Bootstrap編碼規範
isobar前端程式碼規範 及 最佳實踐
書寫具備一致風格、通俗易懂 JavaScript 的原則
eslint-rules
以上就是我在專案當中發現的一些問題,記錄下來,暫時就記錄這麼多吧。希望正好看d到這篇文章的你如果也正好有以上的不良問題,請加油改正哦。當你的程式碼質量又提升一個檔次的時候,我相信,你離前端大神之門又邁進了一步!