前言
与独立开发不同,在多人协作的开发团队中,任何人都有可能参与到你或他人编写的模块中去,倘若每个人的编程风格都大不一致,那么将给其他人带来阅读、协作与维护造成不小的困扰,因此统一团队的编程规范是必要且关键的。
除此之外,养成自己良好灵活的编程风格习惯,对于团队变化带来的规范改动,也将是如鱼得水而非束手束脚。
保持个人规范仅仅是良好的习惯,但建议以社区大规范为主,灵活调整具体细则配合ESLint做代码检查,最终服务于团队项目规范
#
对于 ESLint Rules 及相关代码风格,以 semi 规则为例,结尾是否添加分号一直是大家喜欢诟病的规则。
一般来讲,添加分号是为了规避以往构建工具或工具库带来的问题。现如今环境下,添加分号的必要性显得没那么重要,以 尤大的讨论 及 vue 源码为例。
对于习惯与项目遗留问题,不建议强行进行改动。而更应该注重于实际开发团队而选择对应的规则,良好的编程规范是个人编程素养,而具体规范落实,则应该是团队协作的基本。舍小我服务于团队才是正确的选择,而无需进行无意义的争论。
HTML
在HTML5的标准下,以及网站SEO优化、无障碍阅读需求,对于HTML标签的书写应当是追求标签语义化
CSS
参考文献:
百度前端规范、
css命名规范-BEM
JavaScript
作为前端工程师最重要的语言工具(包括Node.js),js规范显得尤为重要,也是不同水平的编程者风格差异最大的一部分。
不论你使用那种js语法框架,基本js编程风格都是一个合格的前端开发者应该掌握的。
命名规范
- 基本的变量与函数命名 ```javascript // 变量及函数应始终小驼峰命名为主 const nickname = ‘nickname’; let userNum = 4;
function utils() { / to do / }; function formatText() { / to do / };
2. 常量命名```javascript// 常量应使用全部大写字母命名,并以下划线分割单词// 建议:模块/功能_功能/作用_变量const PROMISE_STATUS_PENDING = 'pending';const PROMISE_STATUS_RESOLVE = 'fulfilled';const PROMISE_STATUS_REJECT = 'rejected';// ESM 是值的引用, 此处使用Object.freeze()做枚举避免外部不恰当的改动const PROMISE_STATUS = Object.freeze({PENDING:'pending',RESOLVE:'fulfilled',REJECT:'rejected'})
- 类 & 非ts下的私有方法/变量命名
// 类名必须为大驼峰命名class Person {// 类中或模块化的私有方法/属性应加上下划线前缀constructor() { this._name = '示例' }_getName() {return this._name;}// 公有方法/属性仍遵守基本规范setName(name) {this._name = name;}}
引入 TypeScript
在合适的项目/工具库中引入TypeScript可以降低后期维护成本
class Person {private name: string;constructor() { this._name = '示例' }private getName(): string {return this._name;}public setName(name: string): void {this._name = name;}}// 常量枚举enum PROMISE_STATUS = {PENDING = 'pending',RESOLVE = 'fulfilled',REJECT = 'rejected'}
- 更好的命名
除了基本的命名风格,单词的书写也应更贴近单词
对于boolean类型的属性与方法,应根据场景合理使用is、has等表示判断意义的单词作为前缀
// goodconst nickname = '命名';let isUser = true;let hadLogin = true;function checkIsUser() { return true };function isUser() { return false };// badconst nickName = '命名';let ifUser = false;let login = false;function ifUser() { return false }
语法规范
- 一般情况下使用ES6新语法 ```javascript // good const demo = ‘示例’; let num = 1;
const { a, b: 1 } = obj;
// bad var demo = ‘示例’; var num = 1;
var a = obj.a; var b = obj.b || 1;
2. 单行 if 语句也应保留括号, switch 语句应处理default情况```javascript// badif (condition) // to do// goodif (condition) {// to do}switch (key) {case value:// to dobreak;default:// to do}
合理使用try…catch处理error,避免在catch中对err进行重新赋值
// 对于接口报错应与后端配合进行统一处理,且避免在catch中对err进行重新赋值try {throw new Error('报错了');} catch (err) {// bad// err.errMsg = 'err';throw(err) // // or toast(err.errMsg)}
在合适的地方销毁定时器/监听器 ```javascript // 此处使用vuejs示例 mounted() { this.timer = setInterval(() => { / to do / }, interval); } destroyed() { this.timer && clearInterval(this.timer) }
// 或者 mounted() { this.timer = setInterval(() => { / to do / }, interval); this.$once(‘hook:destroyed’, () => { this.timer && clearInterval(this.timer) }) }
5. 根据实际场景合理使用 for 迭代器```javascriptconst list = [1, 2, 3]// goodlist.forEach(item => {console.log(item);})// map虽然与forEach相似,但map还有生成并返回新数组的特性const newList = map(item => item * 2); // [2, 4, 6]// 而array.reduce()则要注意对数组进行判空处理if (Array.isArray(list) && list.length > 0) {return list.reduce((total, val) => total + val); // 6}// badlist.map(item => {console.log(item);})
- 合理使用async…await配合try…catch 代替 Promise.then() 避免回调地狱 ```javascript function onErrorHandle(err) { throw(err) // // or toast(err.errMsg) }
// good async function _ajax() { try { const res1 = await new Promise1(); const res2 = await new Promise2(); // to do … } catch (err) { onErrorHandle(err) } } // OR async function _ajax() { const res1 = await new Promise1().catch(err1 => …); const res2 = await new Promise2().catch(err2 => …); // to do … }
// bad function _ajax() { new Promise1().then((res1) => { new Promise2().then(res2 => { res2() }).catch(err2 => { onErrorHandle(err) }) }).catch(err1 => { onErrorHandle(err) }) }
<a name="kqXwo"></a>## ESLint<a name="gcmff"></a>### .eslintrc.js```javascript// 个人 ESLint 配置示例module.exports = {// 指定文件为eslint配置根目录,而不再查找使用父级的eslint配置root: true,// 指定脚本的运行环境env: {brower: true,node: true},// 按需引入所需eslint插件extends: ['plugin:vue/essential', '@vue/prettier'],parserOptions: {/*** 自定义语法规则或解析配置*/},rules: {// 规避与prettier的规则冲突'prettier/prettier': 'off',// es6规范'no-unused-vars': 'error','prefer-const': ['error', { destructuring: 'all', ignoreReadBeforeAssign: true }],'no-undef': 'error','no-debugger': 'error','no-return-assign': 'off',// 禁止重复声明变量'no-redeclare': ['error', { builtinGlobals: true }],// == 运算符可能会带来一些非预期问题eqeqeq: 'error',// 省略单参数的箭头函数的括号'arrow-parens': ['warn', 'as-needed'],// 减少不必要的空白或填充'no-trailing-spaces': 'off','padded-blocks': 'off',// 避免直接操作原型对象, 改用call/apply指定调用对象'no-prototype-builtins': 'error',// 在case中声明'no-case-declarations': 'off',// 虽然编译时会自动识别添加分号, 但良好的习惯可以减少;开头的脚本文件之类的产生semi: ['error', 'always'],// if 语句的括号有有必要的curly: ['error', 'all'],// 优先使用单引号quotes: ['error', 'single', { avoidEscape: true }],// 命名风格应始终以大小驼峰为主camelcase: ['error',{properties: 'never' // 对于对象中的属性根据实际情况决定}],// 最后的属性/方法没必要尾随逗号'comma-dangle': ['error'],// 方法名不应该有空格或换行,避免误解'func-call-spacing': 'error',// others'no-return-await': 'off','no-async-promise-executor': 'warn',/*** vue相关的eslint规则* https://eslint.vuejs.org/rules/*/'vue/max-attributes-per-line': 'off','vue/return-in-computed-property': 'error','vue/html-self-closing': ['error',{html: {void: 'any',normal: 'never',component: 'any'},svg: 'always',math: 'always'}]}};
按需禁用ESLint
官方文档 ```bash
ESLint禁用
禁用下一行
// eslint-disable-next-line
禁用当前行
/ eslint-disable-line / // eslint-disable-line
禁用整个文件
/ eslint-disable /
只禁用指定规则 只需在上述命令后加上 eslint-rule
/ eslint-disable no-alert /
添加注释 只需在上述命令后加上—-comment
/ eslint-disable no-alert —-demo /
在项目根目录添加.eslintignore文件并声明禁用文件
/bin/bash
echo “*.json” >> .eslintignore
<a name="DdYGd"></a>### 在vscode中配置- 进入插件界面搜索并安装插件`Prettier`、`Eslint`<br />vscode --> Perference --> Extensions<br />快捷键:[mac]`command + shift + x` / [windows]`ctrl + shift + x`- vscode --> Perference --> Setting --> 右上角Open Setting(json) 添加以下配置```json// 保存时自动格式化"editor.formatOnSave": true"editor.codeActionsOnSave": {"source.fixAll.eslint": true},// 如果需要指明npm工具"eslint.packageManager": "yarn",// 如果需要指明检验文件类型"eslint.validate": ["ts","javascript","javascriptreact","vue","typescript","typescriptreact","typescript"],// 如果需要将eslint作为格式化工具"eslint.format.enable": true
- 为方便修改覆盖Prettier默认格式化规则,可以在项目根目录配置.prettierrc文件
{"printWidth": 120,"singleQuote": true,"trailingComma": "none","semi": true}
- 于项目根目录创建.eslintrc.js配置文件
目录结构参考
合理的目录结构使项目模块更为清晰,便于维护
# 示例为vue2.x项目src文件夹├── api 外部数据的请求以及转换├── assets 静态文件或者全局css样式├── components 全局组件├── core 复杂逻辑或者核心代码├── docs 文档├── lib 工具类, utils/tools├── views 页面组件├──── page.vue├── router 路由├──── index.js├── store 数据流、复杂业务状态├──── modules store 模块化 + namespaced├──── index.js├── App.vue├── main.js app入口
