🍂好的代码习惯为你添砖加瓦!!!

1,978 阅读7分钟

代码规范的好处是什么?

好的代码是:健壮、多态、复用等。总的来说就是利于维护。可以如人类的发展文明一样把写代码分为3个阶段如下:

    1. 野蛮开荒-这个阶段一般算是初级程序员(每个人的定义不一样)都是刚工作没多久,或者说不注重自己的代码风格,只求快。那么这个阶段是最令同事头疼的阶段,功能呢可能也能搞,出来全是bug,代码简直没得看。
    1. 精耕细作-这个阶段一般算是中级程序员每个人的定义不一样)发现或者认识到写好的代码更利于维护,对自己有一定的代码规范要求。会去设计一定的数据结构、运用一些设计模式、代码风格,会阅读社区好的类库、组件设计等。
    1. 发明创造-这个阶段一般算是高级程序员了(每个人的定义不一样)会解决工作中遇到的困难,写脚本、制定代码规范、开发组件、开发类库、代码重构、制定工作流等。

态度端正

我觉得想要编码好、规范,态度一定要端正,时刻注意自己的代码规范。 不能老是着急着编码,最后让领导吐槽bug多,加个小功能就提到要重构了。

要想着每次编码,不论功能模块大小,简单与否都要按照规范编程,编出一行行艺术的代码,至少编出让自己看到会觉得很好看的代码。

文件篇幅过长

编码大忌,单个文件上千、大几百行也就是俗称的屎山,你会觉得现在大几百行没啥问题,我写的还蛮好的啊,函数都挺抽象的,但是再下个再维护尼编码的人,再新加功能的时候。持续迭代,这个时候问题就会越来越明显了,每次迭代比其他项目工时增多,每个人都吐槽这个代码太烂,重构要么没时间、要么就是没人去推进等。

现在都准寻函数式编程组件式编程模块化编程,我觉得一个文件不要超过200行。如果超过了就该考虑要再细化拆分模块了。



拿一个简单例子举例吧:

一个后台管理系统的一个页面,有搜索项(输入框若干、下拉框若干),有表格的增、删、改、查。 表格还有好多项需要自定义内容,展现形式可能不单单式文本,可能式另一种自定义形态。

有些同学就习惯把搜索项、表格逻辑都放在一个文件里面(.vue),这个时候呢写下来这个界面就发现行数已经上千行了!。

像这种其实用现成的UI组件比如element,有人会说没法抽了啊,篇幅过长主要是因为这个界面逻辑太多,需要的搜索项太多、自定义的表格插槽太多了,还有增删改查数据转换的逻辑。

咱们把上面有业务再得组件可以叫做业务组件业务组件可能不像是通用组件考虑多层各种场景复用得情况,但是也不能就这么摆烂,任由成为屎山吧!

正确得模块划分如下:(文件名应该是你的业务名,我这里都是举例)

-- 业务文件名 # 文件夹
  |--search #文件夹
    |-- config.js # 放一些数据枚举值、下来框数据、初始化数据
    |-- index.vue # 就是单纯得form表单搜索
  |--content #文件夹
    |--components # 文件夹 那个列需要自定义 不是单纯映射文本得 都抽出来
       |-- 组件1.vue 
       |-- 组件2.vue
       |-- 组件3.vue
    |-- config.js # 放表格列数据、等一些其他枚举映射数据
    |-- transform.js # 可以存放一些数据转换函数
    |--dialog # 文件夹
       |-- index.vue # 处理表格得增、改、查看等逻辑
       |-- handler.js # 可以存放一些处理方法逻辑
    |-- index.vue 
  |-- index.vue

再上面这种设计模块下,假如要新增一个搜索项,直接再search下书写就好了,如果要新增一列刚好还需要自定义展现形式,就直接再compoents下面写个小组件。

就近原则

编码请遵循就近原则存放目录,不要把所有项目枚举值都放在最外面的constants下。

假如这个枚举就再当前业务模块用到了,请就近创建一个config.jsoptions.js存放,不用都写到外面的constants下。

如果这个枚举值再别的模块也要用到就等下次抽离到外面constants就好了,一定要做好业务模块数据分层,不要都再一个index.js

好的constants如下:

--constants 文件夹
  |-- index.js
  |-- a.js
  |-- b.js
  |-- c.js
// index.js
export * from './a.js'
export * from './b.js'
export * from './c.js'

utils 文件

请不要把业务处理函数放到这里面来!

这里面存放的文件一定是遵循纯函数编程的,每个函数是有使用说明的例如lodash里面的注释。

/**
 *
 * @param {*} value The value to recursively clone.
 * @returns {*} Returns the deep cloned value.
 * @example
 *
 * var objects = [{ 'a': 1 }, { 'b': 2 }];
 *
 * var deep = _.cloneDeep(objects);
 * console.log(deep[0] === objects[0]);
 * // => false
 */
function cloneDeep(value) {
  //
}

文件存放格式如constants一样,如下:

--utils 文件夹
  |-- index.js
  |-- a.js
  |-- b.js
  |-- c.js
