JavaScript前端代码规范

本文提供了一套详尽的前端开发规范,涵盖了HTML、CSS、JavaScript及Vue等方面的最佳实践,旨在提高代码质量、可读性和可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HTML篇

#1. 标准模板

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <link rel="icon" href="/favicon.ico">
  <link rel="stylesheet" href="">
  <title></title>
</head>

<body>
    <div class="app"></div>
    <script src=""></script>
</body>
</html>

#2. script和style标签顺序

推荐将<style>对应的标签放到html对应的<head>标签中;<script>对应的标签放到<body>标签之后(某些涉及页面效果需要预先动态加载的js脚本应放在<body>标签之前)


#CSS篇

#1. 命名

ClassName的命名应该尽量精短、明确,必须以字母开头命名,且全部字母为小写,单词之间统一使用横线 “-” 连接。

#2. css类名语义化

根据模块的功能来命名,而不是根据样式来命名。如:给红色的提示文字起类名。类名可以叫 tip,不应该叫 color-red。如果类名是color-red,那以后提示文字要改成绿色,类名也要改,用功能来命名不会出现这种问题。

下面是常见类名:

  • 布局: header(头部), footer(尾部),main(主栏),side(侧栏), wrap(盒容器), item(条目)
  • 模块: nav(导航), subnav(子导航), breadcrumb(面包屑), menu(菜单), tab(选项卡), title(标题), list(列表), content(内容), table(表格), form(表单), hot(热点), top(排行), login(登录), log(日志), search(搜索), slide(幻灯), tip(提示), help(帮助), news(新闻), download(下载), regist(注册), vote(投票), copyright(版权), result(结果), button(按钮), input(输入), avatar(用户头像), badge(徽章), tag(标签)。
  • 状态: collapsed(收拢的), expanded(展开的), current(当前), selected(选中的), active(活跃的), enabled(可用), disabled(不可用)。

#3. 选择器

  • 尽量少用通用选择器 *
  • 不使用 ID 选择器
  • 不使用无具体语义定义的标签选择器

#4. 简洁性

使用属性缩写。不必要的值不用写。 如0 取代 0px, padding 取代 padding-top/right/bottom/left同时定义等。

#5. 动画

除了变形和改变透明度用animation,其他尽量使用transition。


#javascript篇

#1. 变量命名

  • 标准变量采用驼峰标识
  • 使用的ID的地方一定全大写, 如userID
  • 使用的URL的地方一定全大写, 比如说 reportURL
  • 涉及Android的,一律大写第一个字母
  • 涉及iOS的,一律小写第一个,大写后两个字母
  • 常量采用大写字母,下划线连接的方式,如CITY_CONFIG
  • 构造函数,大写第一个字母

#2. 使用const和let代替var

const 和 let 都是块级作用域,var 是函数级作用域。对于只读的常量(指向的内存地址不变)使用const,变量声明使用let(复合类型的数据,如果不重新赋值,只更改内容,使用const声明)

#3.变量声明

变量必须 即用即声明,不得在函数或其它形式的代码块起始位置统一声明所有变量。

#4. 对象或数组创建

使用字面量值创建对象, 如{}代替new Object{}, []代替new Array()

#5. 避免使用保留字作为对象的键值

如default、continue等等,在 IE8 下不会运行

#6. 对象多个属性赋值优先使用Object.assign

// bad
const obj = {a: 1}
obj.a = 2
obj.b = 3
obj.c = 4

// good
const obj = {a: 1}
Object.assign(obj, {
    a: 2,
    b: 3,
    c: 4
})

#7. 请使用对象属性值的简写方式

const job = 'FrontEnd'

// bad
const item = {
  job: job
}

// good
const item = {
  job
}

#8. 将简写的对象属性分组后统一放到对象声明的开头

const job = 'tester'
const department = 'abc'

// bad
const item = {
  sex: 1,
  job,
  age: 30,
  department
}

// good
const item = {
  job,
  department,
  sex: 1,
  age: 30
}

#9. 不要直接使用 Object.prototype 的方法, 例如 hasOwnProperty, propertyIsEnumerable 和 isPrototypeOf

// bad
console.log(object.hasOwnProperty(key))

// good
console.log(Object.prototype.hasOwnProperty.call(object, key))

#10. 灵活使用展开运算符...

// bad
const original = { a: 1, b: 2 }
const copy = Object.assign({}, original, { c: 3 })
delete copy.a

// 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 }

#11. 数组方法return

