Front End Checklist by @wsafight

在 CR 新同学的代码中,有很多的知识和代码点需要重复解释,这里尝试列一个清单,以方便自己和他人

提交可交付的代码

本文不是代码规范,但也可能会限制您的代码风格

如果你想要为这个清单做贡献或觉得有不合理的地方,请访问New Issue

任何时刻都要检查数据存在
// bad
const { firstName, age, addresses } = params
submit({
    firstName,
    age,
    addresses,
    ...secondaryInfo,
})

// good
const { firstName, age, addresses } = params
if (!firstName) {
    // 提示错误
    return
}
if (addresses.length === 0) {
    // 提示错误
    return
}
submit({
    firstName,
    age,
    ...secondaryInfo,
})
任何时刻都要检查数据类型
// bad
const { firstName, age, addresses } = params
submit({
    firstName,
    age,
    addresses,
    ...secondaryInfo,
})

// good
const { firstName, age, addresses } = params
if (!firstName || typeof firstName !== 'string') {
    // 提示错误
    return
}
if (!Array.isArray(addresses) || addresses.length === 0) {
    // 提示错误
    return
}
submit({
    firstName,
    age,
    ...secondaryInfo,
})

// 注:以下为 lua 代码,没有检查 b 变量的类型是 2021.07.13 B站宕机事故的主要原因
// 具体可看 https://cloud.tencent.com/developer/article/2251012

local _gcd
_gcd = function (a, b)
    if b == 0 then
        return a
    end
    return _gcd(b, a% b)
end
任何时刻都要基于业务进行输入验证
// bad
const { phone } = params
if (!phone || typeof phone !== 'string') {
    // 提示错误
    return
}
submit({
    phone
})

// good
const isValidPhone = (phone) => {
    if (!phone || typeof phone !== 'string') {
        return false
    }
    return /^(?:[() .+-]*\d+)+(?:[ ]?ext[ ]?\d+)?$/.test(phone)
}

const { phone } = params

if (isValidPhone(phone)) {
    // 提示错误
    return
}
submit({
    phone
})
任何时刻都需要在输入框内添加 max 最大长度等限制
// bad
<el-input
    type="textarea"
    v-model="inputForm.contents"
/>

// good
<el-input
    type="textarea"
    v-model="inputForm.contents"
    :rules="[{required: true, message: '请输入工单内容', trigger: 'blur'}]"
    :rows="12"
    :maxlength="4000"
/>
编写函数时注意宽入严出,对接受的数据要宽容,对输出的数据要严格
const OBJECT_TYPE = '[object Object]'

const isRealObject = (val) => getType(val) === OBJECT_TYPE


// bad
const getOwnKeysForObj = (val) => {
    if (!val) {
      return
    }

    if (!isRealObject(val)) {
      return
    }

    return Object.keys(val).filter(key => val.hasOwnProperty(key))
}

// good
const getOwnKeysForObj = (val) => {
    if (!val) {
      return []
    }

    if (!isRealObject(val)) {
      return []
    }

    return Object.keys(val).filter(key => val.hasOwnProperty(key))
}
注意差 1 错误,时刻考虑 <= 和 < 的差距
优先处理异常情况,以方便真正业务处理
// bad
const getService = (code ) => {
    if (isRealServiceByCode(code)) {
        // do something
        if (isXXX()) {
            // do something
            return services
        }
    }
    return []
}

// good
const getService = (code ) => {
    if (!isRealServiceByCode(code)) {
        return []
    }

    // do something
    if (!isXXX()) {
        return []
    }

    // do something
    return services
}
条件判断和循环最多三层(同上)
// bad
const getService = (code ) => {
    if (isRealServiceByCode(code)) {
        // do something
        if (isXXX()) {
            // do something
            return services
        }
    }
    return []
}

// good
const getService = (code ) => {
    if (!isRealServiceByCode(code)) {
        return []
    }

    // do something
    if (!isXXX()) {
        return []
    }

    // do something
    return services
}
不断进行函数拆分,直到用一句话能够表明意义(一个函数只完成一个功能)
// bad
const initial = async () => {
    // 获取数据信息
    // ...

    // 验证过滤并修改数据信息
    // ...

    // 设置渲染
    // ...

    // 其他操作
}

// good
initial = async () => {
    // 在初始化时候获取数据
    const data = await this.getDataWhenInitial();
    // 验证以及格式化当前数据
    this.validateAndNormalizeData(data)
    // 比对然后同步当前数据
    this.diffThenSyncData(data)
}
不要设计面面俱到的函数,开发者只会编写两种代码,一种是有 bug 的,另外一种是将来有 bug 的
无论何时优先使用解构
// bad
if (this.props.dialogVisible) {

}

// good
const { dialogVisible } = this.props
if (dialogVisible) {
}
不要在一个语句中同时处理同步数据和异步数据
// bad
let time = 1

