前端开发规范

781 阅读14分钟

前言 强烈推荐的编辑器 VSCode


为什么选择VSCode进行前端开发

对前端来说,这是一款性感无比的编辑器。咱们团队有大部分人已经在用了,
而且关于VSC 一些提高开发效率的小技巧。我相信已经有不少前端在使用它了。 
VSC 本身是基于当前大火的 TS 来写的,所以对于 TS 的支持自然很好;
又是基于 electron 开发,底层 Node.js 对前端来说再熟悉不过了
知乎种草链接:[如何评价 Visual Studio Code?]https://www.zhihu.com/question/29984607/answer/93300215

通用插件


* Auto Close Tag(自动闭合html标签):极其方便快速开发必备

* Auto Rename Tag(自动更改标签):有时候会在开发时候更改标签,此插件能够自动更改对应的标签名
* Debugger for Chrome(模拟谷歌环境来debug):此插件会在项目中生成.vscode文件夹下的launch.json所有配置将在次文件中

* Document This(注释代码):目前来看应该是最受欢迎的注释插件,尤其是注释函数给个赞!!推荐

* Git History、Git Lens、Git Project Manager:这3个就是标准的打包为vscode集成的git服务的。因为本身习惯使用git bash 来操作git的原因所以并未深入研究。不过喜欢方便的小伙伴还是推荐稍微研究下并不难。

* Instellisense(npm插件):必须安装的插件,能够在vscode集成的终端中使用npm。

* Path Autocomplete(路径自动补全):不用是傻子系列。

* JSDoc 是一个 API 文档生成器,你只需要在代码中添加特定格式的注释,它就可以从注释中为你生成 HTML 文档。

* REST Client(可牛逼了):测试http请求的一个插件可牛逼了。这儿不细说,配置不算难但具体实现每个人方法不一样。

* Version Lens(可牛逼了):相信大家在写项目的时候,可能开发1个月后,新的一些依赖module更新了,但是呢我们用着旧的版本想更新啥的。这个插件就是配合package.json来使用的。

beautify(美化插件,为了好看)

* Beautify(美化代码):覆盖面很大。推荐什么代码都有美化!不用写不了代码系列必需品Beautify 

* css/sass/scss/less(css美化代码):更好的美化css代码,建议安装。HTML 

* Snippets(html5代码片段):同样是个大家都懂得东西,必须的啊谁敢说自己把所有的html标签全给记住了。

* vscode-icons(vscode的icons):简单的小东西就是为你的文件配个icon,好看!安不安装小伙伴自己把。

Vue相关

* Vetur(vscode官方推荐的Vue插件):具体功能挺多自己看官方说明比较详细了

* Vue 2 Snippets(Vue代码片段):不用?你是想v-for、v-if都自己慢慢手打吗???

VSCode 校验&自动格式化&修复

//本人仅供参考
{
    "window.zoomLevel": 1,
    "workbench.iconTheme": "seti",
    "eslint.autoFixOnSave": true,
    "javascript.format.insertSpaceBeforeFunctionParenthesis": true, //让函数(名)和后面的括号之间加个空格
    "vetur.format.defaultFormatter.html": "js-beautify-html", //格式化.vue中html
    "vetur.format.defaultFormatter.js": "vscode-typescript", //让vue中的js按编辑器自带的ts格式进行格式化
    "vetur.format.defaultFormatterOptions": {
    "js-beautify-html": {
    "wrap_attributes": "force-aligned" //属性强制折行对齐
    }
    },
    "eslint.validate": [ //开启对.vue文件中错误的检查
    "javascript",
    "javascriptreact",
    {
    "language": "html",
    "autoFix": true
    },
    {
    "language": "vue",
    "autoFix": true
    }
    ],
    "[javascript]": {
        "editor.defaultFormatter": "HookyQR.beautify"
    },
    "explorer.confirmDelete": false,
    "[less]": {
        "editor.defaultFormatter": "HookyQR.beautify"
    },
    "[json]": {
        "editor.defaultFormatter": "HookyQR.beautify"
    },
    "[jsonc]": {
        "editor.defaultFormatter": "vscode.json-language-features"
    },
    "vsicons.dontShowNewVersionMessage": true,
    "explorer.confirmDragAndDrop": false,
    "diffEditor.ignoreTrimWhitespace": true,
    "vetur.format.defaultFormatter.ts": "vscode-typescript",
    "editor.renderWhitespace": "all",
    "editor.renderControlCharacters": false,
    "workbench.colorTheme": "Vue Theme",
    "editor.tabSize": 2,
    "javascript.updateImportsOnFileMove.enabled": "always",
    "[html]": {
        "editor.defaultFormatter": "HookyQR.beautify"
    },
    "[vue]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
    },
    "prettier.semi": false, //去掉代码结尾的分号
    "prettier.singleQuote": true, //使用单引号替代双引号
}

