fullcalendar日历插件的基本使用(vue)

fullcalendar日历插件的基本使用(vue)

一、背景与问题

在现代Web开发中,日历组件是处理时间轴数据的核心工具。fullcalendar作为开源社区最成熟的日历组件,支持丰富的功能如事件拖拽、日程编辑、多视图切换等。在Vue生态中,开发者需要理解其底层原理才能高效使用。

当前开发场景中,常见的问题包括:

  • 事件数据未正确渲染
  • 拖拽操作失效
  • 性能优化不足
  • 与Vue响应式系统耦合不紧密
  • 自定义样式难以控制

理解fullcalendar的工作机制,能帮助开发者避免常见陷阱并优化应用场景。

二、基本原理

fullcalendar基于jQuery开发,其核心机制包含:

  1. 事件系统:通过eventSources管理事件数据,支持动态加载和更新
  2. 视图系统:包含月视图、周视图、日视图等,通过views配置切换
  3. 渲染机制:使用虚拟DOM技术优化DOM操作,通过ref获取组件实例
  4. 交互系统:支持事件拖拽、拖拽结束、点击事件等交互行为

在Vue中,需要通过ref获取组件实例,配合Vue的响应式系统实现数据绑定。核心原理是通过Vue的nextTick确保DOM更新后执行fullcalendar的初始化。

三、环境准备

1. 项目依赖

npm install fullcalendar
npm install @fullcalendar/daygrid
npm install @fullcalendar/interaction
npm install @fullcalendar/heatmap
注意:v5版本需要显式安装插件,v6版本已整合到核心库中

2. 引入样式

<template>
  <div id="calendar"></div>
</template>

<script>
import { Calendar } from '@fullcalendar/vue'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'

export default {
  components: {
    Calendar
  },
  mounted() {
    this.$nextTick(() => {
      new Calendar(document.getElementById('calendar'), {
        plugins: [dayGridPlugin, interactionPlugin],
        initialView: 'dayGridMonth',
        events: [
          { title: 'Meeting', start: '2023-04-01' }
        ]
      })
    })
  }
}
</script>

四、核心实现

1. 基础日历组件

<template>
  <div id="calendar" ref="calendar"></div>
</template>

<script>
import { Calendar } from '@fullcalendar/vue'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'

export default {
  components: {
    Calendar
  },
  data() {
    return {
      events: [
        { title: 'Project Kickoff', start: '2023-04-01' },
        { title: 'Team Meeting', start: '2023-04-05T10:00:00', end: '2023-04-05T11:00:00' }
      ]
    }
  },
  mounted() {
    this.$nextTick(() => {
      const calendar = new Calendar(this.$refs.calendar, {
        plugins: [dayGridPlugin, interactionPlugin],
        initialView: 'dayGridMonth',
        events: this.events,
        headerToolbar: {
          left: 'prev,next today',
          center: 'title',
          right: 'dayGridMonth,dayGridWeek,dayGridDay'
        }
      })
      calendar.render()
    })
  }
}
</script>

关键代码解释:

  • this.$nextTick确保DOM渲染完成后初始化日历
  • ref属性用于获取DOM节点
  • events数据绑定到日历组件
  • headerToolbar配置导航栏

2. 事件交互功能

<template>
  <div id="calendar" ref="calendar"></div>
</template>

<script>
import { Calendar } from '@fullcalendar/vue'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'

export default {
  components: {
    Calendar
  },
  data() {
    return {
      events: [
        { title: 'Meeting', start: '2023-04-01' }
      ]
    }
  },
  mounted() {
    this.$nextTick(() => {
      const calendar = new Calendar(this.$refs.calendar, {
        plugins: [dayGridPlugin, interactionPlugin],
        initialView: 'dayGridMonth',
        events: this.events,
        editable: true,
        eventDrop: (arg) => {
          console.log('Event dropped:', arg.event.title)
          // 更新事件数据
          this.events = this.events.map(event => 
            event.id === arg.event.id ? { ...event, start: arg.event.start } : event
          )
        },
        eventResize: (arg) => {
          console.log('Event resized:', arg.event.title)
          this.events = this.events.map(event => 
            event.id === arg.event.id ? { ...event, start: arg.event.start, end: arg.event.end } : event
          )
        }
      })
      calendar.render()
    })
  }
}
</script>

关键代码解释:

  • editable: true启用事件编辑功能
  • eventDrop处理事件拖拽
  • eventResize处理事件大小调整
  • 通过响应式数据更新事件列表

3. 资源视图实现

<template>
  <div id="calendar" ref="calendar"></div>
</template>

<script>
import { Calendar } from '@fullcalendar/vue'
import resourceTimePlugin from '@fullcalendar/resource-time'
import interactionPlugin from '@fullcalendar/interaction'

