跳转到内容

从一个需求出发如何更优雅写代码

刀刀
0字
0分钟
2026/8/16

需求分析

需求如下:

  1. 四个商品推荐栏目,基本样式一致,但文字部分不同。
  2. 颜色和排行类型固定,如热销排行固定第一个红色、第二个橘色,利润排行固定粉色,低价好物固定灰色。
  3. 数据统一返回。循环展示四个商品栏目,数据结构中只有栏目名称、栏目商品售价和利润率。

效果图

后端返回的数据通过一个接口返回数组对象,包含标题、商品价格、商品利润和商品地址。

数据结构
js
let data = [
  {
    title: '热销排行',
    goodsData: [
      {
        price: '100.0',
        profit: 30,
        src: '',
      },
      {
        price: '78.0',
        profit: 20,
        src: '',
      },
    ],
  },
  {
    title: '利润排行',
    goodsData: [
      {
        price: '30.0',
        profit: 130,
        src: '',
      },
      {
        price: '333.0',
        profit: 55,
        src: '',
      },
    ],
  },
  {
    title: '热门主题',
    goodsData: [
      {
        price: '137.0',
        src: '',
      },
      {
        price: '78.0',
        src: '',
      },
    ],
  },
  {
    title: '低价好物',
    goodsData: [
      {
        price: '100.0',
        profit: 30,
        src: '',
      },
      {
        price: '78.0',
        profit: 20,
        src: '',
      },
    ],
  },
]

思路分析

首先通过 v-for 渲染出四个模块。

html
<div v-for="(item, index) in data" :key="index">
  <div>{{ item.title }}</div>
  <div>
    <div v-for="(goods, i) in item.goodsData" :key="i">
      <el-image src="" />
    </div>
  </div>
</div>

接下来分析栏目如何处理渲染,分几种情况:是否渲染、渲染的颜色、渲染的文本等。

处理方案有以下思路:

  1. 四个栏目写成四种文本,通过 v-if 判断显示哪个
  2. 文本部分通过函数返回不同样式
  3. 利用 JSX 根据不同栏目渲染不同样式
  4. 修改源数据

实现

v-if

首先看第一种:通过 v-if 判断文本和索引,渲染不同内容和样式。

html
<div v-for="(item, index) in data" :key="index">
  <div>{{ item.title }}</div>
  <div>
    <div v-for="(goods, i) in item.goodsData" :key="i">
      <el-image src="" />
      <div
        v-if="item.title ==='热销排行' && i === 0"
        style="background: red; color: #fff;"
      >
        Top1
      </div>
      <div
        v-if="item.title ==='热销排行' && i === 1"
        style="background: orange; color: #fff;"
      >
        Top2
      </div>
      <div
        v-if="item.title ==='利润排行'"
        style="background: pink; color: #000;"
      >
        {{ goods.profit }}%
      </div>
      <div
        v-if="item.title ==='低价好物'"
        style="background: #555; color: #000;"
      >
        {{ goods.price }}
      </div>
    </div>
  </div>
</div>

该方式简单直接,缺点是 HTML 代码量多且冗余,后续不易维护,不推荐使用。

函数返回

div 盒子内容一致,可通过一个函数统一返回,减少代码量。

vue
<script setup>
const createStyle = (title, index) => {
  console.log('执行了该方法')
  let styleObj = {}
  switch (title) {
    case '热销排行':
      styleObj.color = '#fff'
      if (index === 0) styleObj.background = 'red'
      else styleObj.background = 'orange'
      break
    case '利润排行':
      styleObj.color = '#000'
      styleObj.background = 'pink'
      break
    case '低价好物':
      styleObj.color = '#000'
      styleObj.background = 'gray'
      break
    case '热门主题':
      styleObj.display = 'none'
      break
    default:
      break
  }
  return styleObj
}
const createText = (title, index, good) => {
  console.log('执行了该方法')
  let txt = ''
  switch (title) {
    case '热销排行':
      if (index === 0) txt = 'Top1'
      else txt = 'Top2'
      break
    case '利润排行':
      txt = good.profit + '%'
      break
    case '低价好物':
      txt = good.price
      break
    default:
      break
  }
  return txt
}
</script>