Vue项目开发规范 [Vue-cli Vue Eslint Webpack]


安装一个vue项目 基于vue

vue-cli3+、vue-cli4+ 相比于 vue-cli2+ 
最大的改变就是将约定俗称的配置,全部公共化了,也就是做了一次二次封装。这样的好处在于,我们不必要在繁多的配置代码中寻找需要的配置。
现在最新的版本应该是4.1.2 但是内容基本和cli3.x 一致。所以直接安装就可以。
不过还是建议看一下两个版本的修改的地方。
传送门:https://cli.vuejs.org/migrating-from-v3/

安装完cli后 执行创建项目命令

vue create test-project
建议选择手动配置
创建一个适合自己项目需要的一些配置文件

eslint 建议选择standard标准风格,这个风格也是目前使用人数最多的。

standard风格的规则,传送门: github.com/standard/st…

其实在cli3.x之后完全可以开启Git Hook。这样的话,如果git commit 之前不通过则不允许代码提交。

生成的文件目录

组件开发

删除无用目录或文件,保持项目目录及代码结构清晰

例如如果在init 项目的时候 创建了单元测试文件夹,无用的时候可以将其删除。 没有什么以后可能用的️着的情 况 无用的注释代码 旧的逻辑、样式、没有必要的console可以删除。并不一定要完全以来webpack 和 eslint 去配置

覆盖默认样式【样式重置 reset】

    /* http://meyerweb.com/eric/tools/css/reset/
       v2.0 | 20110126
       License: none (public domain)
    */
    
    html, body, div, span, applet, object, iframe,
    h1, h2, h3, h4, h5, h6, p, blockquote, pre,
    a, abbr, acronym, address, big, cite, code,
    del, dfn, em, img, ins, kbd, q, s, samp,
    small, strike, strong, sub, sup, tt, var,
    b, u, i, center,
    dl, dt, dd, ol, ul, li,
    fieldset, form, label, legend,
    table, caption, tbody, tfoot, thead, tr, th, td,
    article, aside, canvas, details, embed,
    figure, figcaption, footer, header, hgroup,
    menu, nav, output, ruby, section, summary,
    time, mark, audio, video {
     margin: 0;
     padding: 0;
     border: 0;
     font-size: 100%;
     font: inherit;
     vertical-align: baseline;
    }
    /* HTML5 display-role reset for older browsers */
    article, aside, details, figcaption, figure,
    footer, header, hgroup, menu, nav, section {
     display: block;
    }
    body {
     line-height: 1;
    }
    ol, ul {
     list-style: none;
    }
    blockquote, q {
     quotes: none;
    }
    blockquote:before, blockquote:after,
    q:before, q:after {
     content: '';
     content: none;
    }
    table {
     border-collapse: collapse;
     border-spacing: 0;
    }
    
    html, body {
      width: 100%;
      height: 100%;
      overflow: auto;
      margin: 0;
      scroll-behavior: smooth;
      -webkit-overflow-scrolling: touch;
    }

使用mixin样式工具

    @baseColor: #FF6300;
    @bgColor: #FFFFFF;
    @font-family: PingFangSC-Regular, PingFangSC;
    @font-family-1: PingFang-SC-Bold, PingFang-SC;
    @font-family-2: PingFangSC-Medium, PingFangSC;
    @font-family-3: PingFangSC-Light, PingFangSC;
    // .font-mixin(58px, @font-family-2, 500);
    
    .font-mixin(@size, @family, @weight) {
      font-size: @size;
      font-family: @family;
      font-weight: @weight;
    }
    
    //isurl('hetao101.com')  
    //ispixel(20px) 
    //iscolor(#fff) 
    //isstring('11') 
    //isnumber(1)  
    //ispercentage(20%)
    .mixin-bc(@url) when (isurl(@url)) {
      background: @url no-repeat;
      background-position: center 0px;
      background-size: 152px 152px;
    }

项目解构重构【readme文件的更新】

项目结构构建过程中,应该不断维护readme文档,并且建立项目结构版本,如:1.0.0,并且应该标注如下几点:
结构中新建与改动过程的动作说明
项目结构版本信息

文件夹命名规范

一般目录命名采用lowercase方式,,目录由多个单词组件则利用"-"连接,如:

router,

store,

components,

store/modules,

utils/plugin
views中路由视图目录采用大驼峰形式命名,如:

views/Algorithms

views/Events

views/Monitor

代码编写规范

