基于vue3.0+TypeScript的简易日历

前言

第一次用vue3的api和ts写,还在摸索中,有地方写的不规范的,可交流,共同进步html

项目介绍,功能很简单

  1. 显示全部时间日期
  2. 上一年上个月,下个月下一年

预览地址

arhebin.gitee.io/vue-calenda…vue

写完后的感觉和收获

  1. ts很香,但我用的不熟练;我平时会看看vue-next的源码,既能学新api还能学ts,一箭双雕呢;固然其余优质代码也能够
  2. vue3.0新的api很强大;例如就是reative, ref, watcher, watchEffect...,选择太多了,给开发者的组合方式也就多了,如今最缺的应该是最佳实践了。
  3. 最大感觉就是以前基于组件的分离,如今还能够基于数据或者方法层面上的抽离,例如我试着写了一个useComputedDayuseReducerDate,有点意思

怎么写日历

个人思路是:

  1. 须要一个输入的日期,默认是当前日期;这个值能够是传入的props,也能够是state组件自身的状态,(看你喜欢)
  2. 须要知道基于当前日期的年份
  3. 须要知道基于当前日期的月份
  4. 须要知道基于当前日期的几号
  5. 须要知道基于当前日期的当月最大天数
  6. 须要知道基于当前日期的上一个月最大天数
  7. 须要知道基于当前日期的当月一号是星期几

这些值都是基于当前日期的,因此你们会想到能够用一个computed来关联起来,反正我这么想了,😄git

有了这些值作什么能?

  1. 我先画一个7*6的大方块,默认索引是0-41
  2. 我知道当前日期一号是星期几,就能和上面的索引关联起来,在日后填日子,填到何时,就是当月最大天数的量,剩下的就是下一个月的1号仍是到索引结束,
  3. 同理头部,当前日期一号是星期几,以前的能够根据上一月最大天数与索引关联起来,填上去

举个例子🌰github

  1. 今天是2020/4/28;星期2;4月1号是星期三;最大天数是30天;上一个月的最大天数是31天;索引是index
  2. 4月份的全部日子1-30怎么填入里格子中呢?当index - 3 >= 0就往小格子里填入index - 3 + 1;直到(30 + 3 - 1) < index时,😯;剩余头尾
  3. 尾部触发条件就是(30 + 3 - 1) < index,填入index - (30 + 3 - 1)
  4. 剩下就是头部了,只要填入31 - (3 - index - 1)

代码以下api

<p class="item" v-for="(el, index) in maxDays" :key="index">
  <span :class="['day', el.isActive ? 'isactive' : '']"
    v-if="(index - state.currentMonthFirstDay) >= 0 && (state.currentMonthMaxDays + state.currentMonthFirstDay - 1) >= index">
    {{index - state.currentMonthFirstDay + 1}}
  </span>
  <span class="day rest " v-else-if="(state.currentMonthMaxDays + state.currentMonthFirstDay - 1) < index">{{index - (state.currentMonthMaxDays + state.currentMonthFirstDay - 1)}}</span>
  <span class="day rest" v-else>{{state.preMonthMaxDays - (state.currentMonthFirstDay - index - 1)}}</span>
</p>
复制代码

剩下的就是上一年上个月,下一年下个月

个人思路是写一个dispatch,靠action的type去控制,只要改变固然输入日期就好了。由于依赖已经关联了;(可能一开始会写个最简单一个方法,if-else的判断)数组

这些都不是重点啦,你们都有本身的思路。可是重点是包括我在内想知道是怎么基于新api写功能

剩下就是怎么写了

盲写,先实现功能再说

<template>
    //html结构
</template>
<script>
    step() {
        const nowDay = new Date().toLocaleDateString().split('/').map(Number)
        const dayStr = ref<number[]>(nowDay) //这边写成数组,以前是字符串,但写字符串有个问题就是年月日改变一个就会触发因此依赖;数组能够单纯改变一个
        //接来利用computed
        //造成依赖
        const date = computed(() => new Date(str.value.join('/')))
        //年
        const currentYear = computed(() => date.value.getFullYear())
        //剩余的值例如currentMonthMaxDays,preMonthMaxDays,currentMonthFirstDay
        ...
        //handleChange改变日期的方法
        const handleChange = () => {
            if () {
                //上一年
            }
            else if() {
                //上个月
            } else if() {
                //下个月
            } else if () {
                //下一年
            }
        }
    }
</script>

复制代码