// index.js
export * from './a.js'
export * from './b.js'
export * from './c.js'

assets 文件夹

不要整个资源都堆、平铺再这个文件夹下,应该多创建子文件夹,按业务名称分类

services 文件夹

有些同学会创建一个services文件夹,里面写一个http.js然后把交互逻辑axios、还有api都创建一个对象里面都是各个api函数,最后挂载再vue原型上上this.$api.方法这么去用。这么是不好的

正确的是services只是存放和后端统一交互的逻辑就好了,比如axios的拦截器相关配置。

创建api文件夹这个下面按业务模块分文件夹如下:

--api 文件夹
  |-- index.js
  |-- a #文件夹
    |-- index.js
  |-- b #文件夹
    |-- index.js
  |-- c #文件夹
    |-- index.js
// index.js
export * from './a/index.js'
export * from './b/index.js'
export * from './c/index.js'

全局挂载

尽可能不要图方便就把方法啥的放在window上,和vue原型上了。

不知道尼的项目vue出现过没把交互函数都放在$api上 还有全局的$filter、全局的自定义指令等!!,统统不要放。 以后就是累赘!!!

把需要的全局过滤器等方法封装成一个函数 再需要的地方去引入就好了。

vuex的三个不要

不要再vuex去写逻辑!!不要用vuex去做通讯!! 不要vuex引入数据本地持久性!!

  • vuex的定位就是全局数据存储共享,他就是存数据的。存数据供给需要的模块使用。

  • 组件通讯不涉及数据存储也不要使用。最好的是使用propsemit去保持一个数据正确的流向。真需要跨多搁组件通讯可以使用发布订阅模式

  • 不要引入什么插件做数据持久性,这真的是碰到想骂人,还要时刻考虑数据清理掉的问题。

注释

删除掉你没用的注释!!!保持代码整洁

  • 使用语义化命名,删除掉没用的解释变量名注释信息,以及每次打印的console.log日志信息。
  • 可以对复杂逻辑做个简单的注释说明。

vue 文件好的习惯

.vue文件好的习惯如下:

    1. 每个vue文件有name值,组件名, 首字母大写例如ElHome;
    1. 把空的没用到的方法删除掉例如下面:
<template>
  <div>{{ name }}</div>
</template>

<script>
export default {
  name: 'Elname',

  data() {
    return {
      name: '21222'
    }
  },
  mounted() {

  },

  methods: {

  },

  wacth: {

  },
}

</script>

好的习惯不应该保留用不到的方法定义,不要怕同事队友不知道这些api删除掉就好了,需要用到了再定义! 如下:

<template>
  <div>{{ name }}</div>
</template>

<script>
export default {
  name: 'Elname',

  data() {
    return {
      name: '21222'
    }
  },
}
</script>
    1. props定义,应该统一有默认值类型是否必穿如下:
<template>
  <div>{{ name }}</div>
</template>

<script>
export default {
  name: 'Elname',
   
  props: {
    name: {
      type: String,
      default: '123',
      required: true,
    },

    isShow: {
      type: Boolean,
      default: false,
      required: false,
    },

    desc: {
      type: String,
      default: '123',
      required: false,
    },
  }
}
</script>
    1. 事件再不需要传参不用带(); @click="onClick()" -> @click="onClick"
    1. 不要出现魔法字符串,应该用枚举代替;
<template>
  <div @click="onClick">{{ name }} 
     <span v-if="type === 1">我是type1的逻辑</span>
    <span v-if="type === 2">我是type2的逻辑</span>
    <span v-if="type === 3">我是type3的逻辑</span>
  </div>
</template>

<script>
export default {
  name: 'Elname',

  data() {
    return {
      type: 1,
      name: '测试',
    }
  },
   
  methods: {
    onClick() {
      if(type === 1) {
        // 
      }
      if(type === 2) {
        // 
      }
      if(type === 3) {
        // 
      }
    }
  }
}
</script>

好的应该是:

<template>
  <div @click="onClick">
  {{ name }} 
   <span v-if="type === TYPE_MAP.ADD">我是type1的逻辑</span>
   <span v-if="type === TYPE_MAP.DELETE">我是type2的逻辑</span>
   <span v-if="type === TYPE_MAP.EDIT">我是type3的逻辑</span>
  </div>
</template>

<script>

const TYPE_MAP = {
  ADD: 1,
  DELETE: 2,
  EDIT: 3,
  VIEW: 4,
};

