详解怎么使用vue封装一个自定义日历组件

怎么开发一个自定义日历的vue组件,下面本篇文章就手把手教你如何封装一个自定义日历组件,希望对大家有所帮助!

详解怎么使用vue封装一个自定义日历组件

众所周知啊,一般来说,如果项目中有需要用到日历组件,往往是找第三方UI库中的组件来使用,或者是找现成的其他第三方插件。对于很多小伙伴来说,第一眼看到日历组件,下意识的就会觉得很复杂,无从下手。但是当我阅读了这个日历插件的源码之后,发现并没有我想象中的复杂。我以前傻傻得认为,想要做一个日历组件,得需要把距离现在年份前后至少十年的日历数据都获取到,然后才能进行下一步的开发。

然而,在我尝试着阅读了dycalendar.js这个库的源码之后,一方面感觉自己太笨了,把问题想得太复杂了。另外也感慨作者思路之清晰。看完之后感觉受益匪浅。

在将作者的思路逻辑梳理完毕后,我依据这个思路开发了一个vue组件。如下图所示:

立即学习“前端免费学习笔记(深入)”;

自定义日历效果图.gif

接下来,就随着我一起看看如何开发一个自己的日历组件吧。【相关推荐:dycalendar.js、dycalendar.js】

核心代码实现

1、梳理思路

获取到目标日期数据获取到当前日期的各项重要属性,诸如当前年,当前月,当前日期,当前星期几,当前月一共有几天,当前月的第一天对应的是星期几,上个月总共有多少天等。根据这些属性,来生成具体的日历日期数据列表,然后将其循环渲染到模板中。当切换月份的时候,获取到新的目标日期对应的各项关键数据。vue检测到日历属性变化之后,通知页面进行更新。

2、初始化所需要的数据

一般来说,成熟的日历组件,日期都是一个双向绑定的变量。为了方便使用,我们也采用双向绑定的方式。

import { reactive, ref, computed, watch } from "vue";const props = defineProps({  modelValue: Date,});const emits = defineEmits(["update:modelValue"]);/** * 最小年份 */const MIN_YEAR = 1900;/** * 最大年份 */const MAX_YEAR = 9999;/** * 目标日期 */const targetDate = ref(props.modelValue);复制代码

登录后复制

接下来,我们还需要初始化一些常量用来表示月份和日期:

/** * 有关月度的名称列表 */const monthNameList = {  chineseFullName: [    "一月",    "二月",    "三月",    "四月",    "五月",    "六月",    "七月",    "八月",    "九月",    "十月",    "十一月",    "十二月",  ],  fullName: [    "January",    "February",    "March",    "April",    "May",    "June",    "July",    "August",    "September",    "October",    "November",    "December",  ],  mmm: [    "Jan",    "Feb",    "Mar",    "Apr",    "May",    "Jun",    "Jul",    "Aug",    "Sep",    "Oct",    "Nov",    "Dec",  ],};/** * 有关周几的名称列表 */const dayNameList = [  {    chineseFullName: "周日",    chineseShortName: "日",    fullName: "Sunday",    shortName: "Sun",    dayNumber: 0,  },  {    chineseFullName: "周一",    chineseShortName: "一",    fullName: "Monday",    shortName: "Mon",    dayNumber: 1,  },  {    chineseFullName: "周二",    chineseShortName: "二",    fullName: "Tuesday",    shortName: "Tue",    dayNumber: 2,  },  {    chineseFullName: "周三",    chineseShortName: "三",    fullName: "Wednesday",    shortName: "Wed",    dayNumber: 3,  },  {    chineseFullName: "周四",    chineseShortName: "四",    fullName: "Thursday",    shortName: "Thu",    dayNumber: 4,  },  {    chineseFullName: "周五",    chineseShortName: "五",    fullName: "Friday",    shortName: "Fri",    dayNumber: 5,  },  {    chineseFullName: "周六",    chineseShortName: "六",    fullName: "Saturday",    shortName: "Sat",    dayNumber: 6,  },];复制代码

登录后复制

接下来,准备几个vue的响应式数据:

/** * 今日 */const today = new Date();/** * 日历的各项属性 */const calendarProps = reactive({  target: {    year: null,    month: null,    date: null,    day: null,    monthShortName: null,    monthFullName: null,    monthChineseFullName: null,    firstDay: null,    firstDayIndex: null,    totalDays: null,  },  previous: {    totalDays: null,  },});/** * 用于展现的日历数据 */const calendarData = ref([]);复制代码

登录后复制

3、初始化日历的各项属性

接下来,通过setCalendarProps方法获取日历的各个属性,逐个填充calendarProps中的数据:

