美文网首页
为什么我的代码让别人看起头发麻?

为什么我的代码让别人看起头发麻?

作者: 880d91446f17 | 来源:发表于2018-11-06 16:50 被阅读14次

    面试官:谈谈你们项目当中的前端代码规范吧?

    自己先想一分钟。

    为什么我的代码让别人看起头发麻?

    前面的话

    有些同学在开发某个新功能时根据需求就哐哐哐(按照自己的代码风格)一顿撸。写完发现,另一个地方也有这个模块功能,可能只是标题的颜色,字体大小不对。怎么办? 于是很鸡贼的复制粘贴过去,改吧改吧,提交代码,万事大吉!自己倒是爽了,功能是按照需求如期完成了啊,没毛病。可是你却忽视了一件很重要的东西: 团队

    记住,你不是一个人在写代码。

    这篇文章有别于其他教程类的文章,不是教你如何制定代码规范,也不是告诉你这样写就是错的亦或说是正确的。本文是我这些天从优化别人代码过程中的所见和所得,凝结成文。旨在分享给大家,对号入座,然后改之。 三人行,必有我师!;择其善者而从之,其不善者而改之。 由于我是做前端的,所以只说前端代码规范,其他语言同样适用!

    目的

    把一些常见的错误的不良的代码示例分享给大家,希望有的改之,无则加勉。看完之后,希望对你们有所帮助,提高自己的代码质量,每个人都能写出一手漂亮的代码。这是这篇文章最大的目的了!

    概述

    本文将以我的亲身项目经历为例,来谈谈我们日常开发当中,就代码层面来讲,我们应该注意的一些小细节。希望各位看客能吸取精华去其糟粕。主要涉及的方面有:

    • 项目结构
    • 文件命名
    • 路由
    • Vue 组件
    • JavaScript
    • Html
    • Css
    • Git 代码提交

    我将会从以上几个方面逐一枚举和大家分享讨论。

    枚举

    1. 项目结构

    没说之前,您不妨看下自己的项目结构是什么样的。目前我们的项目结构是这样的:

    my-project
    ├── .idea # 这个是编辑器生成的
    ├── build # Webpack 配置文件放在这里
    ├── config # Vue 基本配置文件放在这里
    ├── node_modules # 第三方依赖
    ├── src # 项目源码(核心文件)
    │ ├── assets # 资源文件(js, css, scss)
    │ ├── components # 所有组件
    │ ├── js # 自己写的 js,里面各种工具类方法等
    │ ├── mixins # 混合
    │ ├── router # 路由
    │ ├── vuex # 状态管理
    │ ├── App.vue # 根组件
    │ └── main.js # 入口文件
    ├── static # 静态资源,一般放 img
    ├── theme # 主题文件,修改的 Element-UI 主题
    ├── .babelrc # babel 编译配置
    ├── .editorconfig # 代码格式
    ├── .gitignore # Git 提交忽略的文件配置
    ├── .postcssrc.js # 转换 css 的工具配置文件
    ├── element-variables.css # Element 全局定义的变量,不明白为啥放这儿
    ├── index.html # 主页模板
    ├── package-lock.json # 用来锁定依赖的版本号(NPM 自动生成)
    ├── package.json # 项目基本信息
    └── README.md # 项目介绍

    </pre>

    都是用vue-cli 生成的,目录结构和命名规范也就没啥可说的。可能随着时间的推移,自己会在项目里加一些东西(文件或文件夹)。拿上面我们的项目为例来说几点吧:

    • 根目录下不要有 css 文件 :比如 element-variables.css 文件,它属于 theme 文件夹下的东西,应该放它下面。
    • **js 文件夹应该命名为 utils **:因为它对外暴露的都是工具类方法,这样更显语义化。

    关于项目结构,我发现的就这么多。每个项目的目录可能会不同,这个就看你们的规范了。

    2. 文件的命名

    它包含文件的命名和文件夹的命名。依我们的项目为例,我重点说下 src/components 目录下的命名,真的是五花八门:

    2.1. 文件名不够语义化

    为什么我的代码让别人看起头发麻?

    这个还算正常,但还是有些问题。这里是一些问题清单:

    • 这个模块的中文叫,是关于机器人学习的,叫 knowledgeBaseManagement 虽然很好的翻译了中文意思,但总觉的有点长,叫 robot 会不会好些?
    • 而且文件夹下的文件命名也不够语义化

    下面是整理过后的样子:

    robot
    ├── addQuestion.vue
    ├── editQuestion.vue
    ├── index.vue
    └── missedQuestion.vue

    </pre>

    为什么我的代码让别人看起头发麻?

    这个我就不想说了,看的我头皮发麻。从字面意思上来讲,我就认识一个 TreeNode2.vue 。后面还加个 2 是什么鬼?

    2.2. 文件目录不统一

    为什么我的代码让别人看起头发麻? 为什么我的代码让别人看起头发麻? 为什么我的代码让别人看起头发麻?

    这属于一类问题,即里面太乱了,不统一,问题清单:

    • src/components/moduleName/ 下除了子模块外,尽量不要瞎放其他无关的文件夹,如上面的 src 、 component/common 、 top
    • callcenterList/src 下的图片可以放到 static 下
    • 如果是功能型组件(上面的 color 是一个颜色选择器组件),尽量放到一个叫 package 或者 lib 的文件夹下。因为 src/components 下的模块都是系统模块,不要混淆
    • elvesSetting/top 如果是某几个页面头部的公共部分尽量放到 components/common 下

    2.3. 文件名过长

    为什么我的代码让别人看起头发麻?

    如果一个模块下就一个文件,尽量写成 index.vue 。这里文件夹和文件同名,路由是不是很长?你在其他文件中 import 的时候是不是也不方便? 而且我发现 problemManagement 和 problemRetrieve 都属于问题管理模块,完全可以合并到一个文件夹里啊。还有,文件夹已经表明是问题管理模块了,所以文件就不要再以 problem*** 开头了。不觉得啰嗦吗? 下面是整理过后的样子:

    problemManagement
    │ ├── index.vue
    │ ├── retrieve.vue
    qualityCheckAppeal
    └── index.vue

    </pre>

    3. 路由

    我们系统里的路由都是一级路由。举个栗子:

    userManagement
    ├── add.vue
    └── update.vue

    </pre>

    用户管理下有增改两个功能,不使用弹框去做的前提下,假如说 add 和 update 对应两个路由是 /addUser , /updateUser 。我们系统地址栏是这样显示的:

    // 增加用户
    localhost:3030/addUser
    // 修改用户
    localhost:3030/updateUser?id=1

    </pre>

    虽然地址栏路有短看起来虽然让人舒服,但是模块多的话,就不容易区分,其实应该这样做:
    // 增加用户
    localhost:3030/user/add
    // 修改用户
    localhost:3030/user/update?id=1
    ...
    // 总结
    localhost:3030/module/function?queryString

    </pre>

    4. Vue 组件

    关于 Vue 组件开发规范可以参考官方的风格指南。下面是我们项目的一些问题清单和改正意见,我列举一下作为对照:

    • 不要在 App.vue 中直接修改第三方样式(比如: ElementUI )。请使用外部文件导入:

    App.vue 文件:


    ...
    <style>
    .el-input__icon {
    cursor: pointer
    }
    </style>

    ...
    <style>
    @import 'element-style-overwrite';
    ...
    </style>

    </pre>

    _element-style-overwrite.scss 外部样式文件:

    .el-input__icon {
    cursor: pointer
    }

    </pre>

    • 给每个组件起个名字是个好习惯。例如 Dialog 组件:

    // incorrect
    export default {
    ...
    }
    // correct
    export default {
    name: 'MyDialog', // 以大驼峰命名
    ...
    }

    </pre>

    • 给组件样式设置作用域 scoped

    如果你在某个子组件中修改了全局样式,本来只想在该组件中使用,没想到造成了全局污染。等进行代码 review 的时候是很难排查的。

    例如,用户管理( UserManagement.vue )组件:

    <style scoped>
    ...
    </style>

    </pre>

    • 组件名要么单词大写开头 (PascalCase),要么横线连接(kebab-case):

    // incorrect
    components/
    └── mycomponent.vue
    components/
    └── myComponent.vue
    // correct
    components/
    └── MyComponent.vue
    // 或者
    components/
    └── my-component.vue

    </pre>

    • .vue 单文件中的 <template> 、 <script> 、 <style> 标签的顺序问题

    有的人喜欢这样写:

    <style>...</style>
    <template>...</template>
    <script>...</script>

    </pre>

    也有人喜欢这样写:

    <script>...</script>
    <style>...</style>
    <template>...</template>

    </pre>

    如果你想写,那好,不阻拦,拜托你统一下行不?别这个组件这个顺序,那个组件那个顺序。累不累? 这里我强力推荐大家按照官方的写法,即下面的顺序来写:

    </template>
    <script>...</script>
    <style scoped>...</style>

    </pre>

    • 使用两个空格(space)进行缩进

    这个放在全局规范会比较好一些。为什么是两个空格? 大神们都是这样做的!而且更重要的是,使用两个空格开发项目,传到 github 或者 gitlab 上排版会很好看。什么?不会设置?百度啊!你用的什么编辑器就查这个编辑器怎么设置的。

    一般是统一把全局规范设置放到一个叫.editorconfig 的文件夹里,有的编辑器支持这个文件,比如: webstorm 。有的则不支持,对于不支持的编辑器,可以下载安装 editorConfig 插件,如: atom 、 sublime 、 vscode 等。

    • 代码中不用的注释都删掉
    • 调试结束,把不用的 console.log(...) 及时删掉,它会影响性能
    • data 中的属性命名和初始化问题

    // incorrect
    export default {
    data () {
    return {
    text: 'wwwwwwww', // 这是啥?
    editBoxId: null, // 很明显Id是String,这里他初始化一个 null
    flag: '', // 这个表示的啥?看意思应该是个 Boolean 类型,为啥弄个 String ?
    pSize: 10, // pSize 是啥?
    cPage: 1, // cPage 是啥?
    popCsr:true, // popCsr 是啥,恐怕现在连那个开发者自己都不知道了吧
    callcenterAuthority: false, // 这么长你告诉是一个 Boolean 类型的
    }
    }
    }
    // correct
    export default {
    data () {
    return {
    text: '', // 'wwwwwwww' 没卵用删掉
    editBoxId: -1, // 它应该是个 Number 类型
    flag: false, // 它应该是个 Boolean 类型啊
    pageSize: 10, // pSize -> pageSize 多好
    currentPage: 1, // 完整写法更易懂,不是吗?
    isPopcsr: true, // Boolean 类型的总是前面加个 is
    isAuthority: false, // 是否授权。
    }
    }
    }

    </pre>

    其实还有好多问题,我就不一一列举了。诸如此类的问题,希望各位看客们都能吸取精华,去其糟粕。

    • Props 中的属性声明要明确类型

    // incorrect
    export default {
    props: ['node', 'size']
    }
    // correct
    export default {
    props: {
    node: Object, // 对象
    size: [String, Number], // 两种类型都可以
    }
    }

    </pre>

    • Vue 生命周期函数按顺序放在 methods 之前

    为什么说这个呢? 我们项目中有的组件就 methods 中的代码就上千行。如果生命周期函数放在 methods 之后,拉来拉去非常不方便:

    // incorrect
    export default {
    ...
    created () {},
    methods: {
    // 省略 1000 行代码
    // ...
    },
    mounted () {},
    beforeDestroy () {},
    destroy () {},
    }
    // correct
    export default {
    ...
    created () {},
    mounted () {},
    beforeDestroy () {},
    destroy () {},
    methods: {
    // 省略 1000 行代码
    // ...
    }
    }

    </pre>

    • Vue 组件中的 this 赋值要统一

    代码中,有时候我们需要把 this 赋给一个变量,你要么统一赋值给变量 vm ,要么统一赋值给变量 self 。别一个组件里,变来变去。

    // incorrect
    export default {
    ...
    methods: {
    one () {
    let vm = this
    },
    two () {
    let self = this
    }
    }
    }
    // incorrect
    export default {
    ...
    methods: {
    one () {
    let vm = this
    // 或者
    let self = this
    },
    two () {
    let vm = this
    // 或者
    let self = this
    }
    }
    }

    </pre>

    • Vue 组件中 Html 如果过长,请换行


    <el-input v-model="ruleForm.maskInput" size="small" class="nodeIpt" :icon="ruleForm.maskInput ? 'circle-close':''" @click="ruleForm.maskInput = ''" @keyup.enter.native="nodesure(event,'ruleForm')"></el-input> <el-input v-model="ruleForm.maskInput" size="small" class="nodeIpt" :icon="ruleForm.maskInput ? 'circle-close':''" @click="ruleForm.maskInput = ''" @keyup.enter.native="nodesure(event,'ruleForm')">
    </el-input>

    </pre>

    • Vue 中监听的事件记得垃圾回收

    举个例子,如果我们在 Vue 组件的 created 声明周期钩子中监听了一个点击事件,那么,当组件销毁(beforeDestroy)之前记得把这个事件释放,看代码:

    export default {
    ...
    created () {
    document.addEventListener('click', this.handleClick)
    },
    beforeDestroy () {
    document.removeEventListener('click', this.handleClick)
    }
    }

    </pre>

    • Vue 组件中不要直接操作异步请求(axios)

    把所有的异步请求方法封装成一个独立 js 文件,或者放到 Vuex 中,千万不要耦合到 Vue 组件中。因为代码量太多,会加重组件的后期维护,各司其职不好吗?

    不好的范例:

    // User.vue
    export default {
    ...
    mounted () {
    this.getUsers()
    },
    methods: {
    getUsers () {
    this.axios(url, data, (response) => {
    // Do something
    }).catch(err => {
    console.error(err)
    })
    }
    }
    }

    </pre>

    如果项目比较小还好,我没意见,如果项目较复杂,千万别这么干。下面是推荐的做法:

    // server.js
    // 专门处理数据请求的文件,也就是我没常说的MVC中的 M 层
    import axios from 'axios'
    export default {
    /**

    • 获取用户列表
      */
      getUsers (url, data) {
      return axios.get(url, data)
      }
      }
      // User.vue
      import api from '@/api/server.js'
      export default {
      ...
      data () {
      return {
      users: null
      }
      },
      mounted () {
      api.getUsers((response) => {
      this.users = response.data.data
      }).catch(err => {
      console.log(err)
      })
      }
      }

    </pre>

    5. JavaScript

    下面所有的错误代码示例都是从我们的项目中发现的,捡主要的列出来一些。希望犯同样错误的你能及时改正哦~

    • 变量命名

    要语义化命名
    // incorrect
    var a = document.getElementById(this.lastid) // 这里的 a
    var aa = true // 这是啥你们知道吗?
    // corrent
    let orderId = this.order.id
    let currentTime = Date.now()

    </pre>

    • 多个单词要驼峰命名

    // incorrent
    vm.timedefault = timedvalue
    vm.currentsessionid = id
    // corrent
    vm.timeDefault = timedValue
    vm.currentSessionId = id

    </pre>

    • 变量要加注释
    为什么我的代码让别人看起头发麻?

    上面那一坨你们知道啥意思吗?如果这个开发人员离职了,那可是坑了后来人了。所以,做开发不能自己爽了,做一个帅气和代码于一身的工程师,难道不更好吗?

    • 不要重复使用 var 声明变量

    // incorrect
    var name = 'test';
    var age = 12;
    var hobby = 'sport';
    // correct
    var name = 'test',
    age = 12,
    hobby = 'sport';

    </pre>

    • = 或 == 之间要保留一个空格

    错误的范例:

    // 变量
    var name='test'
    var arr=[]
    var obj={
    id:1
    }
    // if 判断
    if(this.id==currentId){
    // Do something
    }
    // for 循环
    for(let i=0;i<arr.length;i++){
    // Do something

    </pre>

    上面三种情况是最常见的,其他雷同。下面是正确的范例:
    // 变量
    var name = 'test'
    var arr = []
    var obj = {
    id: 1
    }
    // if 判断
    if(this.id == currentId) {
    // Do something
    }
    // for 循环
    for(let i = 0; i < arr.length; i++) {
    // Do something
    }

    </pre>

    • 右括号 ) 遇到 左大括号 { 时要空一格

    下面是错误的范例:

    // if
    if(a === b){
    // Do something
    }
    // for
    for(let i = 0; i < arr.length; i++){...}
    // 函数
    var T = function(params){
    ...
    }

    </pre>

    常见的几种情况,其他情况不再列举。下面是正确的范例:

    // if
    if (a === b) {
    // Do something
    }
    // for
    for (let i = 0; i < arr.length; i++) {...}
    // 函数
    var T = function(params) {
    ...
    }

    </pre>

    • 一定要进行非空判断处理

    在我们项目里,有人这样写:

    // 假如 Vue 组件中有一个叫 userId 的 data 属性
    if (userId != '' || userId != null || userId != undefined) {
    // ...
    }

    </pre>

    真不知道怎么想的,简单一句话不就搞定了吗?

    if (userId) {
    // ...
    }

    </pre>

    • 对象声明不当问题

    不要用下面的方式之一去声明一个对象:

    // incorrect
    var arr = new Array() // 数组
    var arr = '' // 虽然 js 是弱类型,也不能这样声明
    var obj = new object() // 对象
    var obj = ''

    </pre>

    • 异常处理问题

    我们在处理异步请求的时候,一定要对 response 中的数据进行异常处理,不然控制台回报 response.data is not undefined ,我们项目我看了下,有些地方没做处理,结果在做测试的时候,浏览器控制台一顿报错。那叫一个难看啊!
    // incorrect
    this.axios(url, data, (response) => {
    let result = response.data.data
    })
    // correct
    this.axios(url, data, (response) => {
    if (response.data && response.data.code === 1) {
    let result = response.data.data
    }
    }).catch(err => {
    console.error(err)
    })

    • 如果这个取值过长且多次用到,请赋给一个变量

    export default {
    ...
    methods: {
    handleClick (evt) {
    // incorrect
    evt.target.parentNode.innerHTML = 'test'
    evt.target.style.width = '100px'
    evt.target.style.height = '200px'

    // correct
    let target = evt.target
    target.parentNode.innerHTML = 'test'
    target.style.width = '100px'
    target.style.height = '200px'
    }
    }
    }
    6. HTML
    </pre>

    • 正确的使用标签

    项目中我见有人写个按钮居然用 span 标签,或者一个 div 。

    下面是错误的范例:

    // 用 div 当按钮
    <div class="btn">搜索</div>
    // 在 span 里 嵌套 el-input 组件
    // 这样做的同学,肯定不知道 el-input 编译后的代码是啥样的!
    <span>
    <el-input></el-input>
    </span>
    // 用 label 当标题
    // label 标签是配合表单使用的
    <label>标题</label>
    // 加粗字体没有用原生标签
    <span class="bold">我是加粗字体</span>

    </pre>

    下面是改正后的范例:

    // 用 H5 的 button
    <button class="btn">搜索</button>
    // 如果要包含 el-input 组件请使用块级元素,并加上合适的 class
    <div class="el-input__wrapper">
    <el-input></el-input>
    </div>
    // h1-h6 才是标题的正确打开方式
    <h2>标题</h2>
    // 加粗字体请使用原生标签
    // 然后使用 class 控制字体样式
    <strong class="bold">我是加粗字体</strong>

    </pre>

    • 所有的按钮,超链接,鼠标的 :hover 状态都应该是手形。

    a, button {
    cursor: pointer
    }

    </pre>

    • id 和 class 或者其他的属性,命名要语义化

    不要命个名只有你自己知道。这样会带来后期维护困难。


    <div class="dfdf">
    <el-form class="loginForm">...</el-form>
    </div>

    <div class="login-form__wrapper">
    <el-form class="loginForm">...</el-form>
    </div>

    </pre>

    • 把代码缩进改成 2 个空格
    • Html 中的属性之间保留一个空格距离


    <el-input v-model="form.loginUser" size="small" placeholder="请输入用户名"></el-input>



    <el-input v-model="form.loginUser" size="small" placeholder="请输入用户名"></el-input>

    </pre>

    • 每个代码快尽量加上注释

    代码量少尚且不说,如果一个 .vue 文件很长的话,找起来就很痛苦了。你还别说,我们项目里就是这样没注释。

    <template>
    <div class="user-managerment__wrapper">

    <div class="header">...</div>


    <div class="user-table__wrapper">
    <el-table>...</el-table>
    </div>


    <div class="add-user__dialog">
    <el-dialog title="新增用户">...</el-dialog>
    </div>
    </div>
    </template>

    </pre>

    7. CSS

    • { 和选择器保持一个空格距离

    .selector {
    ...
    }

    </pre>

    • 给每个样式模块加上注释有助于区分

    // Global style
    html, body, a, div {
    margin: 0
    }
    // Login style
    .login button {
    ...
    }
    // User manager style
    .user-manager__wrapper {
    ...
    }

    </pre>

    • 每个独立样式间保留一行距离

    见上面的示例

    • 选择器不要嵌套太多层级

    嵌套太多层级会影响性能,尽量保证在三层以下:

    // incorrect
    .user-management .user-box .user-form .el-form-item .remark {
    color: #42b983
    }
    // correct
    .user-management .user-form .remark {
    color: #42b983
    }

    </pre>

    8. Git 代码提交

    大家可以看我沸点。同事写的注释。希望有问题的同学可以及时改正哦。另外,关于 Git 如何正确的写好注释 。这里有篇文章讲的很好,大家可以看看。传送门 ->

    下面举个例子,比如我这次在用户管理模块中修改了两个 bug。如何以清单的方式提交呢? 看代码:

    add file

    git add src/components/userManager/index.vue

    commit

    git commit -m 'fix: 用户管理模块bug修改。
    清单:

    • 修改了列表分页的bug
    • 修改了当用户点击编辑按钮弹框无法显示的bug
      '

    push code

    git push

    </pre>

    你千万别用下面的方式之一去提交你的代码说明:

    说一些毫无意义的内容

    git commit -m "fix: ok!"

    or 不加 fix、feat、refactor、doc、style等前缀

    为什么要加这些前缀呢?问得好!

    是方便日后检索,当我们以这些前缀去搜索修改日志的时候

    是很容易的哦,微笑。

    git commit -m "修改用户模块bug"

    </pre>

    总结

    最后,给大家找了几个大厂的团队规范文档,希望你看完能够受益多多:

    • Vue风格指南
    • 腾讯团队文档
    • Bootstrap编码规范
    • isobar前端代码规范 及 最佳实践
    • 书写具备一致风格、通俗易懂 JavaScript 的原则
    • eslint-rules

    以上就是我在项目当中发现的一些问题,记录下来,暂时就记录这么多吧。希望正好看d到这篇文章的你如果也正好有以上的不良问题,请加油改正哦。当你的代码质量又提升一个档次的时候,我相信,你离前端大神之门又迈进了一步!

    啰嗦了这么多,希望看到的同学或多或少有点收获吧。不对的地方还请留言指正,不胜感激。俗话说, 三人行则必有我师 ,希望更多志同道合的小伙伴能聚在一起交流技术!

    相关文章

      网友评论

          本文标题:为什么我的代码让别人看起头发麻?

          本文链接:https://www.haomeiwen.com/subject/tavuxqtx.html