从一个需求出发如何更优雅写代码
刀刀
4/8/2025
0 字
0 分钟
- UP主:三十的前端课,视频地址:前往学习
需求分析
下面来看一个需求:
- 四个商品推荐栏目,问题在于,四个栏目基本样式都一样,但是文字部分不一样。
- 颜色和排行类型,写死固定,比如热销排行,固定第一个红色,第二个橘色,利润排行固定粉色,低价好物固定灰色。
- 数据一起返回。用循环展示四个商品栏目,数据结构里只有栏目名字,栏目商品的售价和利润率。
接着看一下后端的数据返回的数据,是通过一个接口返回一个数组对象,都存放着标题、商品价格、商品利润和商品地址。
数据结构
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>
接下来要分析的是栏目该如何处理渲染,它分几种情况:是否渲染、渲染的颜色、渲染的文本等。
关于处理方案,此处有几个不同的思路:
- 四个栏目写成四种文本,通过
v-if
判断显示哪个 - 文本部分不同的样式文字通过函数返回
- 利用 JSX 根据不同栏目渲染不同的样式
- 修改源数据
实现
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 要求使用者了解相关内容,可读性差
- 源数据改写在数据比较复杂的情况下改写会麻烦,若该数据还需要传回给后端还需要取出额外内容