良好的代码编写规范可以大幅度提高项目开发效率,增加代码的可读性/可维护性/可测试性,减少开发/重构等场景的复杂耗时程度。
我主要从 3 个方面来做一些编码效率上的改进
    升级你的 vue-cli 减少 webpack 配置的成本
    使用 less、stylus,利用里面函数、mixins、变量提升 css 文件的复用
    使用 pug,减少 html 的代码编写量

组件以驼峰命名

组件名应该始终是多个单词的,根组件 App 以及 <transition>、<component> 之类的 Vue 内置组件除外。

这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。
Vue.component('todo-item', {
  // ...
})
export default {
  name: 'TodoItem',
  // ...
}

<template>
  <my-components></my-components>
</template>
<script>
  import myComponents from './myComponents.vue'

  export default {
  components: {
      myComponents
    }
  }
</script>

组件按需加载

三种按需加载的方法vue异步resolve => require()、ES6提案的import()、webpack提供的require.ensure() 推荐使用第二种。

    // 下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。
    const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
    const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
    // 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
    // const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
    // const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
    export default new Router({
        routes: [
            {
                path: '/importfuncdemo1',
                name: 'ImportFuncDemo1',
                component: ImportFuncDemo1
            },
            {
                path: '/importfuncdemo2',
                name: 'ImportFuncDemo2',
                component: ImportFuncDemo2
            }
        ]
    })

组件数据

Vue.component('some-comp', {
  data: function () {
    return {
      foo: 'bar'
    }
  }
})
// In a .vue file
export default {
  data () {
    return {
      foo: 'bar'
    }
  }
}

Props定义

props: {
  status: String
}
// 更好的做法!
props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].indexOf(value) !== -1
    }
  }
}

为 v-for 设置键值

<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

避免 v-if 和 v-for 用在一起

<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

methods 里面一个方法尽量只做一件事,增强可读性和维护行。

    setLastNumber () {
      setInterval(() => {
        this.lastNumber = this.lastNumber > 1 ? this.lastNumber - 1 : 100
      }, 1000)
    },
    setLastTime () {
      let endT = Number(window.localStorage.getItem('httt'))
      const currentT = new Date().getTime()
      if (!endT || currentT >= endT) {
        endT = new Date().getTime() + 3 * 60 * 60 * 1000
        window.localStorage.setItem('httt', endT)
      }
      getCountDownTime({
        endTime: endT,
        timeCb: t => {
          this.lastTime = t
        },
        endCb: () => {
          this.setLastTime()
        }
      })
    }

为组件样式设置作用域

<!-- 使用 `scoped` 特性 -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>

组件引用

  import myComponentsA from './myComponentsA.vue'  
  import myComponentsB from './myComponentsB.vue'
  import myComponentsC from './myComponentsC.vue'
  import myComponentsD from './myComponentsD.vue'
  export default {
    components: {
      myComponentsA,
      myComponentsB,
      myComponentsC,
      myComponentsD,
    }
  }

事件

<!-- 不建议 -->
<a v-on:click="pass()">pass</a>

<!-- 推荐 -->
<a @click="pass">pass</a>

组件拆分(复用)

components/
|- TodoList.js
|- TodoItem.js
components/
|- TodoList.vue
|- TodoItem.vue

紧密耦合的组件名

反例
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue

完整的组件名

反例
components/
|- SdSettings.vue
|- UProfOpts.vue
好例子
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

多个特性的元素

反例
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>
好例子
<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>
<MyComponent
  foo="a"
  bar="b"
  baz="c"
/>

模版中的简单表达式 计算属性