export default {
  components: {
    Calendar
  },
  data() {
    return {
      resources: [
        { id: 1, title: 'Resource A' },
        { id: 2, title: 'Resource B' }
      ],
      events: [
        { id: 1, title: 'Task 1', resourceId: 1, start: '2023-04-01' },
        { id: 2, title: 'Task 2', resourceId: 2, start: '2023-04-05' }
      ]
    }
  },
  mounted() {
    this.$nextTick(() => {
      const calendar = new Calendar(this.$refs.calendar, {
        plugins: [resourceTimePlugin, interactionPlugin],
        initialView: 'resourceTimeDay',
        resources: this.resources,
        events: this.events,
        eventColor: 'orange'
      })
      calendar.render()
    })
  }
}
</script>

关键代码解释:

  • 使用resourceTimePlugin实现资源视图
  • resources配置资源信息
  • events关联资源ID
  • eventColor设置事件颜色

五、完整案例

1. 项目结构

src/
├── components/
│   └── CalendarView.vue
├── assets/
│   └── styles.css
├── utils/
│   └── calendarHelpers.js
└── main.js

2. CalendarView.vue组件

<template>
  <div class="calendar-container">
    <div id="calendar" ref="calendar"></div>
    <div class="event-form">
      <input v-model="newEvent.title" placeholder="事件标题">
      <input type="date" v-model="newEvent.start">
      <button @click="addEvent">添加事件</button>
    </div>
  </div>
</template>

<script>
import { Calendar } from '@fullcalendar/vue'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'
import resourceTimePlugin from '@fullcalendar/resource-time'

export default {
  components: {
    Calendar
  },
  data() {
    return {
      events: [
        { id: 1, title: 'Project Kickoff', start: '2023-04-01' },
        { id: 2, title: 'Team Meeting', start: '2023-04-05T10:00:00', end: '2023-04-05T11:00:00' }
      ],
      resources: [
        { id: 1, title: 'Resource A' },
        { id: 2, title: 'Resource B' }
      ],
      newEvent: {
        title: '',
        start: ''
      }
    }
  },
  mounted() {
    this.$nextTick(() => {
      const calendar = new Calendar(this.$refs.calendar, {
        plugins: [dayGridPlugin, interactionPlugin, resourceTimePlugin],
        initialView: 'resourceTimeDay',
        events: this.events,
        resources: this.resources,
        eventColor: 'orange',
        editable: true,
        eventDrop: this.handleEventDrop,
        eventResize: this.handleEventResize,
        eventClick: this.handleEventClick
      })
      calendar.render()
    })
  },
  methods: {
    addEvent() {
      if (this.newEvent.title && this.newEvent.start) {
        const newEvent = {
          id: Date.now(),
          title: this.newEvent.title,
          start: this.newEvent.start,
          resourceId: 1
        }
        this.events.push(newEvent)
        this.newEvent = { title: '', start: '' }
      }
    },
    handleEventDrop(arg) {
      console.log('Event dropped:', arg.event.title)
      this.events = this.events.map(event => 
        event.id === arg.event.id ? { ...event, start: arg.event.start } : event
      )
    },
    handleEventResize(arg) {
      console.log('Event resized:', arg.event.title)
      this.events = this.events.map(event => 
        event.id === arg.event.id ? { ...event, start: arg.event.start, end: arg.event.end } : event
      )
    },
    handleEventClick(arg) {
      console.log('Event clicked:', arg.event.title)
      this.$confirm('是否删除该事件?').then(() => {
        this.events = this.events.filter(event => event.id !== arg.event.id)
      })
    }
  }
}
</script>

<style scoped>
.calendar-container {
  padding: 20px;
}
.event-form {
  margin-top: 20px;
}
</style>

3. 路由配置

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import CalendarView from '../components/CalendarView.vue'

export default createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/calendar',
      name: 'Calendar',
      component: CalendarView
    }
  ]
})

4. 主应用入口

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import '@fullcalendar/common/main.css'
import '@fullcalendar/daygrid/main.css'
import '@fullcalendar/resource-time/main.css'

createApp(App)
  .use(router)
  .mount('#app')

六、源码解析

以eventDrop事件处理为例,深入分析其工作原理:

handleEventDrop(arg) {
  console.log('Event dropped:', arg.event.title)
  this.events = this.events.map(event => 
    event.id === arg.event.id ? { ...event, start: arg.event.start } : event
  )
}
  1. 事件参数解析:arg包含事件对象、拖拽后的新时间等信息
  2. 数据更新:通过map函数更新事件数据
  3. 响应式更新:Vue会自动重新渲染日历组件

完整流程:

  • 用户拖动事件
  • fullcalendar触发eventDrop回调
  • 组件更新事件数据
  • Vue触发重新渲染
  • fullcalendar重新渲染日历

七、进阶使用

1. 动态数据加载

mounted() {
  this.$nextTick(() => {
    const calendar = new Calendar(this.$refs.calendar, {
      plugins: [dayGridPlugin, interactionPlugin],
      initialView: 'dayGridMonth',
      events: this.getEvents(),
      eventSources: [
        {
          url: '/api/events',
          method: 'GET'
        }
      ]
    })
    calendar.render()
  })
},
methods: {
  getEvents() {
    return this.$axios.get('/api/events').then(res => res.data)
  }
}

2. 动态视图切换

