如何在ViewModel中遵循DRY原则?优化MVVM视图模型重复代码
如何在MVVM的ViewModel转换函数中遵循DRY原则实现默认值回退?
在MVVM应用中,我们经常需要编写将原始数据(比如后端返回的entry)转换为ViewModel的函数。当未传入entry参数时,函数需要返回一套默认的回退值,但直接分情况返回两个完整对象会导致大量代码重复,违反**DRY(Don't Repeat Yourself)**原则。
优化方案:基于默认模板的对象合并
核心思路是先定义一份完整的默认ViewModel模板,当存在有效entry时,通过对象展开运算符覆盖默认值,同时处理需要特殊转换的字段(如时间格式化、数组映射、时长计算等)。这样既避免了代码重复,又能清晰区分默认值和转换逻辑。
优化后的代码如下:
import { nanoid } from 'nanoid'; import dayjs from 'dayjs'; // 定义默认的ViewModel模板 const defaultViewModel = { id: null, type: 0, title: null, slug: null, category: null, summary: null, content: null, video: null, duration: { hours: null, minutes: null }, createdAt: dayjs(), updatedAt: dayjs(), isHidden: false, isDeleted: false, notes: [], steps: [], keywords: [], covers: [] }; export default function(entry) { // 若无entry,直接返回默认模板的副本 if (!entry) { return { ...defaultViewModel }; } // 基于默认模板,覆盖entry对应的字段并处理特殊转换 return { ...defaultViewModel, id: entry.id, type: entry.type ?? defaultViewModel.type, title: entry.title, slug: entry.slug, category: entry.category, summary: entry.summary, content: entry.body, video: entry.video, duration: entry.duration === null ? defaultViewModel.duration : { hours: Math.floor(entry.duration / 60), minutes: entry.duration % 60 }, createdAt: dayjs(entry.createdAt), updatedAt: dayjs(entry.updatedAt), isHidden: entry.isHidden, isDeleted: entry.isDeleted, notes: entry.notes.map(item => ({ key: nanoid(), value: item.body })), steps: entry.steps.map(item => ({ key: nanoid(), value: item.body, image: item.image })), keywords: entry.keywords.map(item => ({ key: nanoid(), value: item.body })), covers: entry.covers }; }
关键细节说明
- 默认模板复用:把所有字段的默认值集中定义在
defaultViewModel里,避免重复编写相同的键值对。 - 对象展开运算符:通过
...defaultViewModel快速继承所有默认值,再针对有entry数据的字段进行覆盖,仅编写需要转换或替换的逻辑。 - 空值回退处理:对于
type这类可能为空的字段,使用??运算符确保当entry.type不存在时,依然保留默认值;duration字段则通过条件判断直接复用默认的时长对象。 - 数组转换逻辑:仅在有entry时才执行数组的
map转换,无entry时直接使用默认的空数组。
这种写法既保持了代码的简洁性,又符合DRY原则,后续修改字段默认值或转换逻辑时,只需在对应位置修改即可,维护成本更低。
内容的提问来源于stack exchange,提问作者Rusurano
相关产品推荐
相关产品推荐

