用 reactive 维护日程日历事件数据应将事件列表置于顶层 reactive 对象中,配合 shallowRef 存储原始数据、computed 派生视图数据,优先使用数组原地操作而非全量替换,并通过 ID 精准更新以减少 diff 范围。

用 reactive 维护日程日历中的事件数据,关键在于结构清晰、更新可控、避免意外响应开销。它不是简单把事件数组丢进 reactive({ events: [...] }) 就完事,而是要结合视图生命周期、数据变更粒度和性能边界来设计。
事件数据应放在顶层 reactive 状态中
直接用 reactive 包裹一个包含事件列表的对象,是最自然也最符合 Composition API 直觉的方式:
const calendarState = reactive({
events: [] as CalendarEvent[],
currentView: 'month' as 'month' | 'week' | 'day',
currentDate: new Date()
})这样写的好处是:
- 模板里可直接访问
calendarState.events,无需.value - 修改
calendarState.events.push(newEvent)或calendarState.events.splice(...)都能触发响应式更新 - 嵌套属性(如每个事件的
title、start、isCompleted)也自动响应
但要注意:如果事件数组很大(比如上千条),频繁 push/splice 可能引发不必要的渲染。此时建议配合 shallowRef 或分页加载策略,而不是盲目 deep-reactive。
立即学习“前端免费学习笔记(深入)”;
区分「静态事件源」和「动态视图数据」
实际开发中,原始事件数据(来自接口)和当前视图中要渲染的事件(比如只显示本月的)不该混为一谈:
const rawEvents = shallowRef<CalendarEvent[]>([]) // 避免深层响应,节省开销
const visibleEvents = computed(() => {
return rawEvents.value.filter(e =>
isWithinRange(e.start, e.end, calendarState.displayRange)
)
})rawEvents 用 shallowRef 存原始数据,visibleEvents 用 computed 派生——既保证响应链干净,又让视图只关心“此刻该画什么”。
更新事件时优先复用原引用,避免全量替换
不要每次修改都 calendarState.events = [...oldEvents],这会丢失响应性连接。正确做法是:
- ✅ 直接操作数组方法:
calendarState.events.push(event)、calendarState.events.splice(idx, 1) - ✅ 替换某一项:
calendarState.events[idx] = { ...event, isCompleted: true } - ❌ 避免:
calendarState.events = calendarState.events.map(...)—— 这会新建数组,破坏响应追踪
如果必须 map,可用 Object.assign(calendarState.events[idx], newData) 保持引用不变。
配合事件 ID 做精准更新,减少 diff 范围
每个事件要有唯一 id,更新时按 id 查找再改,比遍历整个数组更高效:
const updateEvent = (id: string, updates: Partial<CalendarEvent>) => {
const idx = calendarState.events.findIndex(e => e.id === id)
if (idx !== -1) {
Object.assign(calendarState.events[idx], updates)
}
}这样 Vue 的 patch 过程只重绘对应单元格,而不是整个日历网格。
注意嵌套对象的响应性穿透
如果事件里有深层结构,比如:
{
id: '1',
details: {
location: '会议室A',
attendees: ['张三', '李四']
}
}reactive 默认会深度监听,所以改 event.details.location 也能响应。但若 details 是后端返回的复杂嵌套对象,且你只读不常改,也可考虑用 markRaw 包一层避免过度代理:
import { markRaw } from 'vue'
event.details = markRaw(event.details) // 不再对其做响应式处理不复杂但容易忽略


