<template>
  <div class="controls">
    <button @click="switchToDay">日视图</button>
    <button @click="switchToWeek">周视图</button>
    <button @click="switchToMonth">月视图</button>
  </div>
</template>

<script>
export default {
  methods: {
    switchToDay() {
      this.$refs.calendar.getApi().changeView('dayGridDay')
    },
    switchToWeek() {
      this.$refs.calendar.getApi().changeView('dayGridWeek')
    },
    switchToMonth() {
      this.$refs.calendar.getApi().changeView('dayGridMonth')
    }
  }
}
</script>

3. 资源视图优化

<template>
  <div id="calendar" ref="calendar"></div>
</template>

<script>
import { Calendar } from '@fullcalendar/vue'
import resourceTimePlugin from '@fullcalendar/resource-time'
import interactionPlugin from '@fullcalendar/interaction'

export default {
  components: {
    Calendar
  },
  data() {
    return {
      resources: [
        { id: 1, title: 'Resource A' },
        { id: 2, title: 'Resource B' }
      ],
      events: [
        { id: 1, title: 'Task 1', resourceId: 1, start: '2023-04-01' },
        { id: 2, title: 'Task 2', resourceId: 2, start: '2023-04-05' }
      ]
    }
  },
  mounted() {
    this.$nextTick(() => {
      const calendar = new Calendar(this.$refs.calendar, {
        plugins: [resourceTimePlugin, interactionPlugin],
        initialView: 'resourceTimeDay',
        resources: this.resources,
        events: this.events,
        eventColor: 'orange',
        editable: true,
        resourceLabelFormat: (resource) => `${resource.title} (ID: ${resource.id})`,
        eventClick: this.handleEventClick
      })
      calendar.render()
    })
  }
}
</script>

八、性能与工程实践

1. 性能优化策略

优化措施说明
分页加载对大量事件使用eventSource分页加载
懒加载对资源视图使用resourceTime的懒加载机制
虚拟滚动对大量日期使用scrollable视图
事件缓存对频繁更新的事件使用内存缓存
响应式优化使用v-lazy对日历组件进行懒加载

2. 异常处理

mounted() {
  this.$nextTick(() => {
    try {
      const calendar = new Calendar(this.$refs.calendar, {
        plugins: [dayGridPlugin, interactionPlugin],
        initialView: 'dayGridMonth',
        events: this.events
      })
      calendar.render()
    } catch (error) {
      console.error('日历初始化失败:', error)
      this.$notify.error({
        title: '错误',
        message: '日历组件加载失败,请刷新页面重试'
      })
    }
  })
}

3. 安全考虑

  • XSS防护:对用户输入的事件标题进行转义
  • 数据验证:对时间格式进行严格校验
  • 权限控制:对事件的增删改操作进行权限校验
  • CSRF防护:对涉及数据修改的操作添加CSRF token

九、常见问题与踩坑

1. 常见错误及解决办法

错误现象原因解决办法
事件未显示未正确引入插件确认安装了dayGridPlugin和interactionPlugin
拖拽失效未启用editable设置editable: true
样式错乱缺少CSS文件引入@fullcalendar/common/main.css
组件未渲染未使用$nextTick在mounted钩子中使用$nextTick
事件数据未更新未正确更新响应式数据使用this.$set更新数组元素
资源视图卡顿数据量过大使用resourceTime的懒加载机制

2. 典型错误示例

// 错误示例:未使用$nextTick
mounted() {
  new Calendar(this.$refs.calendar, { /* ... */ })
}
// 正确示例:使用$nextTick确保DOM渲染
mounted() {
  this.$nextTick(() => {
    new Calendar(this.$refs.calendar, { /* ... */ })
  })
}

十、最佳实践

1. 推荐方案

  • 使用resourceTime插件处理资源分配场景
  • 对大数据量使用分页加载
  • 对关键操作添加确认对话框
  • 使用eventClick处理事件点击事件
  • 对事件进行严格的数据校验

2. 不推荐场景

  • 简单的时间轴展示(建议使用vue-datetime)
  • 需要极高的性能要求(建议使用v-calendar)
  • 需要完全自定义渲染(建议使用v-calendar)
  • 需要复杂的权限控制(建议结合vue-axios)

3. 性能优化建议

  • 使用eventSources进行分页加载
  • 对资源视图使用resourceTime的懒加载
  • 对大量日期使用scrollable视图
  • 对频繁更新的事件使用内存缓存
  • 使用v-lazy对日历组件进行懒加载

十一、总结

fullcalendar作为成熟的日历组件,在Vue中具有广泛的应用场景。通过理解其工作原理,开发者可以更有效地利用其功能。在实际项目中,需要根据具体需求选择合适的视图类型和插件组合。对于处理复杂日程管理、资源分配等场景,fullcalendar提供了丰富的功能支持。同时,也要注意其性能限制,通过合理的设计和优化来保证应用的稳定性。在开发过程中,需要特别注意响应式数据的更新和异常处理,以确保应用的健壮性。

VUE
最后修改于:2026年09月20日 20:00

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日