function setCalendarProps() {  if (!targetDate.value) {    targetDate.value = today;  }  // 获取目标日期的年月日星期几数据  calendarProps.target.year = targetDate.value.getFullYear();  calendarProps.target.month = targetDate.value.getMonth();  calendarProps.target.date = targetDate.value.getDate();  calendarProps.target.day = targetDate.value.getDay();  if (    calendarProps.target.year  MAX_YEAR  ) {    console.error("无效的年份,请检查传入的数据是否是正常");    return;  }  // 获取到目标日期的月份【中文】名称  let dateString;  dateString = targetDate.value.toString().split(" ");  calendarProps.target.monthShortName = dateString[1];  calendarProps.target.monthFullName =    monthNameList.fullName[calendarProps.target.month];  calendarProps.target.monthChineseFullName =    monthNameList.chineseFullName[calendarProps.target.month];  // 获取目标月份的第一天是星期几,和在星期几中的索引值  const targetMonthFirstDay = new Date(    calendarProps.target.year,    calendarProps.target.month,    1  );  calendarProps.target.firstDay = targetMonthFirstDay.getDay();  calendarProps.target.firstDayIndex = dayNameList.findIndex(    (day) => day.dayNumber === calendarProps.target.firstDay  );  // 获取目标月份总共多少天  const targetMonthLastDay = new Date(    calendarProps.target.year,    calendarProps.target.month + 1,    0  );  calendarProps.target.totalDays = targetMonthLastDay.getDate();  // 获取目标月份的上个月总共多少天  const previousMonth = new Date(    calendarProps.target.year,    calendarProps.target.month,    0  );  calendarProps.previous.totalDays = previousMonth.getDate();}复制代码

登录后复制

需要注意的一个知识点是,在获取本月多少天和上个月多少天的时候,都将date值设置为了0。这是因为当date值为0的时候,返回的Date对象是上个月的最后一天。所以说,为了获取本月多少天,需要将本月的month值加1。

执行这个方法之后,此时calendarProps的值为:

image.png

4、根据日历属性生成日历日期的数据

当我们已经知道本月第一天对应的周几索引值本月一共有多少天上个月一共有多少天这三个核心数据之后,就可以开始生成对应的日历数据了。

思路如下

由于大部分情况下,本月的第一天不是从头开始的,之前的部分是上个月的日期。所以第一行要单独进行处理。设置一个公用的date数值,初始值设置为1。然后从本月第一天对应的周几索引值开始进行递增。本月之前的日期和之后的日期设置一个算法进行计算。为了方便之后进行日期切换、样式区分,将生成的数据加工成一个对象,其中包含日期类型——dateType,表示是本月还是上月还是下月;

/** * 生成日历的数据 */function setCalendarData() {  let i;  let date = 1;  const originData = [];  const firstRow = [];  // 设置第一行数据  for (i = 0; i <= 6; i++) {    // 设置目标月份之前月份的日期数据    if (i < calendarProps.target.firstDayIndex) {      const previousDate =        calendarProps.previous.totalDays -        calendarProps.target.firstDayIndex +        (i + 1);      firstRow.push({        dateObj: new Date(          calendarProps.target.year,          calendarProps.target.month - 1,          previousDate        ),        dateNumber: previousDate,        dateType: "previous"      });    } else {      // 设置目标月份当月的日期数据      firstRow.push({        dateObj: new Date(          calendarProps.target.year,          calendarProps.target.month,          date        ),        dateNumber: date,        dateType: "current"      });      date++;    }  }  originData.push(firstRow);  // 设置后面五行的数据  for (let j = 0; j <= 4; j++) {    const rowData = [];    for (let k = 0; k <= 6; k++) {      // 设置目标月份剩下的日期数据      if (date <= calendarProps.target.totalDays) {        rowData.push({          dateObj: new Date(            calendarProps.target.year,            calendarProps.target.month,            date          ),          dateNumber: date,          dateType: "current"        });      } else {        // 设置目标月份下个月的日期数据        const nextDate = date - calendarProps.target.totalDays;        rowData.push({          dateObj: new Date(            calendarProps.target.year,            calendarProps.target.month + 1,            nextDate          ),          dateNumber: nextDate,          dateType: "next"        });      }      date++;    }    originData.push(rowData);  }  calendarData.value = originData;}复制代码

登录后复制

至此,这个日历组件的核心部分的逻辑就已经实现了。你看,是不是很简单?

接下来,我们只需要根据calendarData中的数据渲染出相应的html模板和添加上样式就可以了。

5、添加模板和样式部分

一般来说,日历组件都是网格状的结构,所以我选择table的方式进行渲染。不过你要是问我还有没有别的方式,那还是有的,比如使用flex布局或者grid布局,但是如果采用这种方式的话,calendarData的数据结构就不是现在这个样子了。

dom结构如下图:

image.png

至于按钮边框的流动效果,是我参照苏苏的文章做的,详情请见:

Clip-path实现按钮流动边框动画 dycalendar.js

然后剩下的样式部分,即兴发挥或者根据UI设计图绘制即可。想必各位都领教过UI姐姐们精美的设计图吧(嘻嘻

具体的代码部分就不贴在文章中了,如有需要可以直接查看下方的完整源码

dycalendar.js

结语

有些感觉很麻烦的组件,可能核心逻辑往往不是那么复杂。有些时候,可能仅仅是需要一些耐心,将代码一行一行的拆解出来阅读,理清楚其中的思路。

(学习视频分享:dycalendar.js、dycalendar.js)

以上就是详解怎么使用vue封装一个自定义日历组件的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/3230554.html

(0)
上一篇 2025年4月1日 16:38:57
下一篇 2025年3月1日 02:16:50

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 详解vue各种权限控制与管理的实现思路

    本篇文章给大家带来了关于vue的相关知识,其中主要详细介绍了vue各种权限控制与管理的实现思路,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。 一、 菜单权限 菜单权限:控制用户在系统中能够看到哪些菜单项菜单权限指的就是后台系统中的左侧…

    2025年4月1日
    100
  • 分享一个极致舒适的Vue页面保活方案

    本篇文章给大家带来了关于vue的相关知识,其中主要跟大家分享一个极致舒适的vue页面保活方案,有代码示例,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。 为了让页面保活更加稳定,你们是怎么做的? 我用一行配置实现了 Vue页面保活是指在…

    2025年4月1日
    100
  • 聊聊vue的两种路由模式(哈希和历史)

    路由器分别有两种模式:一种是hash模式、另一种是history模式,在使用vue-cli以及vue-router默认搭建的vue项目,如果不做特殊的配置,默认就是hash模式 这两种模式各有自己的优势,但他们的使用区别,却多多少少都会在面…

    2025年4月1日
    100
  • 手把手教你从零开始搭建一套ui组件库

    本篇文章给大家带来了关于ui组件的相关知识,其中主要跟大家聊聊怎么从零开始搭建一套ui组件库,有代码示例,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。 1. 环境准备 我们在编写我们组件库的组件前,首先需要一套环境,包括下面几项: 需…

    2025年4月1日 编程技术
    100
  • 实例详解vue3实现chatgpt的打字机效果

    在做 chatgpt 镜像站的时候,发现有些镜像站是没做打字机的光标效果的,就只是文字输出,是他们不想做吗?反正我想做。于是我仔细研究了一下,实现了打字机效果加光标的效果,现在分享一下我的解决方案以及效果图 共识 首先要明确一点,chatg…

    2025年4月1日
    100
  • 一起学学vue的mixin

    前言 如果你在写vue组件时,发现有几个组件的逻辑差不多类似,那么你就可以使用vue的mixin(混入),把类似的逻辑抽离出来封装成js,然后在各个组件引入使用。 mixin就是用来解决vue组件逻辑复用的问题。今天咱们来学学vue的mix…

    2025年4月1日
    100
  • 详解用Vue怎么实现数据的双向绑定

    在vue.js中,双向数据绑定是一项非常强大的功能,它能够使数据和视图之间保持同步,让开发者更加方便地操作数据。在本文中,我们将介绍如何用vue.js实现数据的双向绑定。 1.  理解双向绑定 首先,我们需要了解双向绑定的原理。在Vue.j…

    2025年4月1日
    100
  • 聊聊Vue怎么用extend动态创建组件

    vue.js是一个流行的javascript框架,它提供了许多功能来帮助我们构建交互式web应用程序。其中之一是使用extend方法动态创建组件。 什么是extend方法? extend方法是Vue.js提供的一个方法,它允许我们创建一个新…

    2025年4月1日
    100
  • 一文详解Vue 3中的的插件

    当今 vue.js 已经成为了前端框架的主流之一,随着 vue.js 的不断发展,插件 (plugin) 已经成为了 vue.js 中的一个重要组成部分。在 vue.js 3.x 版本中,插件已经成为了 vue.js 的默认行为,因此熟练使…

    2025年4月1日
    100
  • 图文详解如何在Vue项目中集成Ace代码编辑器

    前言 在集成Ace过程中发现网上搜到资料比较零碎,且中文资料比较少,本文主要做一些记录和整理,方便后续查阅在Vue项目中集成Ace代码编辑器Ace配置项的中文对照踩坑:解决Ace editor 光标错位问题优化:按需使用ace-builds…

    2025年4月1日 编程技术
    100

发表回复

登录后才能评论