前言

与独立开发不同,在多人协作的开发团队中,任何人都有可能参与到你或他人编写的模块中去,倘若每个人的编程风格都大不一致,那么将给其他人带来阅读、协作与维护造成不小的困扰,因此统一团队的编程规范是必要且关键的。

除此之外,养成自己良好灵活的编程风格习惯,对于团队变化带来的规范改动,也将是如鱼得水而非束手束脚。

保持个人规范仅仅是良好的习惯,但建议以社区大规范为主,灵活调整具体细则配合ESLint做代码检查,最终服务于团队项目规范


#

对于 ESLint Rules 及相关代码风格,以 semi 规则为例,结尾是否添加分号一直是大家喜欢诟病的规则。
一般来讲,添加分号是为了规避以往构建工具或工具库带来的问题。现如今环境下,添加分号的必要性显得没那么重要,以 尤大的讨论 及 vue 源码为例。
对于习惯与项目遗留问题,不建议强行进行改动。而更应该注重于实际开发团队而选择对应的规则,良好的编程规范是个人编程素养,而具体规范落实,则应该是团队协作的基本。舍小我服务于团队才是正确的选择,而无需进行无意义的争论。

HTML

在HTML5的标准下,以及网站SEO优化、无障碍阅读需求,对于HTML标签的书写应当是追求标签语义化

CSS

参考文献:
百度前端规范
css命名规范-BEM

JavaScript

作为前端工程师最重要的语言工具(包括Node.js),js规范显得尤为重要,也是不同水平的编程者风格差异最大的一部分。
不论你使用那种js语法框架,基本js编程风格都是一个合格的前端开发者应该掌握的。

命名规范

  1. 基本的变量与函数命名 ```javascript // 变量及函数应始终小驼峰命名为主 const nickname = ‘nickname’; let userNum = 4;

function utils() { / to do / }; function formatText() { / to do / };

  1. 2. 常量命名
  2. ```javascript
  3. // 常量应使用全部大写字母命名,并以下划线分割单词
  4. // 建议:模块/功能_功能/作用_变量
  5. const PROMISE_STATUS_PENDING = 'pending';
  6. const PROMISE_STATUS_RESOLVE = 'fulfilled';
  7. const PROMISE_STATUS_REJECT = 'rejected';
  8. // ESM 是值的引用, 此处使用Object.freeze()做枚举避免外部不恰当的改动
  9. const PROMISE_STATUS = Object.freeze({
  10. PENDING:'pending',
  11. RESOLVE:'fulfilled',
  12. REJECT:'rejected'
  13. })
  1. 类 & 非ts下的私有方法/变量命名
    1. // 类名必须为大驼峰命名
    2. class Person {
    3. // 类中或模块化的私有方法/属性应加上下划线前缀
    4. constructor() { this._name = '示例' }
    5. _getName() {
    6. return this._name;
    7. }
    8. // 公有方法/属性仍遵守基本规范
    9. setName(name) {
    10. this._name = name;
    11. }
    12. }

引入 TypeScript

在合适的项目/工具库中引入TypeScript可以降低后期维护成本

  1. class Person {
  2. private name: string;
  3. constructor() { this._name = '示例' }
  4. private getName(): string {
  5. return this._name;
  6. }
  7. public setName(name: string): void {
  8. this._name = name;
  9. }
  10. }
  11. // 常量枚举
  12. enum PROMISE_STATUS = {
  13. PENDING = 'pending',
  14. RESOLVE = 'fulfilled',
  15. REJECT = 'rejected'
  16. }
  1. 更好的命名

除了基本的命名风格,单词的书写也应更贴近单词
对于boolean类型的属性与方法,应根据场景合理使用is、has等表示判断意义的单词作为前缀

  1. // good
  2. const nickname = '命名';
  3. let isUser = true;
  4. let hadLogin = true;
  5. function checkIsUser() { return true };
  6. function isUser() { return false };
  7. // bad
  8. const nickName = '命名';
  9. let ifUser = false;
  10. let login = false;
  11. function ifUser() { return false }

