前端开发规范
规范目的:为了提高工作效率,便于前端后期优化维护,输出高质量的文档,在网站建设中,使结构更加清晰,代码简明有序,有一个更好的前端架构。
规范基本原则:符合web标准,使用具有语义的标签,使结构、表现、行为分离,兼容性优良。页面性能优化,代码简洁、明了、有序,尽可能的减少服务器的负载,保证最快的解析速度。
# 一. 命名规范
# 1.书写方式:
# (1)连词线(-)分隔
- 多个单词,使用中划线分隔
content-box
# (2)连词线(_)分隔
- 多个单词,使用下划线分隔
content_title
# (3)连接点(.)分隔
- 使用连接点,一般用于接口文件名、配置文件名
├── knowledge.service.js
# (4)驼峰命名法
- 大驼峰:首字母大写,如
└── ContentTitle.vue
- 小驼峰:首字母小写,如
var contentTitle
# 2.具体规范:
# (1)文件夹名
- 文件夹名称一般小写,如包含多个单词时,单词之间使用半角的连词线 ( _ ) 分隔
├── rb_layout
# (2)文件名
- 文件名包含多个单词时一般使用小驼峰,不能含有空格
// 反例 ├── index.html ├── main.js └── components ├── pageheader ├── pagearticle └── pagefeader // 正例 ├── index.html ├── main.js └── components ├── pageHeader ├── pageArticle └── pageFooter
# (3)组件名
- 组件名一般使用大驼峰,用来和文件名区分
└── FootTabBar.vue
# (4)css命名
- class 类名使用小写,如包含多个单词,单词间使用半角的连词线(-)分隔
.page-bg
- id 和 class 的命名,需要是有意义的名字
/* 不推荐: 无意义 不易理解 */ #yee-1901 {} /* 推荐: 明确详细 */ #gallery {} #login {} .video {}
# (5)变量名
- 一般使用小驼峰
var aName = [1, 2, 3]; var oBtn = document.getElementById('btn'); var iCount = 0; var sName = "zhuyujia";
# (6)常量名
- 全部大写,如包含多个单词,单词间使用半角的连词线 ( _ ) 分隔
var MAX_COUNT = 10; var URL = 'http://www.baidu.com';
# (7)函数名
- 小驼峰,其中构造函数使用大驼峰
// 按编码查询 export const findByCode = (data) => {}
实用须知:
以上命名必须语义化,且尽量使用英文,而不是汉语拼音。并且函数命名时,前缀需为动词,例如 can,has,is,get,set 等;
css 中 class 命名,须遵循BEM原则,即B模块( block )、E元素( element )、M修饰符( modifier ),按照连词线 ( - ) 的书写方式。如给新闻列表ul-li命名,则为ul:
class=“news-list”
,li:class=“news-list-item”
;为用于存放接口请求(
/src/services
)和 vuex 仓库(/src/store
)等文件夹下的文件命名时,可以使用小驼峰方式,找到同类型页面,按照页面路由层级组合命名 如课程详情页面,所属路由层级为:/view/knowledge/videoDetail
,同类型页面路由层级为:/view/knowledge
,则在 services 层可命名为knowledge.service.js
,在 store 层可命名为/store/knowledge/
# 二、注释规范
# 1.注释方式:
# (1)单行注释
- 采用
// 注释说明
,如下所示:// sass化域名
# (2)多行注释
- 采用
/* 注释说明 */
,如果至少三行注释,第一行为/**
,最后行为*/
,其他行以*
开始,并且注释文字与*
保留一个空格。如下所示:/** * 创建人: zzz * 创建时间: 2017-6-20 * 创建名称: z */
# 2.规范说明:
# (1)html(template内)部分
- 按功能分块注释。如头部区域、选择楼层Tree、客房列表Table、新增弹框;
<!-- 左侧楼层树形结构 --> <Tree @queryTableAgain = "checkedTreeAgain" :sweepTreeData = "sweepTreeData" :trainInstId = "trainInstId" ></Tree> <!-- 右侧客房表格结构 --> <Table @tableQueryAgain = "checkedTableAgain" @passOrNoNum = "passOrNoNum"></Table> <!-- 保洁人员选择页面弹框 --> <el-dialog title="保洁人员"></el-dialog>
# (2)css(style内)部分
- 对应html的模块划分进行注释
# (3)js(script内)部分
- data对应变量、method里面的方法都需要进行注释,如下所示:
data () { return { content:'', // 正文 createTime:'', // 发布时间 remarks:'', // 备注 } } methods:{ // 加载新闻列表 loadNewsList() {} }
# (4)api 文件夹内的接口
- api或service文件夹内的接口文件,需要对每个接口进行注释说明,如下所示:
/** * 判断当前知识有没有权限 * type 0 栏目里来的,1 企业知识或者搜索 */ export const goKnowledge = (knowledgeId, type, masterId) => {}
# (5)store 文件夹内的变量和方法名等
- 如下所示:
// 设置是否为点击上一步 setStepPrev1({dispatch,commit,state}, data){ commit('GET_STEP_PREV1',data) },
# (6)枚举值
- 如下所示:
export const METHOD = { VISIT: '000', // 访问 ADD: '001', // 增 DELETE: '002', // 删 MODIFY: '003', // 改 SELECT: '004', // 查 }
# (7)重要注释
所有不能直接理解的变量和方法,需要特别注释
比较复杂的代码逻辑要有相应的注释说明,参考cuh5工程router/index.js文件
说明:
- 多个变量,单个注释时,尽量排版使得上下变量注释垂直对齐
data() { return { list: [], // 数据列表 schemeId: '', // 方案id keywords: '', // 关键词 moduleId: '', // 主模块id dataStorelist: [], // 子模块列表 } }
- 对函数注释时,尽量使用多行注释的方式,将方法描述、形参、返回体等标注清楚
/**js * 获得指定路由的全地址 * @param {string} name 路由的名称 * @param {object} params 参数(路由里的参数) * @param {object} query 参数(?之后的参数) * @return {string} */ router.getUrlByRouterName = function (name, params, query) { return '' }
# 三、书写规范
# 1.HTML规范
标签语义化:不能全是div、span,如标题根据重要性用h*(同一页面只能有一个h1),段落标记用p,列表用ul
图片属性:img标签通常alt属性不为空。
<img src="html5.gif" alt="HTML5">
禁止使用内联样式:原则上禁止使用内联样式,如
<!-- 禁止使用 --> <div style=“margin-top:20px”></div>
公共的class类名:公共的class类名,比如content、content-title等,只做公共布局用
元素及标签闭合:HTML元素共有以下5种:
- (1)空元素:area、base、br、col、command、embed、hr、img、input、keygen、link、meta、param、source、track、wbr
- (2)原始文本元素:script、style
- (3)RCDATA元素:textarea、title
- (4)外来元素:来自MathML命名空间和SVG命名空间的元素。
- (5)常规元素:其他HTML允许的元素都称为常规元素。
- 元素标签的闭合应遵循以下原则:
- 原始文本元素、RCDATA元素以及常规元素都有一个开始标签来表示开始,一个结束标签来表示结束。 - 某些元素的开始和结束标签是可以省略的,如果规定标签不能被省略,那么就绝对不能省略它。 - 空元素只有一个开始标签,且不能为空元素设置结束标签。 - 外来元素可以有一个开始标签和配对的结束标签,或者只有一个自闭合的开始标签,且后者情况下该元素不能有结束标签。
代码嵌套:原则上,块级元素嵌套内联元素,避免内联元素嵌套块级元素
Viewport Meta 通用设置:原则上,块级元素嵌套内联元素,避免内联元素嵌套块级元素
// width – viewport的宽度 // height – viewport的高度 // initial-scale – 初始的缩放比例 // minimum-scale – 允许用户缩放到的最小比例 // maximum-scale – 允许用户缩放到的最大比例 // user-scalable – 是否允许用户缩放 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
# 2.CSS规范
# (1)样式层级
- 前端样式可分为四个层次:
reset.css:重置各浏览器内置基础样式;
base.css:提供基础的css公用类,减少css代码重复, 如
.ft-14, .bg-white, .clearfix, .fl, .fr
- 需要注意的是,这里将会将页面的基础布局风格抽离成特殊的class, 这些class只能做基础布局用,不可滥用,如
.content, .content-title
- 需要注意的是,这里将会将页面的基础布局风格抽离成特殊的class, 这些class只能做基础布局用,不可滥用,如
common.css:将引入的UI组件库(ElmentUI)内置样式覆盖,作为全局的基础样式, 如覆盖ElmentUI组件库的table方面的样式等;
组件(页面)内样式:通常会使用两个style,其中一个必须加scoped, 防止样式冲突, 另外一个用来覆盖UI组件库样式, 不能加scoped, 但是必须用页面顶级class包裹, 避免出现样式冲突,该顶级class建议使用.vue文件名。
<!--不加scope,但是必须用顶级class包裹--> <style> .logindetail .el-select { border: none!important; height: 0!important; } </style> <!--加scope,避免样式冲突--> <style lang="less" scoped> .header { height:30px; } </style>
# (2)缩写属性和属性值
- CSS有些属性是可以缩写的,比如填充,边缘,字体,背景,边框等等。缩写代码可以提高用户的阅读体验
.header { background: #ffcc66 url('../images/test.jpg') no-repeat fixed left top; }
- 当数值为小数时,小数点前面的“0”可以去除
.header { opacity: .7; }
- “0像素”后面的单位可以去除
.test { margin: 0; padding: 0; font-size: .8em; }
- 16进制的颜色代码重叠的字符可以缩写的尽量缩写
.test { color: #fff; }
# (3)代码风格
- 采用UTF-8编码,在CSS头部引用
@charset "utf-8";
- 使用4个空格作为一个缩进层级,不允许使用2个空格或tab字符,或者可以使用插件比如vscode插件帮助优化排版
- 尽量不要使用@import,与
相比,@import速度较慢,增加了额外的页面请求,并可能导致其他无法预料的问题。 - Media Query不得单独编排,必须与相关的规则一起定义。
- 推荐使用less、sass、stylus等css预处理,但是嵌套层次不要超过四层
# (4)设置css样式时,可以遵循以下顺序(可参考,不做强求)
- 结构性属性:
1. display 2. position, left, top, right etc. 3. overflow, float, clear etc. 4. margin, padding
- 表现性属性:
background, border font, text
# (5)声明完结,所有声明都要用 ;
结尾
/* 不推荐 */
.test {
display: block;
height: 100px
}
/* 推荐 */
.test {
display: block;
height: 100px;
}
# (6)规则声明块样式规范
- 当规则声明块中有多个样式声明时,每条样式独占一行。
- 列表属性并排书写时,用逗号
,
分隔,逗号后必须跟一个空格 - 选择器与左大括号
{
之间必须加一个空格 - 属性名与冒号
:
之间不允许包含空格,冒号与属性值之间必须包含空格 - 在每条样式后面都以分号
;
结尾。 - 规则声明块的右大括号} 独占一行。
- 所有最外层引号使用单引号
''
。 - 当一个属性有多个属性值时,以逗号
,
分隔属性值,每个逗号后添加一个空格,当单个属性值过长时,每个属性值独占一行。 - 每个规则声明间用空行分隔。
# 3.js规范
# (1)使用严格等于(===)判断
- 区别说明:
==, 两边值类型不同的时候,要先进行类型转换,再比较。
===,不做类型转换,类型不同的一定不等。
if (to.name === 'index'){}
# (2)建议使用es6
- 如使用const 和 let,箭头函数、解构、模板字符串等
// 使用箭头函数 [1, 2, 3].map(x => { const y = x + 1; return x * y; });
# (3)对象
- 使用字面量值创建对象
// bad const a = new Object{} // good const a = {}
- 别使用保留字作为对象的键值,这样在 IE8 下不会运行,比如default
请使用对象属性值的简写方式
// bad const item = { sex: 'male', job, age: 25, department } // good const item = { job, department, sex: 'male', age: 25 }
- 优先使用对象展开运算符
...
来做对象浅拷贝而不是使用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 }
# (4)数组
- 使用字面量值创建数组
// bad const items = new Array() // good const items = []
- 使用展开运算符
...
复制数组// bad const items = [] const itemsCopy = [] const len = items.length let i // bad for (i = 0; i < len; i++) { itemsCopy[i] = items[i] } // good itemsCopy = [...items]
- 如果一个数组有多行则要在数组的开括号后和闭括号前使用新行
// bad const arr = [ [0, 1], [2, 3], [4, 5] ] const objectInArray = [{ id: 1 }, { id: 2 }] const numberInArray = [ 1, 2 ] // good const arr = [[0, 1], [2, 3], [4, 5]] const objectInArray = [ { id: 1 }, { id: 2 } ] const numberInArray = [ 1, 2 ]
# (5)解构赋值
- 当需要使用对象的多个属性时,请使用解构赋值
(解构可以避免创建属性的临时引用)
// 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}` } // better function getFullName ({ firstName, lastName }) { return `${firstName} ${lastName}` }
- 当需要使用数组的多个值时,请同样使用解构赋值
const arr = [1, 2, 3, 4] // bad const first = arr[0] const second = arr[1] // good const [first, second] = arr
# (6)字符串
- 字符串太长的时候,请不要使用字符串连接符换行
\
,而是使用+
程序化生成字符串时,请使用模板字符串const test = 'test' // bad const str = ['a', 'b', test].join() // bad const str = 'a' + 'b' + test // good const str = `ab${test}`
- 不要对字符串使用
eval()
,会导致太多漏洞 - 不要在字符串中使用不必要的转义字符
# (7) 函数
- 不要在非函数代码块
if()
,while()
等中声明函数 - 不要将参数命名为
arguments
,会导致该参数的优先级高于每个函数作用域内原先存在的arguments
对象 - 使用参数默认值语法而不是修改函数参数
// really bad function handleThings (opts) { opts = opts || {} // ... } // still bad function handleThings (opts) { if (opts === void 0) { opts = {} } // ... } // good function handleThings (opts = {}) { // ... }
- 不要更改参数
- 不要给参数重新赋值
- 调用可变参数函数时建议使用展开运算符
...
# (8)模块
- 使用标准的 ES6 模块语法
import
和export
// bad const util = require('./util') module.exports = util // good import Util from './util' export default Util // better import { Util } from './util' export default Util
- 同个文件每个模块只允许
import
一次,有多个import
请书写在一起 - 将所有
import
语句放在文件最前方 - 多行导入应该像多行数组和对象文字一样缩进
# 四、图片规范
# 1.命名语义化
- 尽量不使用1.jpg、a.png这种命名方式,可使用header.png, logo.png等
# 2.图片存放
- 考虑页面加载性能,静态图片须统一存放至媒体服务器
# 3.图片压缩
- 对于静态图片需考虑图片大小,使用前最好将图片压缩,推荐压缩网站 https://tinypng.com (opens new window)
# 五、目录结构规范
- vue工程目录结构如下:
├── build 项目构建(webpack)相关代码 ├── config 项目配置 ├── dev.env.js 开发环境变量 ├── index.js 项目配置文件 ├── prod.env.js 生产环境变量 └── test.env.js 测试环境变量 ├── package.json npm包配置文件,npm脚本 ├── src 日常开发主要在这个文件夹 ├── asset 放置静态资源, 会被webpack构建 ├── main.js 项目入口文件 ├── components 公共组件 └── App.vue 根组件 └── pages 页面文件 └── api 接口文件 └── router 路由文件 ├── static 纯静态资源,不会被webpack构建 ├── index.html 首页入口文件 ├── README.md 项目的说明文档 ├── dist 打包之后的文件
说明
- 注意将公共组件抽出来放入components文件夹中
- router文件夹中通常会有一个index.js文件,用来实现公共登录逻辑
# 六、单文件结构规范
- 单个vue文件整体代码结构,按照 template —-> script —-> style 顺序
<template> <div> <!--必须在div中编写页面--> </div> </template> <!--js脚本--> <script> export default {} </script> <!--声明语言--> <style lang="scss" scoped> </style>
- script内的export default,按照如下顺序:
<script> export default { components : {}, // 1.注册需要用到的组件 beforeRouteEnter() {} // 2.导航进入该组件前 beforeRouteLeave() {} // 3.导航离开该组件前 props:{}, // 4.如果有父组件,传递到这个子组件的props data () { // 5.定义变量 return {} }, directives :{}, // 6.注册自定义指令 filters:{}, // 7.自定义过滤器 watch:{}, // 8.监控一个变量或对象,必须data里面声明 computed:{}, // 9.对多个变量或者对象进行处理后返回一个结果值,不在data声明 created(){}, // 10.组件实例创建完成,未生成dom mounted(){}, // 11.模板编译/挂载之后,已挂载到实例之后 beforeDestroy (){}, // 12.组件销毁前调用 activated (){}, // 13.keep-alive组件被激活时调用 deactivated (){}, // 14.keep-alive组件被移除时调用 methods: { // 15.自定义方法,相近、相关联的方法需放在一起 // 16.监听($emit)回调事件 callBack (val){} // 17.页面初始请求api方法 loadParamList () { paramManageService.statisticsBudgetParam(queryInfo).then(resp => {}) } // 18.其他方法 }, } </script>
# 七、组件规范
# 1.按功能分
- 组件分为UI组件(或称为展示组件),和容器组件;
- 将每个 .vue 文件拆成一个文件夹,其中index.vue 作为容器组件;
- 其余如Table.vue和Search.vue等作为UI组件;
展示组件 容器组件 作用 描述如何展现(骨架、样式) 描述如何运行(数据获取、状态更新) 直接使用store 否 是 数据来源 props 监听store state 数据修改 从props调用回调函数 向store派发actions
# 2.按可复用性分
- 组件分为全局基础组件和页面级组件;将全局基础组件提取至/src/components内。
# 八、Vuex使用规范
- 使用手册请参照官方文档,此处只涉及相关开发规范
- 将store切分成modules, 以页面层级和文件名嵌套和命名;
- state变化,导致视图更新;
- 所有的state改变,必须是由Mutation内的函数引起;
- 所有的Mutaions内的函数,必须通过Actions内的commit()方法触发;
- 所有的Actions方法,都必须是页面通过dispatch()派发;
- Mutation内的函数只操作state, 任何其他的操作在Actions内进行。
store文件结构示例如下:
└── store ├── index.js # 组装模块并导出 store 的地方 └── modules # 放置所有模块 ├── module1 # 模块1 ├── action.js # 提交修改方法,commit mutation里面的方法 ├── getters.js # 监听state数据源变化,返回结果 ├── index.js # 放置state数据源 ├── mutations.js # 定义修改方法,只操作state, 任何其他的操作在Actions内进行 └── module2 # 模块2
# 九、特别说明
- 禁止使用 import * as XXX from 'xxx.service',防止注入过多无用代码;
- 公共部分代码,不要私自修改,如确有需求,可以先行思考,将方案上报讨论通过后,由指定人员进行修改;
- 公共代码是指除view文件夹以外的工程代码,且不为全局公共组件。新增依赖也属于公共代码范围;
- 提交前检查代码格式是否按规范调整好;
- 开发调试完成之后去除全部的console.log,再提交;
- 提交前去除无用代码,主要是指页面上注释不要的代码,并且后面也应该不会用到的代码;
- 提交代码前开发人员必须先解决当前开发的页面在控制台的所有报错。