time = time + await getTime()

// good
let time = 1
const oldTime = await getTime()
time = time + oldTime
函数返回值要,清楚,明了,统一,千万不要出现在一个函数中存在异步和同步两种结果
// bad
const getInfo = (key) => {
    const cache = sessionStorage.getItem(key)
    if (cache) {
        return cache
    }
    return api('xxx').then(res => {
        sessionStorage.setItem(key, res)
        return res
    })
}


// good
const getInfo = (key) => {
    const cache = sessionStorage.getItem(key)
    if (cache) {
        return Promise.resolve(cache)
    }
    return api('xxx').then(res => {
        sessionStorage.setItem(key, res)
        return res
    })
}
钩子函数中只写调用,而不写具体逻辑代码
// bad
async componentDidMount () {
    // 获取数据信息
    // ...

    // 验证过滤并修改数据信息
    // ...

    // 设置渲染
    // ...

    // 其他操作
}

// good
componentDidMount () {
    this.initial()
    // 其他操作,抽取后不回彼此干扰,上面的 return 不回影响其他操作
}

initial = async () => {
    // 在初始化时候获取数据
    const data = await this.getDataWhenInitial();
    // 验证以及格式化当前数据
    this.validateAndNormalizeData(data)
    // 比对然后同步当前数据
    this.diffThenSyncData(data)
}
不要扩大代码的影响
不要轻易的封装重复的业务代码,有可能是一种巧合而并非重复
注释要有侧重点,数据结构的注释是非常重要的,其次是不合理的业务逻辑然后才是正常函数
// bad


// good
变量命名缩写必须能够的到团队认可,否则使用完整单词
// bad
bgImgSrc


// good
backgroundImgSrc
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚
函数命名使用 动词 + 名词的形式,如 发送短信 sendSms,如果是在模板或者 jsx 中,使用 handle + 名词 + 动词,如 处理短信发送,handle 也有具柄的意义
函数参数多于 3 个时使用对象传参数
避免使用布尔参数,如果有需要使用对象传递参数
尽量不要使用默认导出
通过代码上通过 for, by, from, when,then 等单词添加函数的意图,提供有用的额外信息
减少没有必要的数据类型默认转换与强制转换(分析为什么当前数据不是该数据类型,而并不是进行转换)
考虑在布尔运算的过程中,false 0 '' 是否和 null,undefined 意义相同
在进行判断的过程中,先判断范围大的,在进行小范围判断,如: 权限判断 -> 数据权限判断 -> 业务判断
在复杂布尔运算判断中添加括号,方便他人读懂
if, else, for, while, do, switch, try, catch, finally 等语句的执行语句部分无论多少都要加括号 { }
轻易不要手动操作 DOM
添加定时器,以及事件等,记住清除,否则可能引发内存泄漏或者错误
处理异步时候,多考虑异常,同时 Promise 必须使用 resolve 或 reject 进入下一个状态,避免代码执行卡死
使用删除而不是注释代码来精简代码
避免大数组进行查询等运算,使用 Object、Map 或者 Set 进行优化
避免大量数据进行递归
除非有必要的性能要求,不要用奇技淫巧进行优化
HTML 标签和属性操作,必须限定/过滤传入变量值
属性一个一行,方便使用
永远不要在渲染函数中进行数据修改
永远不要在渲染函数中创建随机值(Math.random() Date.now())
永远不要在渲染函数中发出网络请求
永远不要在渲染中创造新的组件,这将导致 React 反复销毁并重新创建子组件树
轻易不要复制代码,如果必需的话,请手敲一遍,确保需要执行的每一行代码都是可用的
结构的设计要尽量考虑向前兼容和以后的版本升级,并为某些未来可能的应用保留余地
解决 bug 不能只考虑 bug 本身,要分析产生的原因
不要在提示用户的信息的中添加语气助词以及网络用语,使用陈述句
不要在注释中包含侮辱性词汇
少引用不需要的代码,开源项目是否能够只引入一部分
建议不要在 package.json 依赖项中使用 ^ ~ 等,最好直接使用当前的版本号,以避免依赖项升级导致项目问题
尽量不使用行内样式,即使使用 props 传递,在一定范围内也要传递 class 类
注重语意,操作数组不需要返回值时使用 forEach,而不是 map,同时 map 会比 forEach 慢
多使用可以终止的循环,如 some,every,find 等(空数组使用 some 时返回 false,every 返回 true)
程序要懒惰,不到最后一刻绝对不去获取或者处理数据
不要使用 setTimeout 解决异步问题,这将会成为一个不易重现(更加复杂)的 bug
在符合逻辑的情况下,尽量大的提升系统的容错,增强健壮性
注重事物生命周期,严格遵循初始化时候创建,结束前清理
使用更新的语言特性提升程序健壮性
单文件添加行数限制(需要由团队自行定制)
业务功能不要依赖语言特性