语法规范

  1. 一般情况下使用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;

  1. 2. 单行 if 语句也应保留括号, switch 语句应处理default情况
  2. ```javascript
  3. // bad
  4. if (condition) // to do
  5. // good
  6. if (condition) {
  7. // to do
  8. }
  9. switch (key) {
  10. case value:
  11. // to do
  12. break;
  13. default:
  14. // to do
  15. }
  1. 合理使用try…catch处理error,避免在catch中对err进行重新赋值

    1. // 对于接口报错应与后端配合进行统一处理,且避免在catch中对err进行重新赋值
    2. try {
    3. throw new Error('报错了');
    4. } catch (err) {
    5. // bad
    6. // err.errMsg = 'err';
    7. throw(err) // // or toast(err.errMsg)
    8. }
  2. 在合适的地方销毁定时器/监听器 ```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) }) }

  1. 5. 根据实际场景合理使用 for 迭代器
  2. ```javascript
  3. const list = [1, 2, 3]
  4. // good
  5. list.forEach(item => {
  6. console.log(item);
  7. })
  8. // map虽然与forEach相似,但map还有生成并返回新数组的特性
  9. const newList = map(item => item * 2); // [2, 4, 6]
  10. // 而array.reduce()则要注意对数组进行判空处理
  11. if (Array.isArray(list) && list.length > 0) {
  12. return list.reduce((total, val) => total + val); // 6
  13. }
  14. // bad
  15. list.map(item => {
  16. console.log(item);
  17. })
  1. 合理使用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) }) }

  1. <a name="kqXwo"></a>
  2. ## ESLint
  3. <a name="gcmff"></a>
  4. ### .eslintrc.js
  5. ```javascript
  6. // 个人 ESLint 配置示例
  7. module.exports = {
  8. // 指定文件为eslint配置根目录,而不再查找使用父级的eslint配置
  9. root: true,
  10. // 指定脚本的运行环境
  11. env: {
  12. brower: true,
  13. node: true
  14. },
  15. // 按需引入所需eslint插件
  16. extends: ['plugin:vue/essential', '@vue/prettier'],
  17. parserOptions: {
  18. /**
  19. * 自定义语法规则或解析配置
  20. */
  21. },
  22. rules: {
  23. // 规避与prettier的规则冲突
  24. 'prettier/prettier': 'off',
  25. // es6规范
  26. 'no-unused-vars': 'error',
  27. 'prefer-const': ['error', { destructuring: 'all', ignoreReadBeforeAssign: true }],
  28. 'no-undef': 'error',
  29. 'no-debugger': 'error',
  30. 'no-return-assign': 'off',
  31. // 禁止重复声明变量
  32. 'no-redeclare': ['error', { builtinGlobals: true }],
  33. // == 运算符可能会带来一些非预期问题
  34. eqeqeq: 'error',
  35. // 省略单参数的箭头函数的括号
  36. 'arrow-parens': ['warn', 'as-needed'],
  37. // 减少不必要的空白或填充
  38. 'no-trailing-spaces': 'off',
  39. 'padded-blocks': 'off',
  40. // 避免直接操作原型对象, 改用call/apply指定调用对象
  41. 'no-prototype-builtins': 'error',
  42. // 在case中声明
  43. 'no-case-declarations': 'off',
  44. // 虽然编译时会自动识别添加分号, 但良好的习惯可以减少;开头的脚本文件之类的产生
  45. semi: ['error', 'always'],
  46. // if 语句的括号有有必要的
  47. curly: ['error', 'all'],
  48. // 优先使用单引号
  49. quotes: ['error', 'single', { avoidEscape: true }],
  50. // 命名风格应始终以大小驼峰为主
  51. camelcase: [
  52. 'error',
  53. {
  54. properties: 'never' // 对于对象中的属性根据实际情况决定
  55. }
  56. ],
  57. // 最后的属性/方法没必要尾随逗号
  58. 'comma-dangle': ['error'],
  59. // 方法名不应该有空格或换行,避免误解
  60. 'func-call-spacing': 'error',
  61. // others
  62. 'no-return-await': 'off',
  63. 'no-async-promise-executor': 'warn',
  64. /**
  65. * vue相关的eslint规则
  66. * https://eslint.vuejs.org/rules/
  67. */
  68. 'vue/max-attributes-per-line': 'off',
  69. 'vue/return-in-computed-property': 'error',
  70. 'vue/html-self-closing': [
  71. 'error',
  72. {
  73. html: {
  74. void: 'any',
  75. normal: 'never',
  76. component: 'any'
  77. },
  78. svg: 'always',
  79. math: 'always'
  80. }
  81. ]
  82. }
  83. };

按需禁用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

  1. <a name="DdYGd"></a>
  2. ### 在vscode中配置
  3. - 进入插件界面搜索并安装插件`Prettier`、`Eslint`<br />vscode --> Perference --> Extensions<br />快捷键:[mac]`command + shift + x` / [windows]`ctrl + shift + x`
  4. - vscode --> Perference --> Setting --> 右上角Open Setting(json) 添加以下配置
  5. ```json
  6. // 保存时自动格式化
  7. "editor.formatOnSave": true
  8. "editor.codeActionsOnSave": {
  9. "source.fixAll.eslint": true
  10. },
  11. // 如果需要指明npm工具
  12. "eslint.packageManager": "yarn",
  13. // 如果需要指明检验文件类型
  14. "eslint.validate": [
  15. "ts",
  16. "javascript",
  17. "javascriptreact",
  18. "vue",
  19. "typescript",
  20. "typescriptreact",
  21. "typescript"
  22. ],
  23. // 如果需要将eslint作为格式化工具
  24. "eslint.format.enable": true
  • 为方便修改覆盖Prettier默认格式化规则,可以在项目根目录配置.prettierrc文件
    1. {
    2. "printWidth": 120,
    3. "singleQuote": true,
    4. "trailingComma": "none",
    5. "semi": true
    6. }

目录结构参考

合理的目录结构使项目模块更为清晰,便于维护

  1. # 示例为vue2.x项目src文件夹
  2. ├── api 外部数据的请求以及转换
  3. ├── assets 静态文件或者全局css样式
  4. ├── components 全局组件
  5. ├── core 复杂逻辑或者核心代码
  6. ├── docs 文档
  7. ├── lib 工具类, utils/tools
  8. ├── views 页面组件
  9. ├──── page.vue
  10. ├── router 路由
  11. ├──── index.js
  12. ├── store 数据流、复杂业务状态
  13. ├──── modules store 模块化 + namespaced
  14. ├──── index.js
  15. ├── App.vue
  16. ├── main.js app入口