使用数组的 map/reduce/filter/find 等方法时,请使用 return 声明,如果是单一声明语句的情况,可省略 return

#12. 解构赋值

需要使用对象的多个属性或数组的多个值时,请使用解构赋值

const obj = {a: 1, b: 2, c: 3}
const arr = [1, 2, 3, 4]

// bad
const a = obj.a
const b = obj.b
const first = arr[0]
const second = arr[1]

// good
const {a, b} = obj
const [first, second] = arr
// 对象解构+赋值
let people = { name: null, age: null };
let result = { name: '张三', age: 16 };
{ name: people.name, age: people.age } = result;
console.log(people)

// 基础数据类型结构
const { length: a } = '1234';
console.log(a) // 4

// 对数组结构快速拿到最后一项
const arr = [1, 2, 3];
const { 0: first, length, [length - 1]: last } = arr;
first; // 1
last; // 3
length; // 3

#13. 函数回传多个值或者方法多个可选入参时优先使用对象的结构

// bad
function doSomething () {
  return [top, right, bottom, left]
}
// 如果是数组解构,那么在调用时就需要考虑数据的顺序
const [top, xx, xxx, left] = doSomething()

// good
function doSomething () {
  return { top, right, bottom, left }
}
// 此时不需要考虑数据的顺序
const { top, left } = doSomething()


// bad
function doSomething (a, b, c, d) {
}
// 有默认参数也必须按顺序传入
doSomething(true, 1, 2, 3)

// good
function doSomething ({a = true, b, c = 1, d}) {
}
// 此时使用默认的参数就可以不传
doSomething({b: 1, d: 3})

#14. 模板字符串

变量与常量拼接尽量使用模板字符串,如`my name is ${name}`

#15. 不要给参数重新赋值

// bad
function f1 (a) {
  a = 1
}
function f2 (a) {
  if (!a) { a = 1 }
}

// good
function f3 (a) {
  const b = a || 1
}
function f4 (a = 1) {
}

#16. 比较运算符

使用 === 和 !== 而非 == 和 !=

说明:使用 === 可以避免等于判断中隐式的类型转换

#17. debugger

调试完成后请清理debugger和console.log

#18. 布尔转换

避免不必要的布尔转换,

在if条件等情况中会自动转为布尔值,无需!!,而赋值仍需!!来强制转为布尔值

// bad
const status = 1
if (!!status) {
}
const isDelete = status > 0 ? true : false

// good
const status = 1
if (status) {
}
const isDelete = !!status // or const isDelete = Boolean(status)

#19. 三元表达式

  • 二选一条件下 三元表达式与if…else是可以互换的
  • 根据条件二选一给变量赋值的情况下,可使用三元表达式,其他复杂的逻辑处理不推荐
  • 如果有更好的实现,尽量不要使用三元表达式
// bad
let score = val ? val : 0

// good
let score = val || 0

#20. 循环

  • 尽量避免嵌套循环 
    说明:额外增加很多次循环,影响效率,且堆叠代码影响可读性
  • 尽量少用for-in和for-of
  • 数组的遍历,虽然fori的性能优于其他,但很多场景下使用map、find、filter、reduce、every、some等更合适,语义清晰且代码简洁
const students= [
    {key: 'p1', name: '张三', score: 99},
    {key: 'p2', name: '李四', score: 98},
]
const nodes = [
    {key: 'p1', checked: true},
    {key: 'p2', checked: false},
]

// bad
for (let i = 0; i < nodes.length; i ++) {
    const node = nodes[i]
    for (let j = 0; j < students.length; j ++) {
        const student = students[j]
        if (student.key === node.key) {
            // TODO
            break;
        }
    }
}

// good
const studentKeyMap = {}
students.forEach(student => {
    studentKeyMap[student.key] = student
})
nodes.forEach(node => {
    if (studentKeyMap[node.key]) {
        // TODO
    }
})

const objMap = {
    a: [{a1:0, a2: 0}],
    b: [{b1:0, b2: 0}]
}
for(const [key, value] of Object.entries(objMap)) {
    console.log(key)
    console.log(value.a1)
}

#21. 类型检测

  • 基本数据类型判断使用 typeof
  • 引用类型检测可使用 instanceof
  • null 或 undefined 的检测使用 == null
  • Object.prototype.toString.call() 最为通用

注意

  • typeof null === 'object'
  • 检测有效数字: !isNaN(value) && typeof value === 'number'
  • 数组检测推荐用Array.isArray()

#VUE篇

#1. 组件命名

应该始终是多个单词的,可以避免跟现有的以及未来的 HTML 元素相冲突