<template>
  <div
    @click="
      () => {
        num += 1
      }
    "
  >
    点击事件触发,createStyle与createText函数依旧触发:{{ num }}
  </div>
  <div v-for="(item, index) in data" :key="index">
    <div>{{ item.title }}</div>
    <div>
      <div v-for="(goods, i) in item.goodsData" :key="i">
        <el-image src="" />
        <div :style="createStyle(item.title, i)">
          {{ createText(item.title, i, goods) }}
        </div>
      </div>
    </div>
  </div>
</template>

虽然 template 内的代码量减少了,但是函数被执行了多次,且在操作 num 的点击事件时,因为该Vue文件的渲染依赖于变量 num ,在 num 发生变化时,整个组件都会触发更新,因此函数方法都会重新执行,造成一定的性能压力。

JSX

vue
<script setup lang="jsx">
function TextComponent({ title, index, goods }) {
  console.log('执行了。')
  let styleObj = {}
  let text = ''
  switch (title) {
    case '热销排行':
      if (index === 0) {
        styleObj.background = 'red'
        styleObj.color = '#fff'
        text = 'Top1'
      } else {
        styleObj.background = 'orange'
        styleObj.color = '#fff'
        text = 'Top2'
      }
      break
    case '利润排行':
      styleObj.background = 'pink'
      styleObj.color = '#000'
      text = goods.profit + '%'
      break
    case '热门主题':
      styleObj.display = 'none'
      break
    case '低价好物':
      styleObj.background = 'gray'
      styleObj.color = '#000'
      text = goods.price
      break
    default:
      break
  }
  return <div style={styleObj}>{text}</div>
}
</script>

<template>
  <div
    @click="
      () => {
        num += 1
      }
    "
  >
    {{ num }}
  </div>
  <div v-for="(item, index) in data" :key="index">
    <div>{{ item.title }}</div>
    <div>
      <div v-for="(goods, i) in item.goodsData" :key="i">
        <el-image src="" />
        <TextComponent
          :title="item.title"
          :index="i"
          :goods="goods"
        ></TextComponent>
      </div>
    </div>
  </div>
</template>

该方法仅在初始时执行了八次,后续修改无关变量(如 num)时不会额外触发,性能更佳。

代码层面,还可使用策略模式进一步简化。

jsx
function TextComponent({ title, index, goods }) {
  console.log('执行了。')
  let styleObj = {}
  let text = ''
  let emnu = {
    热销排行: function () {
      if (index === 0) {
        styleObj.background = 'red'
        styleObj.color = '#fff'
        text = 'Top1'
      } else {
        styleObj.background = 'orange'
        styleObj.color = '#fff'
        text = 'Top2'
      }
    },
    利润排行: function () {
      styleObj.background = 'pink'
      styleObj.color = '#000'
      text = goods.profit + '%'
    },
    热门主题: function () {
      styleObj.display = 'none'
    },
    低价好物: function () {
      styleObj.background = 'gray'
      styleObj.color = '#000'
      text = goods.price
    },
  }

  obj[title]()
  return <div style={styleObj}>{text}</div>
}

修改源数据

通过修改源数据统一字段,渲染时直接渲染无需判断。

vue
<script setup>
data.forEach((item, index) => {
  switch (item.title) {
    case '热销排行':
      item.goodsData.forEach((goods, i) => {
        goods.styleObj = {
          background: 'red',
          color: '#fff',
        }
        goods.text = 'Top1'
        if (i !== 0) {
          goods.styleObj.background = 'orange'
          goods.text = 'Top2'
        }
      })
      break
    // ...省略
    default:
      break
  }
})
</script>

<template>
  <div v-for="(item, index) in data" :key="index">
    <div>{{ item.title }}</div>
    <div>
      <div v-for="(goods, i) in item.goodsData" :key="i">
        <el-image src="" />
        <div :style="goods.styleObj">{{ goods.text }}</div>
      </div>
    </div>
  </div>
</template>

比较

以上几种方法各有优缺点:

  • 函数方法在组件数据变化引发更新时,等于组件重新执行渲染,造成性能浪费
  • JSX 要求使用者了解相关内容,可读性较差
  • 源数据改写在数据复杂时较麻烦,若该数据还需传回后端则需取出额外内容

总体效果

跳转预览:点击跳转

贡献者

The avatar of contributor named as v_duyilin v_duyilin
The avatar of contributor named as duyidao duyidao
The avatar of contributor named as 刀刀 刀刀
The avatar of contributor named as 平安喜乐 平安喜乐

页面历史

刀刀博客累计访客 人;文档累计访问量共