不建议
{{
  fullName.split(' ').map(function (word) {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}

建议
<!-- 在模板中 -->
{{ normalizedFullName }}
// 复杂表达式已经移入一个计算属性
computed: {
  normalizedFullName: function () {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}
反例
computed: {
  price: function () {
    var basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0)
    )
  }
}
好例子
computed: {
  basePrice: function () {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount: function () {
    return this.basePrice * (this.discountPercent || 0)
  },
  finalPrice: function () {
    return this.basePrice - this.discount
  }
}

指令缩写

指令缩写 (用 : 表示 v-bind: 、用 @ 表示 v-on: 和用 # 表示 v-slot:) 应该要么都用要么都不用。
<input
  @input="onInput"
  @focus="onFocus"
>
<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>

vuex之store拆分即多模块状态管理

将store进行拆分,有利于我们更好的管理项目中的状态,以及使我们的项目维护更加加单高效。各个模块之间的开发互相不影响。
vuex使用尽量用modules把store分成模块,且为避免命名冲突问题建议开启module命名空间,目录如下:
|-store
    |-index.js
    |-modules
        |-moduleA
        |-moduleB
// mod1.js
 export default {
  state:{},
  mutatons:{},
  actions:{},
  getters:{}
}

// mod2.js
 export default {
  state:{},
  mutatons:{},
  actions:{},
  getters:{}
}

// 然后在将几个mod合并到store中去:
import vue from 'vue'
import vuex from 'vuex'
import mod1 from './mod1.js'
import mod2 from './mod2.js'
vue.use(vuex);
export default new vuex.Store({
    modules:{
          mod1:mod1,
          mod2:mod1
  }
 });

Eslint .eslintrc.js其实希望不要用到rules这个规则。


文件配置 [环境、全局变量、解析器、规则]

module.exports = {
  root: true,
  env: {
    browser: true
  },
  'extends': [
    'plugin:vue/essential',
    '@vue/standard'
  ],
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  },
  parserOptions: {
    parser: 'babel-eslint'
  },
  globals: {
    'sa': false,
    'pingpp_ui': false,
    'fundebug': false,
    'gdt': true,
    'meteor': true,
    'utq': true
  },
}

rules规则配置

.eslintrc.js文件是cli抛给开发者的配置eslint的文件入口。养成良好的开发习惯,并且按照standard风格开发,这一块其实完全不需要配置。也不建议配置

按照一种约定俗成的规则开发项目,也别是某个项目多人都介入的情况下,一致的开发规则能够减少冗余代码的堆积、提升工作效率。

{
"rules" : {
  // 定义对象的set存取器属性时,强制定义get
  "accessor-pairs": 2,
  // 指定数组的元素之间要以空格隔开(,后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
  "array-bracket-spacing": [2, "never"],
  // 在块级作用域外访问块内定义的变量是否报错提示
  "block-scoped-var": 0,
  // if while function 后面的{必须与if在同一行,java风格。
  "brace-style": [2, "1tbs", { "allowSingleLine": true }],
  // 双峰驼命名格式
  "camelcase": 2,
  // 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
  // always-multiline:多行模式必须带逗号,单行模式不能带逗号
  "comma-dangle": [2, "never"],
  // 控制逗号前后的空格
  "comma-spacing": [2, { "before": false, "after": true }],
  // 控制逗号在行尾出现还是在行首出现
  // http://eslint.org/docs/rules/comma-style
  "comma-style": [2, "last"],
  // 圈复杂度
  "complexity": [2,9],
  // 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
  "computed-property-spacing": [2,"never"],
  // 强制方法必须返回值,TypeScript强类型,不配置
  "consistent-return": 0,
  // 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
  // e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值
  "consistent-this": 0,
  // 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示
  "constructor-super": 0,
  // if else while for do后面的代码块是否需要{ }包围,参数:
  //    multi  只有块中有多行语句时才需要{ }包围
  //    multi-line  只有块中有多行语句时才需要{ }包围, 但是块中的执行语句只有一行时,
  //                   块中的语句只能跟和if语句在同一行。if (foo) foo++; else doSomething();
  //    multi-or-nest 只有块中有多行语句时才需要{ }包围, 如果块中的执行语句只有一行,执行语句可以零另起一行也可以跟在if语句后面
  //    [2, "multi", "consistent"] 保持前后语句的{ }一致
  //    default: [2, "all"] 全都需要{ }包围
  "curly": [2, "all"],
  // switch语句强制default分支,也可添加 // no default 注释取消此次警告
  "default-case": 2,
  // 强制object.key 中 . 的位置,参数:
  //      property,'.'号应与属性在同一行
  //      object, '.' 号应与对象名在同一行
  "dot-location": [2, "property"],
  // 强制使用.号取属性
  //    参数: allowKeywords:true 使用保留字做属性名时,只能使用.方式取属性
  //                          false 使用保留字做属性名时, 只能使用[]方式取属性 e.g [2, {"allowKeywords": false}]
  //           allowPattern:  当属性名匹配提供的正则表达式时,允许使用[]方式取值,否则只能用.号取值 e.g [2, {"allowPattern": "^[a-z]+(_[a-z]+)+$"}]
  "dot-notation": [2, {"allowKeywords": true}],
  // 文件末尾强制换行
  "eol-last": 2,
  // 使用 === 替代 ==
  "eqeqeq": [2, "allow-null"],
  // 方法表达式是否需要命名
  "func-names": 0,
  // 方法定义风格,参数:
  //    declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"]
  //    expression:强制使用方法表达式的方式,var f = function() {}  e.g [2, "expression"]
  //    allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", { "allowArrowFunctions": true }]
  "func-style": 0,
  "no-alert": 0,//禁止使用alert confirm prompt
  "no-array-constructor": 2,//禁止使用数组构造器
  "no-bitwise": 0,//禁止使用按位运算符
  "no-caller": 1,//禁止使用arguments.caller或arguments.callee
  "no-catch-shadow": 2,//禁止catch子句参数与外部作用域变量同名
  "no-class-assign": 2,//禁止给类赋值
  "no-cond-assign": 2,//禁止在条件表达式中使用赋值语句
  "no-console": 2,//禁止使用console
  "no-const-assign": 2,//禁止修改const声明的变量
  "no-constant-condition": 2,//禁止在条件中使用常量表达式 if(true) if(1)
  "no-continue": 0,//禁止使用continue
  "no-control-regex": 2,//禁止在正则表达式中使用控制字符
  "no-debugger": 2,//禁止使用debugger
  "no-delete-var": 2,//不能对var声明的变量使用delete操作符
  "no-div-regex": 1,//不能使用看起来像除法的正则表达式/=foo/
  "no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1}
  "no-dupe-args": 2,//函数参数不能重复
  "no-duplicate-case": 2,//switch中的case标签不能重复
  "no-else-return": 2,//如果if语句里面有return,后面不能跟else语句
  "no-empty": 2,//块语句中的内容不能为空
  "no-empty-character-class": 2,//正则表达式中的[]内容不能为空
  "no-empty-label": 2,//禁止使用空label
  "no-eq-null": 2,//禁止对null使用==或!=运算符
  "no-eval": 1,//禁止使用eval
  "no-ex-assign": 2,//禁止给catch语句中的异常参数赋值
  "no-extend-native": 2,//禁止扩展native对象
  "no-extra-bind": 2,//禁止不必要的函数绑定
  "no-extra-boolean-cast": 2,//禁止不必要的bool转换
  "no-extra-parens": 2,//禁止非必要的括号
  "no-extra-semi": 2,//禁止多余的冒号
  "no-fallthrough": 1,//禁止switch穿透
  "no-floating-decimal": 2,//禁止省略浮点数中的0 .5 3.
  "no-func-assign": 2,//禁止重复的函数声明
  "no-implicit-coercion": 1,//禁止隐式转换
  "no-implied-eval": 2,//禁止使用隐式eval
  "no-inline-comments": 0,//禁止行内备注
  "no-inner-declarations": [2, "functions"],//禁止在块语句中使用声明(变量或函数)
  "no-invalid-regexp": 2,//禁止无效的正则表达式
  "no-invalid-this": 2,//禁止无效的this,只能用在构造器,类,对象字面量
  "no-irregular-whitespace": 2,//不能有不规则的空格
  "no-iterator": 2,//禁止使用__iterator__ 属性
  "no-label-var": 2,//label名不能与var声明的变量名相同
  "no-labels": 2,//禁止标签声明
  "no-lone-blocks": 2,//禁止不必要的嵌套块
  "no-lonely-if": 2,//禁止else语句内只有if语句
  "no-loop-func": 1,//禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
  "no-mixed-requires": [0, false],//声明时不能混用声明类型
  "no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格
  "linebreak-style": [0, "windows"],//换行风格
  "no-multi-spaces": 1,//不能用多余的空格
  "no-multi-str": 2,//字符串不能用\换行
  "no-multiple-empty-lines": [1, {"max": 2}],//空行最多不能超过2行
  "no-native-reassign": 2,//不能重写native对象
  "no-negated-in-lhs": 2,//in 操作符的左边不能有!
  "no-nested-ternary": 0,//禁止使用嵌套的三目运算
  "no-new": 1,//禁止在使用new构造一个实例后不赋值
  "no-new-func": 1,//禁止使用new Function
  "no-new-object": 2,//禁止使用new Object()
  "no-new-require": 2,//禁止使用new require
  "no-new-wrappers": 2,//禁止使用new创建包装实例,new String new Boolean new Number
  "no-obj-calls": 2,//不能调用内置的全局对象,比如Math() JSON()
  "no-octal": 2,//禁止使用八进制数字
  "no-octal-escape": 2,//禁止使用八进制转义序列
  "no-param-reassign": 2,//禁止给参数重新赋值
  "no-path-concat": 0,//node中不能使用__dirname或__filename做路径拼接
  "no-plusplus": 0,//禁止使用++,--
  "no-process-env": 0,//禁止使用process.env
  "no-process-exit": 0,//禁止使用process.exit()
  "no-proto": 2,//禁止使用__proto__属性
  "no-redeclare": 2,//禁止重复声明变量
  "no-regex-spaces": 2,//禁止在正则表达式字面量中使用多个空格 /foo bar/
  "no-restricted-modules": 0,//如果禁用了指定模块,使用就会报错
  "no-return-assign": 1,//return 语句中不能有赋值表达式
  "no-script-url": 0,//禁止使用javascript:void(0)
  "no-self-compare": 2,//不能比较自身
  "no-sequences": 0,//禁止使用逗号运算符
  "no-shadow": 2,//外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
  "no-shadow-restricted-names": 2,//严格模式中规定的限制标识符不能作为声明时的变量名使用
  "no-spaced-func": 2,//函数调用时 函数名与()之间不能有空格
  "no-sparse-arrays": 2,//禁止稀疏数组, [1,,2]
  "no-sync": 0,//nodejs 禁止同步方法
  "no-ternary": 0,//禁止使用三目运算符
  "no-trailing-spaces": 1,//一行结束后面不要有空格
  "no-this-before-super": 0,//在调用super()之前不能使用this或super
  "no-throw-literal": 2,//禁止抛出字面量错误 throw "error";
  "no-undef": 1,//不能有未定义的变量
  "no-undef-init": 2,//变量初始化时不能直接给它赋值为undefined
  "no-undefined": 2,//不能使用undefined
  "no-unexpected-multiline": 2,//避免多行表达式
  "no-underscore-dangle": 1,//标识符不能以_开头或结尾
  "no-unneeded-ternary": 2,//禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
  "no-unreachable": 2,//不能有无法执行的代码
  "no-unused-expressions": 2,//禁止无用的表达式
  "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],//不能有声明后未被使用的变量或参数
  "no-use-before-define": 2,//未定义前不能使用
  "no-useless-call": 2,//禁止不必要的call和apply
  "no-void": 2,//禁用void操作符
  "no-var": 0,//禁用var,用let和const代替
  "no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],//不能有警告备注
  "no-with": 2,//禁用with
  "array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格
  "arrow-parens": 0,//箭头函数用小括号括起来
  "arrow-spacing": 0,//=>的前/后括号
  "accessor-pairs": 0,//在对象中使用getter/setter
  "block-scoped-var": 0,//块语句中使用var
  "brace-style": [1, "1tbs"],//大括号风格
  "callback-return": 1,//避免多次调用回调什么的
  "camelcase": 2,//强制驼峰法命名
  "comma-dangle": [2, "never"],//对象字面量项尾不能有逗号
  "comma-spacing": 0,//逗号前后的空格
  "comma-style": [2, "last"],//逗号风格,换行时在行首还是行尾
  "complexity": [0, 11],//循环复杂度
  "computed-property-spacing": [0, "never"],//是否允许计算后的键名什么的
  "consistent-return": 0,//return 后面是否允许省略
  "consistent-this": [2, "that"],//this别名
  "constructor-super": 0,//非派生类不能调用super,派生类必须调用super
  "curly": [2, "all"],//必须使用 if(){} 中的{}
  "default-case": 2,//switch语句最后必须有default
  "dot-location": 0,//对象访问符的位置,换行的时候在行首还是行尾
  "dot-notation": [0, { "allowKeywords": true }],//避免不必要的方括号
  "eol-last": 0,//文件以单一的换行符结束
  "eqeqeq": 2,//必须使用全等
  "func-names": 0,//函数表达式必须有名字
  "func-style": [0, "declaration"],//函数风格,规定只能使用函数声明/函数表达式
  "generator-star-spacing": 0,//生成器函数*的前后空格
  "guard-for-in": 0,//for in循环要用if语句过滤
  "handle-callback-err": 0,//nodejs 处理错误
  "id-length": 0,//变量名长度
  "indent": [2, 4],//缩进风格
  "init-declarations": 0,//声明时必须赋初值
  "key-spacing": [0, { "beforeColon": false, "afterColon": true }],//对象字面量中冒号的前后空格
  "lines-around-comment": 0,//行前/行后备注
  "max-depth": [0, 4],//嵌套块深度
  "max-len": [0, 80, 4],//字符串最大长度
  "max-nested-callbacks": [0, 2],//回调嵌套深度
  "max-params": [0, 3],//函数最多只能有3个参数
  "max-statements": [0, 10],//函数内最多有几个声明
  "new-cap": 2,//函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用
  "new-parens": 2,//new时必须加小括号
  "newline-after-var": 2,//变量声明后是否需要空一行
  "object-curly-spacing": [0, "never"],//大括号内是否允许不必要的空格
  "object-shorthand": 0,//强制对象字面量缩写语法
  "one-var": 1,//连续声明
  "operator-assignment": [0, "always"],//赋值运算符 += -=什么的
  "operator-linebreak": [2, "after"],//换行时运算符在行尾还是行首
  "padded-blocks": 0,//块语句内行首行尾是否要空行
  "prefer-const": 0,//首选const
  "prefer-spread": 0,//首选展开运算
  "prefer-reflect": 0,//首选Reflect的方法
  "quotes": [1, "single"],//引号类型 `` "" ''
  "quote-props":[2, "always"],//对象字面量中的属性名是否强制双引号
  "radix": 2,//parseInt必须指定第二个参数
  "id-match": 0,//命名检测
  "require-yield": 0,//生成器函数必须有yield
  "semi": [2, "always"],//语句强制分号结尾
  "semi-spacing": [0, {"before": false, "after": true}],//分号前后空格
  "sort-vars": 0,//变量声明时排序
  "space-after-keywords": [0, "always"],//关键字后面是否要空一格
  "space-before-blocks": [0, "always"],//不以新行开始的块{前面要不要有空格
  "space-before-function-paren": [0, "always"],//函数定义时括号前面要不要有空格
  "space-in-parens": [0, "never"],//小括号里面要不要有空格
  "space-infix-ops": 0,//中缀操作符周围要不要有空格
  "space-return-throw-case": 2,//return throw case后面要不要加空格
  "space-unary-ops": [0, { "words": true, "nonwords": false }],//一元运算符的前/后要不要加空格
  "spaced-comment": 0,//注释风格不要有空格什么的
  "strict": 2,//使用严格模式
  "use-isnan": 2,//禁止比较时使用NaN,只能用isNaN()
  "valid-jsdoc": 0,//jsdoc规则
  "valid-typeof": 2,//必须使用合法的typeof的值
  "vars-on-top": 2,//var必须放在作用域顶部
  "wrap-iife": [2, "inside"],//立即执行函数表达式的小括号风格
  "wrap-regex": 0,//正则表达式字面量用小括号包起来
  "yoda": [2, "never"]//禁止尤达条件
  }
}

.eslintignore

【功能与 .gitignore类似 对某些文件或者文件夹忽略】
/build/
/config/
/dist/
/*.js
/test/unit/coverage/

//不建议
/src/service/
/src/utils/
/src/components/sharelandingpage/
/src/router/
/src/main.js
/src/pages/common/Protocol.vue

浏览器兼容性

兼容的浏览器版本 .browserslistrc

//你会发现有 package.json 文件里的 browserslist 字段 (或一个单独的 .browserslistrc 文件),指定了项目的目标浏览器的范围。
//这个值会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀

> 1%
last 2 versions

语法兼容 babel.config.js

Vue Cli 3已经开始使用babel.config.js,而不是.babelrc
默认情况下,cli会把 useBuiltIns: 'usage' 传递给 @babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill
这确保了最终包里 polyfill 数量的最小化。但是如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来。我们可以通过如下三种方式解决此类问题:

* 将依赖添加到 vue.config.js 中的 transpileDependencies 选项// vue.config.js
    module.exports = {
        // 默认情况下 babel-loader 会忽略所有 node_modules 中的文件。如果你想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来
        transpileDependencies: 'glob'
    }
    // babel.config.js
    module.exports = {
      presets: [
        ['@vue/app', {
          polyfills: 
            'es6.promise',
            'es6.symbol'
          
        }]
      ]
    }
* 可以使用 @vue/babel-preset-app 的 polyfills 选项预包含所需要的 polyfill
* 使用 useBuiltIns: 'entry' 然后在入口文件添加 import '@babel/polyfill',这种方式的问题就是会增加包的大小
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
    plugins: [
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
      }
    ]
    ]
}

移动端样式兼容 postcss.config.js

//咱们项目中普遍通用的移动端兼容的方法。
module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-url': {},
    'postcss-preset-env': {},
    'cssnano': {},
    // to edit target browsers: use "browserslist" field in package.json
    autoprefixer: {},
    'postcss-px-to-viewport': {
      viewportWidth: 750, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750
      viewportHeight: 1334, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
      unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
      viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
      selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
      minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
      mediaQuery: false // 允许在媒体查询中转换`px`
    }
  }
}

Webpack 配置


vue cli 3.x之后的版本 webpack的配置默认使用 vue.config.js 中的configureWebpack & chainWebpack

const path = require("path");
const resolve = dir => path.join(__dirname, dir);
//用于生产环境去除多余的css
const PurgecssPlugin = require("purgecss-webpack-plugin");
//全局文件路径
const glob = require("glob-all");
//压缩代码并去掉console
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
//代码打包zip
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
module.exports = {
  // 废弃baseUrl  一般运维会配置好的
  publicPath: process.env.NODE_ENV === "production" ? "/configtest/" : "/",
  //打包的输出目录
  outputDir: "dist/configtest",
  //保存是校验
  lintOnSave: true,
  //如果文件等设置
  pages: {
    index: {
      entry: "src/main.js",
      template: "public/index.html",
      filename: "index.html"
    }
  },
  //静态资源打包路径
  assetsDir: "static",
  //默认false 可以加快打包
  productionSourceMap: false,
  //打包后的启动文件
  indexPath: "congfigtest.html",
  //打包文件是否使用hash
  filenameHashing: true,
  runtimeCompiler: false,
  transpileDependencies: [],
  //打包的css路径及命名
  css: {
    modules: false,
    //vue 文件中修改css 不生效 注释掉  extract:true
    extract: {
      filename: "style/[name].[hash:8].css",
      chunkFilename: "style/[name].[hash:8].css"
    },
    sourceMap: false,
    loaderOptions: {
      css: {},
      less: {
        // 向全局less样式传入共享的全局变量
        // data: `@import "~assets/less/variables.less";$src: "${process.env.VUE_APP_SRC}";`
      },
      // postcss 设置
      postcss: {
        plugins: [
          require("postcss-px-to-viewport")({
            viewportWidth: 750, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750
            viewportHeight: 1334, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
            unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
            viewportUnit: "vw", // 指定需要转换成的视窗单位,建议使用vw
            selectorBlackList: [".ignore", ".hairlines"], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
            minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
            mediaQuery: false // 允许在媒体查询中转换`px`
          })
        ]
      }
    }
  },
  //webpack 链式配置   默认已经配置好了  node_moudles/@vue
  chainWebpack: config => {
    // 修复HMR
    config.resolve.symlinks(true);
    // 修复Lazy loading routes  按需加载的问题,如果没有配置按需加载不需要写,会报错
    // config.plugin("html").tap(args => {
    //   args[0].chunksSortMode = "none";
    //   return args;
    // });
    //添加别名
    config.resolve.alias
      .set("@", resolve("src"))
      .set("assets", resolve("src/assets"))
      .set("components", resolve("src/components"))
      .set("layout", resolve("src/layout"))
      .set("base", resolve("src/base"))
      .set("static", resolve("src/static"));
    // 压缩图片
    config.module
      .rule("images")
      .use("image-webpack-loader")
      .loader("image-webpack-loader")
      .options({
        mozjpeg: { progressive: true, quality: 65 },
        optipng: { enabled: false },
        pngquant: { quality: "65-90", speed: 4 },
        gifsicle: { interlaced: false },
        webp: { quality: 75 }
      });
  },
  //webpack 配置
  configureWebpack: config => {
    const plugins = [];
    //去掉不用的css 多余的css
    plugins.push(
      new PurgecssPlugin({
        paths: glob.sync([path.join(__dirname, "./**/*.vue")]),
        extractors: [
          {
            extractor: class Extractor {
              static extract(content) {
                const validSection = content.replace(
                  /<style([\s\S]*?)<\/style>+/gim,
                  ""
                );
                return validSection.match(/[A-Za-z0-9-_:/]+/g) || [];
              }
            },
            extensions: ["html", "vue"]
          }
        ],
        whitelist: ["html", "body"],
        whitelistPatterns: [/el-.*/],
        whitelistPatternsChildren: [/^token/, /^pre/, /^code/]
      })
    );
    //启用代码压缩
    plugins.push(
      new UglifyJsPlugin({
        uglifyOptions: {
          compress: {
            warnings: false,
            drop_console: true,
            drop_debugger: false,
            pure_funcs: ["console.log"] //移除console
          }
        },
        sourceMap: false,
        parallel: true
      })
    ),
      //代码压缩打包
      plugins.push(
        new CompressionWebpackPlugin({
          filename: "[path].gz[query]",
          algorithm: "gzip",
          test: productionGzipExtensions,
          threshold: 10240,
          minRatio: 0.8
        })
      );
    config.plugins = [...config.plugins, ...plugins];
  },
  parallel: require("os").cpus().length > 1,
  pluginOptions: {},
  pwa: {},
  //设置代理
  devServer: {
    port: 8080,
    host: "0.0.0.0",
    https: false,
    open: true,
    openPage: "about",
    hot: true,
    disableHostCheck: true,
    proxy: {
      "/api": {
        target: "https://cdn.awenliang.cn",
        ws: true,
        changeOrigin: true
      },
      "/foo": {
        target: "https://cdn.awenliang.cn",
        ws: true,
        changeOrigin: true
      }
    }
  }
};