深色模式
写了两年 Vue,最近开始反思组件该怎么设计。总结几个让组件更可维护的原则。
单一职责
每个组件只做一件事:
javascript
// ❌ 一个组件做了太多事
// UserPage.vue:展示用户信息 + 处理权限 + 管理分页 + 调用 API
export default {
data() {
return { user: null, permissions: [], list: [], page: 1 };
},
created() {
this.fetchUser();
this.fetchPermissions();
this.fetchList();
},
// 200行代码...
};
// ✅ 拆分关注点
// UserProfile.vue:只展示用户信息
// PermissionPanel.vue:权限管理
// UserList.vue:列表展示(含分页)
// UserPage.vue:组合以上组件,处理页面级逻辑Props 要尽量原子化
javascript
{% raw %}
// ❌ 传入整个 user 对象,组件依赖了对象的结构
props: { user: Object }
// 模板里:{{ user.profile.avatar }}
// ✅ 只传组件真正需要的数据
props: {
name: String,
avatarUrl: String,
role: String
}
{% endraw %}好处:组件更容易测试,依赖更清晰。
事件命名:用动词
javascript
// ❌ 事件名含糊
this.$emit("click");
this.$emit("change");
this.$emit("update");
// ✅ 事件名描述发生了什么
this.$emit("user:saved", savedUser);
this.$emit("filter:changed", newFilter);
this.$emit("item:deleted", itemId);避免直接修改 Props
javascript
// ❌ 修改 prop(Vue 会警告)
props: { value: String },
methods: {
clear() { this.value = '' } // 不能这样做!
}
// ✅ 触发事件,让父组件修改
props: { value: String },
methods: {
clear() { this.$emit('update:value', '') }
}
// 父组件
<MyInput :value="text" @update:value="text = $event" />
// 或用 .sync 语法糖
<MyInput :value.sync="text" />可配置的默认值
javascript
props: {
size: {
type: String,
default: 'medium',
validator: (v) => ['small', 'medium', 'large'].includes(v)
},
loading: {
type: Boolean,
default: false
}
}受控与非受控
受控组件:数据由父组件控制(通过 v-model / props)
javascript
// 受控:父组件控制值
<SearchInput :value="searchText" @input="searchText = $event" />
// 非受控:内部自己管理状态
// 只在需要时通过事件向外抛出结果
<DatePicker @change="handleDateSelect" />根据场景选择,不是所有状态都需要提升到父组件。
文档化
哪怕只是注释,也要说明 prop 的用途:
javascript
props: {
// 列表数据,格式:[{ id, name, status }]
items: {
type: Array,
required: true
},
// 选中项的 id,支持 .sync 双向绑定
selectedId: {
type: [Number, null],
default: null
}
}小结
- 单一职责:一个组件只做一件事,拆分是美德
- Props 原子化:只传真正需要的数据
- 用事件描述行为,不要直接修改 props
- 组件要有合理的默认值和参数校验