自定义日历组件应分离视图状态与业务数据:用 viewDate 控制渲染月份,selectedDate 独立管理用户选择;通过事件驱动更新,computed 惰性计算派生状态;支持受控/非受控模式及外部约束拦截。

自定义日历组件的状态管理核心在于分离「视图状态」与「业务数据」,避免将日期选择、月份切换、高亮事件等逻辑全部耦合在组件内部。
日期范围与当前视图状态解耦
日历通常需要同时维护两个关键时间维度:一是用户当前浏览的「视图月份」(如 2024-06),用于渲染当月日期格子;二是用户实际选中的「业务日期」(如单选、多选、范围选择)。这两者不应混用同一个响应式变量。
- 用 viewDate(ref 或 reactive)控制当前显示的年月,仅用于计算起始日、天数、周几偏移等渲染所需信息
- 用 selectedDate(s) 独立管理用户操作结果,例如单选用 ref
,范围选择用 ref - 切换月份时只更新 viewDate,不重置 selectedDate —— 用户可能跨月选范围
事件驱动而非手动同步
避免在点击日期后手动更新多个 ref。推荐通过事件触发状态变更,由父组件或组合式函数统一响应。
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 子组件 emit('select', date) 和 emit('change-month', delta),不直接修改父级状态
- 父组件监听 select 事件后,按需更新 selectedDate,并可同步触发 API 请求或表单校验
- 月份切换事件用于更新 viewDate,也可配合 keep-alive 缓存历史月数据,提升滚动体验
不可变更新与派生状态计算
日历中大量依赖派生值(如当月所有日期数组、是否为今天、是否禁用、是否有事件标记),应基于原始状态惰性计算,而非预先生成并维护。
立即学习“前端免费学习笔记(深入)”;
- 用 computed 生成 daysArray:根据 viewDate 的年月,生成包含完整四周(含上月/下月补位)的日期对象数组
- 用 computed 判断是否 today / disabled / inRange / hasEvent,每个判断只依赖最小必要状态(如 today 只比对 Date.now())
- 事件标记数据建议通过 props 传入(如 events: { date: string; title: string }[]),组件内用 Map 或 find 做快速匹配
支持外部受控与非受控模式
增强组件复用性:既允许父组件完全控制 selectedDate(受控),也支持组件内部维护默认选中逻辑(非受控)。
- 通过 defineModel(Vue 3.4+)或 v-model:xxx + props + emits 实现双向绑定
- 内部使用 useVModel 封装逻辑,自动 fallback 到本地 ref 作为默认值
- 禁用状态、最小/最大日期限制等约束条件,应在计算属性中统一拦截,而非在模板里写一堆 v-if

















