解析ElementUI中DatePicker组件日历面板实现原理

2,547 阅读2分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

日历UI部分源码

  • 日历面板7行7列,UI部分的代码采用table布局,第一行为星期,循环渲染周一至周日;第二至第七行tr两层循环,循环的变量为computed中组装好的rows
// basic/date-table.vue

<template>
  <table
    cellspacing="0"
    cellpadding="0"
    class="el-date-table"
    @click="handleClick"
    @mousemove="handleMouseMove"
    :class="{ 'is-week-mode': selectionMode === 'week' }">
    <tbody>
    <tr>
      <th v-if="showWeekNumber">{{ t('el.datepicker.week') }}</th>
      <th v-for="(week, key) in WEEKS" :key="key">{{ t('el.datepicker.weeks.' + week) }}</th>
    </tr>
    <tr
      class="el-date-table__row"
      v-for="(row, key) in rows"
      :class="{ current: isWeekActive(row[1]) }"
      :key="key">
      <td
        v-for="(cell, key) in row"
        :class="getCellClasses(cell)"
        :key="key">
        <div>
          <span>
            {{ cell.text }}
          </span>
        </div>
      </td>
    </tr>
    </tbody>
  </table>
</template>
  • 组件UI

WX20210828-105439@2x.png

日历实现原理

  • 核心思路是获取日历面板展示的当月第一天位置:首先获取当月第一天日期是星期几,以上图为例。假设日历显示的规则是,周日在第一个位置、周六为最后一个位置,周一至周五在两者之间顺序展示。那周六就是日历面板第一行第7天。也就是说我们假定的这个日期的第一天是显示在第一行最后一个位置。那日历面板第一行第一天与第七天中间还有5天,所以日历面板第一个位置的日期是上个月总天数减去间隔的5天,第二个位置就是上个月日期总天数减去间隔的4天、第三天就是上个月日期总天数减去间隔的3天、以此类推得出第一行上个月所有剩余的日期。难点已经处理完了,接下来就只要计算出当月的天数,按顺序排列渲染日期,超出当月的天数后,再排列渲染下个月的日期即可。
  • 以上思路涉及到的计算方法皆可以通过Date原生方法实现,具体可参考源码element-ui/src/utils/date-util中的方法
    • 计算某个月第一天是星期几
    • 计算某个月的上一个月有多少天,涉及平年闰年的情况
    • 计算当月有多少天,涉及平年闰年的情况
  • 计算后,组装的数据放在computed中,当前选择日期或者默认日期变化时,日历面板就会变化。外层tr6次循环代表6行数据、内层td7次循环代表一星期7天,7列数据。
  • 当然日历展示规则也可能是星期一在第一行第一个位置,星期日在第七个位置,同样的原理,计算出当月第一天是星期几,确定好在第一行的位置后,计算出上个月相对这天的偏移量推算即可。

参考

ElementUI源码