export default {
  name: 'Elname',

  data() {
    return {
      TYPE_MAP,
      type: 1,
      name: '测试',
    }
  },
   
  methods: {
    onClick() {
      if(type === TYPE_MAP.ADD) {
        // 
      }
      if(type === TYPE_MAP.DELETE) {
        // 
      }
      if(type === TYPE_MAP.EDIT) {
        // 
      }
    }
  }
}
</script>
    1. 命名规范
    • Event 命名直接使用事件名称,无需在事件名称前加on等词。例如

      • click
      • xxx-click
      • xxx-dblclick
      • validate
      • change
      • xxx-change
      • close
      • remove
    • 绑定命名可是使用on、handler@click="onHomeClick"、@click="handleHomeClick"

    • Slot 命名 优先使用功能名,其次使用位置名称。

      • title:标题区域
      • header:头部区域
      • footer:底部区域
      • content:主体内容区域
      • description:描述文本区域
      • prefix: 前缀
      • suffix: 后缀
      • append: 添加
    1. 组件通讯
    • 禁止通过 this.$parent 来访问父组件的上下文。如果子组件依赖父组件的一些内容,或许你需要的是依赖注入 (provide/inject)。
    • 在大多数情况下,通过this.$refs 来访问其它组件的上下文是可以避免的。在使用的的时候你需要注意避免调用了不恰当的组件 API,所以应该尽量避免使用 this.$refs
    • 尽量使用propsemit通讯,保证数据的一个正确的流向。
    1. 组件导出
    • 没编写一个组件都应该具备一个对应的导出js模板如下:
import CustomButton from './index.vue';

export {
  CustomButton
};

const plugin = {
  install (Vue, options) {
    Vue.component(CustomButton.name, CustomButton);
  }
};

export default plugin;


let GlobalVue = null;
if (typeof window !== 'undefined') {
  GlobalVue = window.Vue;
} else if (typeof globalThis !== 'undefined') {
  GlobalVue = globalThis.Vue;
}
if (GlobalVue) {
  GlobalVue.use(plugin);
}
    1. 组件文档 每个组件编写好都必须有README.md,对应的组件使用示例以及各个api示例,避免人员流动,使用组件还要看源码照成不必要的资源浪费。
    1. 组件分格(仅个人习惯)会把每个api间隔用换一行隔开,会更好读(element ui就是此风格)如下。
<template>
  <div @click="onClick">
  {{ name }} 
   <span v-if="type === TYPE_MAP.ADD">我是type1的逻辑</span>
   <span v-if="type === TYPE_MAP.DELETE">我是type2的逻辑</span>
   <span v-if="type === TYPE_MAP.EDIT">我是type3的逻辑</span>
  </div>
</template>

<script>

const TYPE_MAP = {
  ADD: 1,
  DELETE: 2,
  EDIT: 3,
  VIEW: 4,
};

export default {
  name: 'Elname',

  data() {
    return {
      TYPE_MAP,
      type: 1,
      name: '测试',
    }
  },

  props: {
    isShow: {
      type: Boolean,
      default: false,
      required: false,
    },

    desc: {
      type: String,
      default: '123',
      required: false,
    },
  },
   
  methods: {
    onClick() {
      if(type === TYPE_MAP.ADD) {
        // 
      }
      if(type === TYPE_MAP.DELETE) {
        // 
      }
      if(type === TYPE_MAP.EDIT) {
        // 
      }
    }
  },

  mounted() {
   // 逻辑
  },

  computed: {
    a() {
      return '123'
    },

    b() {
      return '123'
    },
  }
}
</script>

代码中不可少判断

再编码中可能大家会为了更快、更简洁等因素会莫名少了许多错误数据判断,导致只要是数据结构发生一丁点的变化,尼的程序就崩溃了。

比如如下:

async function getList() {
  const response = getListApi();
  const list = response.data.map((item) => {
    return {
      ...item,
      a: item.name,
    }
  });

  return list;
}
  • 好像看起来也没啥问题,都挺好的啊。获取接口数据给每条数据新增字段a。 但是这个时候假如数据结构有些许变化程序就崩溃了!! 就是说程序会死掉了。

  • 那我加个try/catch兜底,(这个能解决程序死掉的问题,但是不是最好的处理方式),不要对能知道的错误做错误捕获。

async function getList() {
  let list = []
  try {
    const response = getListApi();
    list = response.data.map((item) => {
        return {
        ...item,
        a: item.name,
        }
    });
  } catch (error){
    console.log('getList error:' error)
  }
  return list;
}
  • 最佳处理时增加一些判断如下(不要担心你加判断了程序就执行慢了的问题,影响不了太多的):
async function getList() {
  const { data } = getListApi() || { data: [] }; // 注意这里 或者
  if(data?.length) { // 增加判断
    return data.map((item) => {
      return {
      ...item,
      a: item.name,
      }
    });
  } else {
   console.log('获取数据为空或者失败!')
   return [];
  }
}

UI 得还原

UI 一定得高度尽可能100%还原,不要你觉得,要ued觉得。有些开发觉得自己也不去吸UI得颜色、间距、大小 看着设计就直接开搞,搞出来得东西上下、左右间距都没对齐,最后产品不满意,会逐渐对你失去信任,这是一份工作,不是差不多就行了。虽然样式影响不了太大事,但是会让别人觉得你这个产品不够专业, 研发不够强!!!

scss/less 嵌套

嵌套不要缩写, 不好找到具体class,会全局查,查不出来比如:

.item {
  cursor: pointer;

  &__title {

  }

  &__content {

  }

  &__footer {

  }
}

优化成:(这个样查就很方便了)

.item {
  cursor: pointer;

  .item__title {

  }

  .item__content {

  }

  .item__footer {

  }
}