#2. Prop 定义

Prop 定义应该尽量详细, 至少需要指定其类型

#3. 为 v-for 设置键值

必须用 key 配合 v-for,以便维护内部组件及其子树的状态

#4. 避免 v-if 和 v-for 用在一起

永远不要把 v-if 和 v-for 同时用在同一个元素上; 一般我们在两种常见的情况下会倾向于这样做:

  • 为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。
  • 为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ul、ol,如果没有,可以增加template标签作为父元素)。

#5. 为组件样式设置作用域

对于应用来说,顶级 App 组件和布局组件中的样式可以是全局的,但是其它所有组件都应该是有作用域的。 目前基本都使用scoped attribute,还可以通过 CSS Modules,另外尽量使用唯一的 class 名可以帮你确保那些三方库的 CSS 不会运用在你自己的 HTML 上,如添加组件专属前缀

#6. 私有 property 名

使用模块作用域保持不允许外部访问的函数的私有性。如果无法做到这一点,就始终为插件、混入等不考虑作为对外公共 API 的自定义私有 property 使用$_前缀。并附带一个命名空间以回避和其它作者的冲突 (比如$yourPluginName)。

#7. 文件命名

  • index.js 或者 index.vue不变
  • 属于组件或类的,统一使用大写字母开头的(PascalCase)命名规范
  • 其他非组件或类的,统一使用小写字母开头的(kebab-case)命名规范

具体来说:

  • components文件夹下的子文件夹,使用PascalCase,其他文件夹使用kebab-case
  • *.js,属于类的除index.js外使用PascalCase, 其他使用kebab-case
  • *.vue,除index.vue外统一使用PascalCase
  • *.less/sass,统一使用kebab-case

#8. 模板中简单的表达式

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法,且复杂计算属性可以分割为尽可能多的更简单的 property

#9. 指令缩写

推荐指令缩写 (用 : 表示 v-bind:、用 @ 表示 v-on: 和用 # 表示 v-slot:) 应该要么都用要么都不用。

#10. 全局状态管理

优先通过Vuex管理全局状态,而不是通过 this.$root 或一个全局事件总线,甚至挂载到全局变量window上。

#11. 生命周期

  • 尽量避免在mounted、created、watch等内写逻辑,逻辑代码尽量放到methods下
  • 推荐created获取后台数据,mounted基于dom初始化实例(如地图初始化),beforeDestroy清除绑定事件、定时器、清理vuex状态等

#12. 避免不必要的状态

避免不必要的状态,使用局部变量代替


#代码设计篇

#1. 单一职责

一个函数只做一件事。单一职责的优点:

  • 降低代码的复杂度
  • 有利于代码复用

#2. 高内聚低耦合

模块内的代码是紧密联系的,而模块间的依赖尽可能低。优点是:

  • 好维护
  • 方便配置和嵌入

#3. 减少重复代码

同样的代码出现了三次,就应该考虑去消除这些重复代码。

#4. 约定优于配置

通过命名规则之类的约束来减少程序中的配置。 写组件时,对属性的设计,可以用约定优于配置的做法。

#5. 健壮性

软件对要求以外输入情况的处理能力。 健壮的代码,就是考虑的全面。具体来说:

  • 异常代码,是否捕获;
  • 写 switch 语句时,是否有 default 分支;
  • 当接口报错时,代码是否能正确处理;
  • 用户填表单的非法输入,是否会让程序奔溃;
  • 当用户输错网址,是否会跳404页面;
  • 事件的绑定、定时器的创建、全局状态的赋值有对应的清理等等。

#6. 可读性

  • 代码格式规范,变量方法名语义清晰
  • 必要的注释,注释要简单清晰语义明确,并及时更新
  • 保持代码简洁,在保证性能的前提下,实现同样的功能用尽量简洁的代码
  • 不要用看似聪明的伎俩混淆代码的意图,如~~3.14 vs Math.floor(3.14), a || b() vs if(!a) b()

#7. 数据格式

数据格式的选择结合实际场景,增加可扩展性

  • 展示多项时优先使用数组而非对象
  • 多个变量为一个整体赋值和解析时优先使用对象而非多个单独变量
// bad
const statisticStudents = {'男': 10, '女': 20}
// good
const statisticStudents = [
    {label: '男', total: 10},
    {label: '女', total: 20}
]

// bad
this.checked = true
this.checkedId = 1
// good
this.checkedItem = {
    checked: true,
    checkedId: 1,
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木易66丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值