WEB前端规范和代码优化手册

931 阅读5分钟

前言:

程序员都认同代码规范的重要性,本手册用实际例子来说明如何规范代码,提高可读性和维护性。
  

变量命名

变量命名不能太简单也不能太复杂,不要用难以读懂的简写方式

// ❌错误的示范:
let ord = 1; // 过于简单
let myGoodsOrderList = 1; // 过于复杂,有些单词是多余的
 
// ✅正确的示范:
let orderId = 1;


 
用知名其意的方式为变量命名,通过这种方式,当再次看到变量名时,就能大概理解其中的用意

// ❌错误的示范:
let daysSLV = 10; 
let y = new Date().getFullYear();
let ok = true;
if(age > 30){
  ok = true
}
 
// ✅正确的示范:
const MAX_AGE = 30;
let daysSinceLastVisit = 10;
let currentYear = new Date().getFullYear();
const isUserTooOld = age > MAX_AGE;

 
不要在变量名中添加额外的不需要的单词

// ❌错误的示范:
let nameValue;
let theProduct;
 
// ✅正确的示范:
let name;
let product;

不要简写变量上下文 

// ❌错误的示范:
users.forEach(u => {
  //...
  //...
  //...
  register(u); // 当上面的代码很多时,这”u“是什么鬼?
  //...
}
 
// ✅正确的示范:
users.forEach(user => {
  //...
  //...
  //...
  register(user);
  //...
}

不要添加不必要的上下文

// ❌错误的示范:
const users = {
  userName: 'John', 
  userSurname: 'ka',
  userAge: 30
}
 
// ✅正确的示范:
const users = {
  name: 'John', 
  surname: 'ka',
  age: 30
}

 

去掉对阅读者没有意义的注释

// ❌错误的示范:
function getUserList() {
   // 获取用户列表
   ajax.post('userList').then(resp => {
   }
}
 
// ✅正确的示范:
function getUserList() {
   ajax.post('userList').then(resp => {
   }
}

函数命名

使用长而具有描述性的名称,考虑到函数表示某种行为,函数名称应该是动词或短语,用以说明其背后的意图以及参数的意图。函数的名字应该说明他们做了什么。

// ❌错误的示范:
function notif(user) {
    
}
 
// ✅正确的示范:
function notifyUser(email) {
   
}

避免使用大量参数,理想情况下,函数应该指定两个或更少的参数。参数越少,测试函数就越容易,参数多的情况可以使用对象。

// ❌错误的示范:
function getUsers(fields, fromDate, toDate) {
    
}
 
// ✅正确的示范:
function getUsers({fields, fromDate, toDate}) {
   
}

使用默认参数替代 || 操作

// ❌错误的示范:
function getUsers(fromDate) {
    let date = fromDate || ''
}
 
// ✅正确的示范:
function getUsers(fromDate = '') {
   
}

一个函数应该只做一件事,不要在一个函数中执行多个操作

// ❌错误的示范:
function notifyUsers(users) {
    users.forEach(user => {
      const userRecord = database.lookup(user);
      if (userRecord.isVerified()){
        notify(user)
      }
    }
}
 
// ✅正确的示范:
function notifyUsers(users) {
   users.filter(isUserVerified).forEach(notify);
}
  
function isUserVerified(user) {
  const userRecord = database.lookup(user);
  return userRecord.isVerified();
}

不要污染全局变量,如果需要扩展现有对象,请使用ES6类和继承,而不是在原生对象的原型链上创建函数

// ❌错误的示范:
Array.prototype.myFunc = function myFunc() {
    
}
 
// ✅正确的示范:
 
class SuperArray extends Array {
  myFunc() {
  }
}

避免使用反面条件

// ❌错误的示范:
isUserNotBlocked(user) {
}
 
// ✅正确的示范:
isUserBlocked(user) {
}

使用条件简写,仅对布尔值使用此方法,并且如果确信该值不会是undefined 或null的,则使用此方法

// ❌错误的示范:
if(isValid === true) {
}
if(isValid === false) {
}
// ✅正确的示范:
if(isValid) {
}
if(!isValid) {
}


### 对象简写 对象尽量简写,比如 {name:name} 应当简写为 {name}
```javascript // ❌错误的示范: const name = 'John'; getList({name:name});

// ✅正确的示范: const name = 'John'; getList({name});

 
<a name="1ZIO2"></a>
### 双目运算简写
(userData.userPhoto)?userData.userPhoto:userLogo<br />改为:userData.userPhoto || userLogo 
```javascript
// ❌错误的示范:
let img = userData.userPhoto?userData.userPhoto:userLogo
 
// ✅正确的示范:
let img = userData.userPhoto || userLogo
 

代码要简洁而不简单,代码要让阅读者觉得舒服

/* 找到提供的句子中最长的单词,并计算它的长度。函数的返回值应该是一个数字
   例如:findLongestWord("The quick brown fox jumped over the lazy dog");
*/
// ❌错误的示范:
function findLongestWord(str){
   let arr = str.split(' ')
   let flagNum = 0
   arr.forEach((ele)=>{
     if(ele.length > flagNum){
       flagNum = ele.length
     }
   })
   return flagNum
 }
 
// ✅正确的示范:
function findLongestWord(str){
   let arr = str.split(' ');
   return Math.max(...arr.map(item => item.length));
}
 

用对象map代替过多的if else,让代码更易读,易维护

/* 找到提供的句子中最长的单词,并计算它的长度。函数的返回值应该是一个数字
   例如:findLongestWord("The quick brown fox jumped over the lazy dog");
*/
// ❌错误的示范:
// 1 正常; 2 待审核; 4 禁用; -1 驳回审核; -10 删除
if (this.detail.status === 1) {
  return '已认证'
} else if (this.detail.status === 2) {
  return '待审核'
} else if (this.detail.status === 4) {
  return '已禁用'
} else if (this.detail.status === -1) {
  return '驳回审核'
} else {
  return '--'
}

// ✅正确的示范:
statusMap = {
   1: '已认证',
   2: '待审核',
   4: '已禁用',
   '-1':  '驳回审核'
 }
return statusMap[this.detail.status] || '--'
 

函数也可以用map优化(请仔细理解其中用意)

// bad
function(flag){
    if(flag==="left"){
        move("right");
    }else if(flag==="right"){
        move("left");
    }else if(flag==="top"){
        move("bottom");
    }else if(flag==="bottom"){
        move("top");
    }
}

// good
// 更改后,语义更清晰,使用更灵活
var move = flag => { console.log(flag) }
var command = {
    left: function(){
        move("left");
    },
    right: function(){
        move("right");
    },
    top: function(){
        move("top");
    },
    bottom: function(){
        move("bottom");
    },
};
function moveTo(flag){
    command[flag]();
}
moveTo('left')

利用 || 优化代码

// bad
if (value === "") {
    value = "similar";
}

// good
value = value || "similar";

巧用正则优化代码

//(bad) 格式化字符串 fontSize => font-size
function stringFormat(str) {
    var strArr = str.split(''),
        len = strArr.length,
        i = 0;

    for (; i < len; i++) {
        if(/^[A-Z]$/.test(strArr[i])) {
            strArr[i] = "-" + strArr[i].toLowerCase();
        }
    }

    return strArr.join('');
}

//(good) 格式化字符串 fontSize => font-size
function stringFormat(str) {
    return (str.replace(/([A-Z])/g, "-$1")).toLowerCase();
}

更少的嵌套,尽早 return

// bad
let getPayAmount = () => {
  let result
  if (_isDead) result = deadAmount()
  else {
    if (_isSeparated) result = separatedAmount()
    else {
      if (_isRetired) result = retiredAmount()
      else result = normalPayAmount()
    }
  }

  return result
}

// good
let payAmount = () => {
  if (_isDead) return deadAmount()
  if (_isSeparated) return separatedAmount()
  if (_isRetired) return retiredAmount()
  return normalPayAmount()
}

利用空行让代码更易读

<!-- ✅正确的示范:-->
<uni-popup ref="showshare" @change="change" type="bottom">
      <view class="uni-share">
        
            <text class="uni-share-title">分享到</text>

            <view class="uni-share-content">
              
              <button open-type="share" class="uni-share-content-box" style="background: #ffffff;" @tap="cancel">
                <view class="uni-share-content-image">
                  <img src="../../../static/image/weixin.png" class="content-image" mode="widthFix"/>
                </view>
                <text class="uni-share-content-text">微信</text>
              </button>

              <button class="uni-share-content-box" @tap="saveImg" style="background: #ffffff;">
                <view class="uni-share-content-image">
                  <img src="../../../static/image/save-img.png" class="content-image" mode="widthFix"/>
                </view>
                <text class="uni-share-content-text">保存图片</text>
              </button>

            </view>

            <text class="uni-share-btn" @tap="cancel('share')">取消分享</text>
        </view>
 </uni-popup>

正确选择组件

在使用VUE的UI组件库时,要根据功能而不是外观选用组件。 例如下面的UI需求,看似是tab,但如果它的行为是radio,那应该选用checkbox组件。
image.png

代码块重复2次以上,建议封装,重复3次以上,必须封装

// good,封装微信自带的toast,这样可以统一修改持续时间,并且在使用的时候代码更简洁
function showToast(msg, icon = 'none') {
 		wx.showToast({
      title: msg,
      icon,
      duration: 2000
    }); 
}

### css优化:书写代码前, 考虑并提高样式重复使用率 对于常用的按钮,文本,圆角,阴影等效果,做mixin或者全局class ```javascript .app-container { padding: 20px; margin-top: 80px; }

.text-center { text-align: center }

.link-type, .link-type:focus { color: #2E6FFF; cursor: pointer;

&:hover { color: rgb(32, 160, 255); } }

@mixin clearfix { &:after { content: ""; display: table; clear: both; } }

@mixin bg-image(url) { background: url('../../assets/' + url + ".png?" + $version) no-repeat top left; -webkit-background-size: 100% 100%; background-size: 100% 100%; }

@mixin icon-image(url) { display: inline-block; vertical-align: top; margin-right: 10px; background: url('https://piggy-bank-image.hupofintech.com/piggy/pumanzhu-2/' + url + ".png?" + $version) no-repeat center left; -webkit-background-size: 100%; background-size: 100%; }

<a name="gwgmm"></a>
### 
<a name="s0vEN"></a>
### 外观模式、工厂模式在vue中应用
```javascript
<template>
    <div>
        <form>
            <login
                v-for='item of comInp'
                :name = 'item'
                :key='item'
            ></login>
        </form>
    </div>
</template>
<script>
import login from './login';
const inputList = {
    login: ['Username', 'Password', 'ImgCode'],
    mobile: ['mobile', 'Password']
}
export default {
    name: 'Form',
    data() {
        return {
            type: 'login'
        }
    },
    components: {
        login
    },
    computed: {
        comInp(){
            console.log(inputList)
            return inputList[this.type]
        }
    },
    
}
</script>

// login.vue
<template>
    <component :is='comInput' />
</template>
<script>
import Username from './input/Username'
import Password from './input/Password'
import ImgCode from './input/ImgCode'
import Mobile from './input/mobile'

const inpMap = {
    Username,
    Password,
    ImgCode,
    Mobile
}
export default {
    name: 'login',
    data() {
        return {
            codeStatus: 'img'
        }
    },
    components: inpMap,
    props: ['name'],
    computed: {
        comInput(){
            return inpMap[this.name]
        }
    },
}
</script>

状态模式在vue中应用

<template>
    <div>
        <div>
            <div>步骤一<span>{{state > 1 && '完成' || '未完成'}}</span></div>
            <div>步骤二<span>{{state > 2 && '完成' || '未完成'}}</span></div>
            <div>步骤三<span>{{state > 3 && '完成' || '未完成'}}</span></div>
            <div>步骤四<span>{{state > 4 && '完成' || '未完成'}}</span></div>
        </div>
        <button v-if="canGoBack" @click="goBack">返回上一步</button>
        <component :is="stepName"  @getChildData = 'changeState' />
    
    </div>
    

</template>

<script>
import step1 from './step/step1'
import step2 from './step/step2'
import step3 from './step/step3'
import step4 from './step/step4'
export default {
    name: 'stepHome',
    data() {
        return {
            state: 1,
            cache: []
        }
    },
    computed: {
        stepName(){
            const stepList = {
                1: step1,
                2: step2,
                3: step3,
                4: step4,
            }
            return stepList[this.state]
        },
        canGoBack(){
            return this.cache.length > 0
        }
    },
    methods: {
        changeState(state){
            this.cache.push(state)
            this.$set(this, 'state', state)
        },
        goBack(){
            this.cache.pop()
            this.state = this.cache[this.cache.length - 1]
        }
    }, 
}
</script>

图片压缩

所有的大图都应当经过压缩,推荐在线压缩工具:tinypng.com/

参考
阮一峰的ES6规范: es6.ruanyifeng.com/#docs/style
VUE的风格指南:cn.vuejs.org/v2/style-gu…