当你写完,你会感受🐸,好长啊。就我我的而言,不怎么喜欢在组件的入口把代码的篇幅搞得很长。bash

怎么优化呢?

如今基于vue3.0,我的感受数据,方法也能抽离,除了️组件抽离(只是我我的的想法,能够交流)函数

  1. 第一个想法💡是:能不能把基于输入日期的值拿出来溜溜呢? 对于那写当前年份,当前月份等,数据能够抽离成到一个文件中,取个好听的名字叫作useComputedDay
export const useComputedDay = (str: Ref<number[]>) => {
    //注入了依赖
    const date = computed(() => new Date(str.value.join('/')))
    //年
    const currentYear = computed(() => date.value.getFullYear())
    //月
    const currentMonth = computed(() => date.value.getMonth() + 1)
    //日
    const currentDate = computed(() => date.value.getDate())
    //星期
    const currentDay = computed(() => date.value.getDay())
    //当月最大天数
    const currentMonthMaxDays = computed(() => getMonthMaxDays(currentYear.value, currentMonth.value))
    //当月星期几
    const currentMonthFirstDay = computed(() => computedFirstDay(currentYear.value, currentMonth.value))
    //上一个月最大天数
    // const preMonthMaxDays = computed(() => new Date(currentYear.value, currentMonth.value - 1, 0).getDate())
    const preMonthMaxDays = computed(() => getMonthMaxDays(currentYear.value, currentMonth.value - 1))
    return {
      date,
      currentYear,
      currentMonth,
      currentDate,
      currentDay,
      currentMonthMaxDays,
      preMonthMaxDays,
      currentMonthFirstDay
    } 
}
复制代码

怎么用呢?布局

const nowDay = new Date().toLocaleDateString().split('/').map(Number)
    const dayStr = ref<number[]>(nowDay) //这边写成数组,以前是字符串,但写字符串有个问题就是年月日改变一个就会触发因此依赖;数组能够单纯改变一个
    const dispatch = useReducerDate(dayStr)
    const {
      date,
      currentYear,
      currentMonth,
      currentDate,
      currentDay,
      currentMonthMaxDays,
      preMonthMaxDays,
      currentMonthFirstDay
    } = useComputedDay(dayStr);
复制代码

这样的话只要dayStr改变了全部的都会改变学习

  1. 第二个想法💡是:能不能把控制上下年月的方法也搞到外面去呢?,例如整个dispatchdispatch怎么写呢?能够抽离出来写成相似reducer那样
export const useReducerDate = (state: Ref<number[]>) => {
    return function (action: action) {
        switch(action.type) {
            case 'preYear': {
                const _var = state.value.map(Number)
                _var[0] = _var[0] - 1;
                state.value = _var
                break;
            }
            case 'preMonth': {
                cutMonth(state);
                break;
            } 
            case 'nextYear': {
                const _var = state.value.map(Number)
                _var[0] = _var[0] + 1;
                state.value = _var
                break;
            }
            case 'nextMonth': {
                addMonth(state);
                break;
            } 
            default: {
                state.value = new Date().toLocaleDateString().split('/').map(Number)
            }
                
        }
    }
}
复制代码

怎么用呢?

<div class="textheader">
    <i class="iconfont textItem" @click="dispatch({type: 'preYear'})">&#xe714;</i>
    <i class="iconfont textItem" @click="dispatch({type: 'preMonth'})">&#xe667;</i>
    <p class="textItem">
      {{state.currentYear}} 年 {{state.currentMonth}} 月 {{state.currentDate}} 日
    </p>
    <i class="iconfont textItem " @click="dispatch({type: 'nextMonth'})">&#xe610;</i>
    <i class="iconfont textItem" @click="dispatch({type: 'nextYear'})">&#xe713;</i>
</div>
const dispatch = useReducerDate(dayStr)
复制代码

很简单吧,香不香。

  1. 第三个想法💡(刚加的)这个useReducerDate还能在提取一下写个相似 useReducer,写个可复用的函数
export const useReducer = (state: Ref<any>, reducer: (state: Ref<any>, action: action) => any) => {
    return function dispatch(val: action) {
        reducer(state, val)
    }
}
复制代码
  1. 主要功能就完成了

源码

github.com/coolpail/vu…

总结

  1. 主要体会并非日历怎么实现,而是在vue3.0的背景下,如何更好的基于新的api去实现功能,布局本身的目录结构;api太多了,玩法组合太多了
  2. 期待你们的写法和想法💡,对交流学习共同进步
相关文章
相关标签/搜索