前端code review

4,049 阅读4分钟

前端 Code Review

一、为啥要 cr

1、通过团队内相互审核,交叉排查缺陷,以避免代码层面出现明显的问题。
2、通过团队成员的互相监督,在实现功能的基础上不断改善代码结构,以提高代码质量。
3、建立团队意识,意识到代码是团队的共同财产,在相互督促和改进中共同成长。

二 、如何 cr

三 、常见问题

1.没有README文档、或者README太简单、太作用有限

1111.png

除了项目的READMD,每个模块都应该有各自的README,说明这个模块的功能点、技术实现方案等,看个人习惯。

2.console.log

222.png (1)使用构建工具(如Webpack)的插件或配置:可以使用 Webpack 的 UglifyJs 插件或其他相关插件,在构建时自动移除或注释掉 console.log 语句。例如,在 Webpack 的配置文件中添加如下插件配置:

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
  // ...其他配置
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        uglifyOptions: {
          compress: {
            drop_console: true, // 移除 console.log
          },
        },
      }),
    ],
  },
};

(2)使用 eslint 插件或规则:可以使用 eslint 的插件或规则,在开发过程中通过静态代码检查移除或注释掉 console.log。例如,安装并配置 eslint-plugin-no-console 插件,在 .eslintrc.eslintrc.js 文件中添加如下配置:

module.exports = {
  // ...其他配置
  plugins: ['no-console'],
  rules: {
    // 禁止使用 console.log
    'no-console': 'error',
  },
};

3. 严格的等于

企业微信截图_6dad504b-d654-4975-908b-2839250d0d3d.png
不严格的等于,前端需要找一个id = 5,而后端返回了一个id = '5'

let num = 0;
let str = "0";
let obj = new String("0");
 
console.log(num == obj); // true
console.log(num == str); // true
console.log(obj == str); // true
console.log(null == undefined); // true
console.log('' == false);  // true
console.log(0 == false);  // true

配置 ESLint:在项目根目录下创建一个 .eslintrc.eslintrc.json 文件,并配置以下规则:

{
  "rules": {
    "eqeqeq": "error"
  }
}

4.不规范命名:

命名方式
苏州话 单词拼写错误: submitFrom submitForm
单复数 不清楚:
commentDatas commentsData
comments commentList commentsList commentsLists

5.使用合适的函数
async function downloadExcelFromAPI(url, fileName) {
  try {
    const response = await fetch(url);
    const blob = await response.blob();

    // 创建一个下载链接
    const downloadLink = document.createElement('a');
    downloadLink.href = URL.createObjectURL(blob);

    // 设置下载链接的属性
    downloadLink.download = fileName;

    // 将下载链接添加到文档中
    document.body.appendChild(downloadLink);

    // 模拟点击下载链接进行文件下载
    downloadLink.click();

    // 移除下载链接
    document.body.removeChild(downloadLink);
  } catch (error) {
    console.error('下载文件出错:', error);
  }
}

import React, { Component } from 'react';
import { openFile } from '@za/za-utils';
class ModalUpload extends Component {
 ...
 ...
 ...
 ...
   <Form.Item labelCol={{ span: 3, offset: 12 }}>
         <Button
             type="primary"
              disabled={loading || !validatePath}
              onClick={() => {
                    openFile.downloadWithLink(validatePath);
                    }} >
           异常错误下载
        </Button>
    </Form.Item>
 
   ...
   ...
   ...

6.适当的三元操作

image.png

image.png

image.png

// 👎 不够清晰,要是再嵌套一层两层呢
isSubscribed ? (
  <A />
) : isRegistered ? (
  <B>
) : (
  <C />
)

// 👍 将判断逻辑进行拆分
function D({ subscribed, registered }) {
  if (subscribed) {
    return <A/>
  }

  if (registered) {
    return <B />
  }

  return <C />
}

function Component() {
  return (
    <D
      subscribed={subscribed}
      registered={registered}
    />
  )
}
7.是否引用了不必要的npm包

以下的例子是否需要引入整个 lodash 包

import React from 'react';
import { Button } from 'antd';
import _ from 'lodash'; 

const QueryBtn = (props) => {
  const { btnList } = props;
  return (
    <div>
      {btnList.map((item) => {
        const { style, isDisabled, onClick, title } = item;
        return (
          <Button
            key={title}
            size="small"
            type="primary"
            style={style}
            disabled={isDisabled}
            onClick={onClick}
          >
            {title}
          </Button>
        );
      })}
    </div>
  );
};

export default React.memo(QueryBtn, (prev, next) => {
  const toStrPrve = JSON.stringify(prev);
  const toStrNext = JSON.stringify(next);
  const result = _.isEqual(toStrPrve, toStrNext);
  return result;
});

其他方案

使用 lodash-eslodash-es 采用了按需加载(tree-shaking)的方式,可以更细粒度地导入所需的函数,减小最终构建产物的尺寸。相比之下,lodash 在导入整个库时会包含所有的函数,尺寸较大。
使用子包。

8.逻辑优化,视图层无关的代码
const deleteTagById = async (id) => {
  const ids = [];
  id.forEach((item) => {
  ids.push(item);
});

const data = new FormData();
data.append('tagIds', ids);
const res = await deleteTag(data);
  if (res?.success) {
  message.success('删除成功');
  actionRef.current.reload();
}

};

优化后

export const deleteTag = (ids) => {
  const data = new FormData();
  data.append('tagIds', ids);
  return post('/tag/delete', data);
};
9.是否正确使用条件语句来实现期望的代码逻辑?
if (isTrue === true) {
  // 执行一些操作
}

// 良好示例
if (isTrue) {
  // 执行一些操作
}

function loop(rule, keys) {
      const result = rule.rule(value, keys, param);
      if (result === false) {
        if (typeof onError === 'function') {
          onError(
            typeof rule.errorMsg === 'function'
              ? rule.errorMsg(value, keys, param, prevLocation)
              : rule.errorMsg,
            keys,
            value,
            prevLocation
          );
        }
      } else if (result === true) {
        if (rule.successMsg && typeof onSuccess === 'function') {
          onSuccess(rule.successMsg, keys, value, prevLocation);
        }
      } else {
        throw new Error(`${key} must return a value of Boolean!`);
      }
      return result;
    }

四 、总结

在CodeReview阶段发现的逻辑错误、业务理解偏差、性能隐患等时有发生,CR可以提前发现问题。
主要体现在代码健壮性、设计合理性、代码优雅性等方面,持续CodeReview可以提升团队整体代码质量。
每一次CodeReview,都是一次知识的分享,磨合一定时间后,团队成员间会你中有我、我中有你,集百家之所长,融百家之所思。同时,业务逻辑都在代码中,团队CodeReview也是一种新人业务细节学习的途径。
通过多次讨论与交流,逐步达成团队共识,特别是对架构理解和设计原则的认知,在共识的基础上团队也会更有凝聚力,特别是在较多新人加入时尤为重要。