转变思维:from 代码美观 to 运行效率

334 阅读5分钟

最近我在实习的过程中认识到了很多自己在业务开发思维上的不足之处,最明显的一处便是:提出mr之后总是被leader打回修改,很少有一次提交就合并到主分支的需求。

那么为什么一碰到稍微复杂的需求,经验略微不足的实习生写出来的代码总显得如此漏洞百出呢?我遇到的问题是:总纠结于代码的美观而忽视了代码的运行效率。

以下是根据我的自身经历总结出的一些问题示例:


为了文章更加简单易懂,示例当中均采用伪代码的形式叙述问题

问题与背景

有这样一个需求:

页面应用拥有若干不同种类的组件,用户可以自由 增加/删除 组件,而不同种类的组件都拥有若干主题(有的组件主题数量为0),组件的主题需要请求/theme接口获取主题数据。 以往的方案为:最初加载页面时便向/theme接口请求得到所有种类组件的所有主题。 需求:/theme 接口提供modulename字段,前端需要在用户点击某个组件的主题列表时按需请求组件主题数据。 补充说明:用户点击主题列表的频率很低。

原本代码结构如下:

const { render } = useHtml() // 渲染组件主题列表函数

// list 目前页面已请求并缓存下来的组件主题列表(此处最初为全部组件主题数据)
const { list } = useData() 

/**
 * @param moduleName 用户点击切换主题的目标组件名
 * @description 用户点击切换主题时触发的回调函数
 */
const onClick = (moduleName)=>{
  const themeData = list[moduleName]
  render(themeData)
} 

可以看到在原版的代码当中,是没有请求数据的动作的(请求/theme的动作在主页已经完成),而是直接从全局数据状态管理(redux)中取出list

解决方案

在全局数据中新增alreadyList字段

const { render } = useHtml() // 渲染组件主题列表函数

// list 目前页面已请求并缓存下来的组件主题列表(此处最初为空数组)
// alreadyList 目前页面一请求并缓存下来的主题名组成的数组
const { list, alreadyList } = useData()

/**
 * @param moduleName 用户点击切换主题的目标组件名
 * @description 用户点击切换主题时触发的回调函数
 */
const onClick = (moduleName)=>{
  let themeData
  if(alreadyList.includes(moduleName)){ // 若已经请求过该组件的主题,直接从全局数据中获取主题数据
    themeData = list[moduleName]
  } else { // 没有请求过该组件主题,通过/theme接口按需请求
    themeData = fetch('/theme',{
      moduleName
    })
    setData(themeData, moduleName) // 将请求得到的主题数据放入全局数据 即改变list和alreadyList
  }
  render(themeData) // 将主题数据渲染至页面上
}

通过list判断是否需要重新请求

const { render } = useHtml() // 渲染组件主题列表函数

// list 目前页面已请求并缓存下来的组件主题列表(此处最初为空数组)
const { list } = useData()

/**
 * @param moduleName 用户点击切换主题的目标组件名
 * @description 用户点击切换主题时触发的回调函数
 */
const onClick = (moduleName)=>{
  let themeData
  if(hasModule(list,moduleName)){ // 若已经请求过该组件的主题,直接从全局数据中获取主题数据
    themeData = list[moduleName]
  } else { // 没有请求过该组件主题,通过/theme接口按需请求
    themeData = fetch('/theme',{
      moduleName
    })
    setData(themeData) // 将请求得到的主题数据放入全局数据 即改变list
  }
  render(themeData) // 将主题数据渲染至页面上
}

我的分析

  1. 从代码的美观程度来说,hasModule方法内部很复杂,需要遍历list从而判断其是否已存在该组件的主题,非常不优雅。
  2. 从代码的时间复杂度来说,hasModule方法内部遍历list数组,时间复杂度高。
  3. 从功能实现上来说,由于某些组件并没有主题,因此请求得到的数据为空数组,setData函数执行后list并没有变化,因此下一次请求该组件的主题时,无法通过list判断是否已请求过该组件主题,此时用户多次操作将进行多次重复请求。

综上所述,我选择方案一作为最终的解决方案。

leader的锐评

  1. 从内存占用的角度讲,项目使用的全局变量管理包会将我们注册的全局变量永久贮存于内存之中,影响项目的运行效率。
  2. 虽然功能上没有完全实现按需请求,会有对某些组件重复请求的问题,但用户很少使用该功能,因此相对于内存的消耗,重复请求的耗时是可以接受的。

综上所述,选择方案二。

总结

我听到leader的想法后才明白,我与前端行业的资深前辈之间也许差的不只是知识的积累,更是思维的转变。我总想着要写出优雅的代码,想着要写出完美的代码,但却忽视了优雅与完美背后往往付出了性能的代价(这个问题是这样的)。

而在面对空间成本与时间成本的取舍问题时,我的考量也并没有切中项目的需求点。

不禁感叹,前端路漫漫,任重而道远,还需要上下求索啊!