记一次代码优化过程

419 阅读2分钟

首先来看看需求:

可以看到,这里条件很多,并且最后都是要生成节点,那么就先有请被优化的嘉宾上场吧:

const Create = (row) => {
    const currentTarget = getCurrentTarget(id)
    const isCurrentTargetOnline = isTargetOnline(currentTarget.status)
    const isFile = row.is_file
    const isLargeFile = row.length > 1024 * 1024 * 1024
    if (isCurrentTargetOnline) {
      // 外部已经获取了
      if (isWifi) {
        if (isFile) {
          if (isLargeFile) {
            return <Button onClick={() => handleCacheNow(row)}>cache now</Button>
          } else {
            return (
              <Popconfirm
                placement="top"
                title="在线 && wifi && 大文件"
                onConfirm={() => handleCacheNowConfirm(row)}
                onCancel={() => handleCacheNowCancel(row)}
                okText="No"
                cancelText="Yes"
              >
                <Button>cache now</Button>
              </Popconfirm>
            )
          }
        } else {
          return (
            <Space size="small">
              <Button onClick={() => handleUpdate(row)}>cache now</Button>
              <Popconfirm
                placement="top"
                title="在线 && wifi && 大文件"
                onConfirm={() => handleCacheNowConfirm(row)}
                onCancel={() => handleCacheNowCancel(row)}
                okText="No"
                cancelText="Yes"
              >
                <Button>cache now</Button>
              </Popconfirm>
            </Space>
          )
        }
      }
    }
  }

原谅我实在没法把这个代码写完......那是在太磨人了

上面的代码暴露了哪些问题呢?

  • 首先嵌套的 if-else 绝对是罪大恶极,条件语句这种东西一旦多层嵌套起来,自己都不知道哪个条件是那个条件
  • 然后这里每个条件最后都是返回组件,那么完全可以将这部分代码抽离出来做一个生成函数,根据参数生成不同的节点
  • 开头的状态获取,有的从内部获取,有的从外部获取,也很容易混乱
  • handle 函数,明明都是一样的功能,却因为情况不同分了好几个函数来写

那么下面就从这几个角度来优化代码,首先是条件语句,嵌套的条件语句,完全可以改成下面这个样子:(当然最是照着流程图来写,避免遗漏)

const Create = (row) => {
    if (isOnline && isWifi && isFile && isLargeFile) {
      /* do someting */
    } else if (isOnline && isWifi && isFile && !isLargeFile) {
      /* do someting */
    } else if (isOnline && isWifi && !isFile) {
      /* do someting */
    } else if (isOnline && !isWifi && isFile && isLargeFile) {
      /* do someting */
    } else if (isOnline && !isWifi && isFile && !isLargeFile) {
      /* do someting */
    } else if (isOnline && !isWifi && !isFile) {
      /* do someting */
    } else if (!isOnline && isFile && isLargeFile) {
      /* do someting */
    } else if (!isOnline && isFile && !isLargeFile) {
      /* do someting */
    } else if (!isOnline && !isFile) {
      /* do someting */
    }
  }

这样就从嵌套变成了扁平化的代码,加上注释就能很快的找到对应逻辑链了。当然,这里也可以建一张表:

const Create = (row) => {
    const mapConditionToNode = new Map([
      [isOnline && isWifi && isFile && isLargeFile, () => {}],
      [isOnline && isWifi && isFile && !isLargeFile, () => {}],
      [isOnline && isWifi && !isFile, () => {}],
      [isOnline && !isWifi && isFile && isLargeFile, () => {}],
      [isOnline && !isWifi && isFile && !isLargeFile, () => {}],
      [isOnline && !isWifi && !isFile, () => {}],
      [!isOnline && isFile && isLargeFile, () => {}],
      [!isOnline && isFile && !isLargeFile, () => {}],
      [!isOnline && !isFile, () => {}],
    ])
    mapConditionToNode.forEach((value, key) => {
      key && value()
    })
  }

现在代码已经从嵌套地狱里逃出来了,接下来就是节点的生成器。

从逻辑图可以看到,这里目标节点只有两种类型:

  • Button
  • Popconfirm

其中 Popconfirm 内部又需要 Button,那么只要建两个工厂就好:

const CreateBtn = ({ text, icon, disabled, fn }) => {
  return (
    <Button icon={<IconDisplay type={icon} />} onClick={fn} disabled={disabled}>
      {text}
    </Button>
  )
}
const CreatePop = (btnConfig, { text: popText, confirmFn, cancelFn }) => {
  return (
    <Popconfirm
      placement="top"
      title={popText}
      onConfirm={cancelFn}
      onCancel={confirmFn}
      okText="No"
      cancelText="Yes"
    >
      {CreateBtn(btnConfig)}
    </Popconfirm>
  )
}

接下来将状态获取统一一下:

const { isOnline, isFile, isLargeFile, isWifi } = getStatus(row)

函数我们根据不同的参数做不同的处理即可,这里就不再赘述。

结语

更加阅读体验:记一次代码优化过程