前言
首先讲个真实的段子,我在之前公司亲身遇到过的。
阳光明媚的上午,大家都在安静的敲代码,突然某脾气暴躁的全栈大佬大 J 大喊一声:这屎一样的代码是谁写的?老子找了半天的 bug 全是因为这个!大家面面相觑,因为不知道他说的是前端代码还是后端代码,并且不确定是不是自己写过的,毕竟谁能保证自己从没写过屎一样的代码呢?这时项目经理说:看 git 提交记录啊,找出来请大家喝奶茶。大 J 说:正在找了。过了好久,大 J 也没说是谁的代码,项目经理就问:找到了吗?大 J 缓缓地说:找到了,一年前我自己写的。。。
代码不规范,回首两行泪啊!
我相信大家都遇到过各种各样神奇的代码:
命名看心情,有时拼音,有时字母,甚至有时 1234。
明明 1 行代码能搞定的,由于基础差,写了 20 行,还觉得自己挺牛。
一个函数里面处理 n 件事情,动辄几十行,严重的甚至上百行。
从来不写注释,还埋怨别人从不写注释。(这个说的真的不是我自己)
……
像上面这些问题林林总总千奇百怪,列到明天也列不完。
所以今天,大冰块来把这些问题细分成了 3 大类和 15 小类的前端代码规范,希望对大家的开发过程有一些帮助。
命名篇
在所有的规范中,命名可以说是重中之重,一个好的命名习惯让代码看起来结构清晰明朗,数据查找快捷方便,不好的命名习惯让人摸不着头脑,找不到南北,不小心就陷进命名者的混乱泥潭中无法自拔。
命名最最基本的要求是:
命名单词准确可描述,避免不易理解的缩写,不知意义的字符等,不要冗余,尽可能短。命名单词准确可描述,避免不易理解的缩写,不知意义的字符等,不要冗余,尽可能短。命名单词准确可描述,避免不易理解的缩写,不知意义的字符等,不要冗余,尽可能短。
变量、常量、属性、函数命名
变量命名: 统一规范:小驼峰式命名法: 单词之间没有连接符,首字母小写,后续单词首字母大写。同时要注意变量的类型,例如 boolean 类型的值可以用 isXXX、hasXXX、canXXX 等命名;Array 类型的值可以用 xxxList、xxxArray 方式命名;Object 类型的值可以用 xxxObj 方式命名。如:
// bad
let a = [] // 语义不明确
let dc = [] // 不通用的缩写,不知道是什么
let bookData = [] // 书本数据,但是不知道什么类型
// good
let isBook = true // 清晰明了,这是是否为书的boolean类型
let bookList = [] // 清晰明了,这是一个书本的Array集合
let studentObj = {} // 清晰明了,这是一个学生的Object对象
复制代码
常量命名: 统一规范:大写字母+下划线符+大写字母 。如:BASE_URL
// bad
const baseUrl = "http://localhost" // 在其他地方调用时和常量没有区别,看不出是否能更改
// good
const BASE_URL = "http://localhost" // 很明显的知道这是一个常量
复制代码
属性命名: 统一规范:小驼峰式命名法: 单词之间没有连接符,首字母小写,后续单词首字母大写。如:
// bad
let studentObj={
studyName: "马冬梅"
}
/*
* studyName中study就是冗余的,name属于studentObj对象。所以不必写成studyName。
*
*/
// good
let studentObj={
name: "马冬梅"
}
复制代码
函数命名: 统一规范:小驼峰式命名法:单词之间没有连接符,首字母小写,后续单词首字母大写。如:
// 很明显能看出:这个函数的作用是获取学生列表
function getStudentList(){
...
}
// 很明显能看出:这个函数的作用是跳转页面
function jumpPage(){
...
}
复制代码
循环内当前项命名(避免依赖上下文命名)
统一规范:根据当前循环列表的对象命名。
// bad
studentList.forEach((item)=>{
...
// something
...
let list = item.books.map((e=>{
...
// something
...
}))
...
// something
...
})
/*
* item和e和studentList看起来毫无关系,当forEach内的逻辑复杂需要修改的时候,
* 在循环遍历时,很多人会使用item,value,e等代表遍历的当前项。
* 当forEach内的逻辑复杂导致上下文过长时,这样的命名可读性就会变得非常差。
* 你可能还需要翻到上面去看item和e表示什么,我们需要一个清晰的变量命名。
* 这样维护代码的时候我们可以迅速理解这个变量代表的含义,而不是维护代码的时候必须去翻看复杂逻辑的上下文。
*/
// good
studentList.forEach((student)=>{
...
// something
...
let list = student.bookList.map((book=>{
...
// something
...
}))
...
// something
...
})
复制代码
文件夹命名
项目总文件夹:小写字母+连字符+小写字母 。如:
vue-element-admin // 作为项目总文件夹,一眼就能知道是什么项目
复制代码
views 或 pages 内的文件夹:小写字母+连字符+小写字母 。如:
error-page // 作为页面的文件夹,命名要准确清晰,一眼就能知道是什么页面
复制代码
components 内的文件夹:大驼峰命名法: 单词之间没有连接符,首字母都要大写。如:
HeaderSearch // 作为存放组件的文件夹,大驼峰命名能让它与其它文件夹区分开来,更易理解
复制代码
css,scss,less 文件命名
统一规范:小写字母+连字符+小写字母 。如:
element-ui.scss // css文件同样要单词明了简介,通过名字能知道它的作用范围
element-ui.less
element-ui.css
复制代码
js 文件命名
统一规范:小写字母+连字符+小写字母 。如:
drag-dialog.js // js文件同样要单词明了简洁,通过名字能知道它的大致作用
复制代码
vue 文件命名
src 和 components 内的 vue 文件:大驼峰命名法: 单词之间没有连接符,首字母都要大写。如:
/**
*入口文件及组件文件,大驼峰命名能让它与其它文件夹区分开来,更易理解。
*同样组件类文件单词要明了,通过名字能知道它的大致作用
*/
SwitchRoles.vue
复制代码
其他文件夹内的 vue 文件:小写字母+连字符+小写字母 。如:
auth-redirect.vue // 命名要准确清晰,一眼就能知道组件含义
复制代码
img 文件命名
若是 icon 图标要体现在名字中。
统一规范:小写字母+下划线符+小写字母 。如:
icon_up_arrow.png // 一眼就能明白:icon图标,向上的箭头
bg_header.png // 一眼就能明白:这是一张头部的背景图片
复制代码
json 文件命名
统一规范:小写字母+下划线符+小写字母 。如:
china_city.json // 一眼就能明白:中国城市的json文件
复制代码
其它文件命名
统一规范:小写字母+下划线符+小写字母 。如:deploy-docker.sh
deploy-docker.sh // 单词简洁,语义明了
复制代码
函数篇
形参设置默认值
给形参设置默认值,不需要在函数内处理当前形参未传值的情况。避免了代码冗余,可读性更强。如:
// bad 代码冗余,增加阅读量
function getStudentList(currentPage, pageSize) {
currentPage = currentPage || 1
pageSize = pageSize || 10
...
// something
...
}
// good 赋默认值,一个字:绝!
function getStudentList(currentPage = 1, pageSize = 10) {
...
// something
...
}
复制代码
形参个数限制
如果一个函数有 5 个以上的形参,你会发现读起来很累。并且形参是有顺序的,这意味着调用函数的时候你需要把传入的参数和形参一一对应起来,否则会出现意想不到的错误。
所以我们最好把函数形参控制在 3 个之内,如果形参超过 3 个,可以直接将形参合并为一个对象,这样调用该函数时也能清楚的理解每一个形参的含义。如:
// bad 形参过多,代码冗余,增加阅读量
function getStudentList(currentPage = 1, pageSize = 10, search = "", startTime = "", endTime = "") {
currentPage = currentPage || 1
pageSize = pageSize || 10
...
// something
...
}
// good 合并形参,无需记住顺序,一个字:绝!
let searchData = {
currentPage: 1,
pageSize: 10,
search: "",
startTime: "",
endTime: ""
}
function getStudentList(searchData) {
...
// something
...
}
复制代码
函数功能限制
想感受被动辄数十行甚至上百行的一个函数支配的恐惧吗?它就静静的躺在那里:来呀,来深入了解我啊~ 除非你能完完全全理一遍,不然你就不知道它的作用~
一个函数应该只对应一个功能,按功能来创建函数才是正确的做法,这不仅易于理解和维护,同时也能让别人阅读你代码的时候心情舒畅而不是想骂你。如
// bad
function init() {
axios.get('/studentList').then((success)=>{
...
// something
...
})
...
// something
...
axios.get('/bookList').then((success)=>{
...
// something
...
})
}
// good
function getStudentList() {
axios.get('/studentList').then((success)=>{
...
// something
...
})
}
function getBookList() {
axios.get('/bookList').then((success)=>{
...
// something
...
})
}
function init(){
getStudentList()
getBookList()
}
复制代码
注释篇
我生平最讨厌两种人:不写注释的人和逼我写注释的人。不得不说,写注释是个好习惯,可是认真写注释的程序员越来越少见了。
那么关于注释,有什么需要注意的呢?
不必注释的情况
命名语义化的代码不必注释。如果你的命名很标准,不需要注释别人也能看懂。如:
let studentList = ["小日","小月","小明"] // 一眼就知道这是一个学生的列表,还需要什么注释呢?
function getStudentList(){} // 一眼就能看出这是获取学生列表的方法,还需要什么注释呢?
复制代码
单行注释
js 中使用 // 单行注释,一般在注释对象后面直接写,或者在注释对象上面单独一行使用。如果单独一行,最好在注释前插入空行,使得注释与上面一行保持距离,能看出注释针对的是下面代码。
// bad
let sb = "j100" // 这是啥???
function getUCData(){} // 这是啥???
// good
let sb = "j100" // sb是水杯的意思,我忘了水杯的英文,暂时用sb代替
// 这个函数用于获取UC的某些数据,不需要管它
function getUCData(){}
复制代码
多行注释
使用 /** ... */ 作为多行注释。包含描述、指定所有参数和返回值的类型和值。/* *@关键字 说明 */
如:
// bad 无注释,要完全看一遍才能理解当前函数作用
function addTips(text="无内容", time=1500) {
let oldTip = document.querySelector(".window-new-tips")
oldTip? document.body.removeChild(oldTip) : ''
let tip = document.createElement("p")
tip.classList.add("window-new-tips")
tip.innerText = text
tip.style.cssText = "z-index: 9; position:fixed; top:40%; left:50%; transform: translate(-50%,-50%); opacity: 0.6; background: #000000; border-radius: 0.06rem; font-size: 14px; color: #FFFFFF; text-align: center; padding: 0.1rem 0.35rem;"
document.body.appendChild(tip)
setTimeout(() => {
document.body.contains(tip)? document.body.removeChild(tip) : ''
}, time)
}
// good 写了注释一目了然,不需要完全看懂函数内容即可知道作用
/**
* @param text:展示的文本, time:展示的时长
* @return 无
* @example formatNumber("没有更多了",1500);
* @description 自定义提示信息,可自定义展示文本和展示时长
*/
function addTips(text="无内容", time=1500) {
let oldTip = document.querySelector(".window-new-tips")
oldTip? document.body.removeChild(oldTip) : ''
let tip = document.createElement("p")
tip.classList.add("window-new-tips")
tip.innerText = text
tip.style.cssText = "z-index: 9; position:fixed; top:40%; left:50%; transform: translate(-50%,-50%); opacity: 0.6; background: #000000; border-radius: 0.06rem; font-size: 14px; color: #FFFFFF; text-align: center; padding: 0.1rem 0.35rem;"
document.body.appendChild(tip)
setTimeout(() => {
document.body.contains(tip)? document.body.removeChild(tip) : ''
}, time)
}
复制代码
后记
前端开发规范注定是绕不开的坎,记得之前有同事总是喜欢格式化代码,凡是他更改过的文件,哪怕只有 1 行,整个文件也会被他格式化。回头再看 git 提交记录,好家伙,历史的 commit 全被覆盖了~ 当然,git 提交也是前端开发规范的一部分,今天列举的一些规范并不十分全面,只是大冰块细心整理的一部分。无谓对错,毕竟规范没有对错之分,只要团队大家都遵守就是有益的。
本文旨在提供一条前端项目及代码优化的思路,从而减少代码维护时的时间和人力成本。如果对你有帮助,点个赞就好,如果有错误欢迎指出交流。感谢阅读~
评论