React中使用Day.js指南

发布于:2025-06-15 ⋅ 阅读:(20) ⋅ 点赞:(0)

在这里插入图片描述



引言

在现代前端开发中,日期和时间处理是一个常见且重要的需求。无论是显示文章发布时间、处理用户输入的日期,还是实现复杂的时间计算,我们都需要一个强大而轻量的日期库。Day.js 作为 Moment.js 的现代替代品,以其轻量、API友好和不可变性等特点,成为了 React 项目中处理日期时间的首选工具。

什么是Day.js?

Day.js 是一个轻量级的 JavaScript 日期库,专为现代浏览器设计。它提供了与 Moment.js 类似的 API,但体积仅有 2KB(压缩后),相比 Moment.js 的 67KB 有着显著的优势。

Day.js的核心特性

  • 轻量级:仅2KB的体积
  • 不可变性:所有API都返回新的实例
  • 链式调用:支持方法链式调用
  • 国际化:支持多语言
  • 插件系统:可按需加载功能
  • TypeScript支持:完整的类型定义

安装和基础配置

安装Day.js

# 使用npm
npm install dayjs

# 使用yarn
yarn add dayjs

# 使用pnpm
pnpm add dayjs

基础导入和使用

import dayjs from 'dayjs'

// 创建当前时间
const now = dayjs()

// 创建指定时间
const specificDate = dayjs('2024-01-01')
const fromTimestamp = dayjs(1640995200000)

在React中的基础使用

1. 显示格式化日期

import React from 'react'
import dayjs from 'dayjs'

const DateDisplay = ({ date }) => {
  return (
    <div>
      <p>完整日期: {dayjs(date).format('YYYY-MM-DD HH:mm:ss')}</p>
      <p>相对时间: {dayjs(date).fromNow()}</p>
      <p>友好格式: {dayjs(date).format('MMMM D, YYYY')}</p>
    </div>
  )
}

2. 实时时钟组件

import React, { useState, useEffect } from 'react'
import dayjs from 'dayjs'

const RealTimeClock = () => {
  const [currentTime, setCurrentTime] = useState(dayjs())

  useEffect(() => {
    const timer = setInterval(() => {
      setCurrentTime(dayjs())
    }, 1000)

    return () => clearInterval(timer)
  }, [])

  return (
    <div className="clock">
      <h2>{currentTime.format('HH:mm:ss')}</h2>
      <p>{currentTime.format('YYYY年MM月DD日 dddd')}</p>
    </div>
  )
}

常用插件配置

Day.js 采用插件系统来扩展功能,常用的插件包括:

1. 相对时间插件

import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import 'dayjs/locale/zh-cn'

dayjs.extend(relativeTime)
dayjs.locale('zh-cn')

// 使用相对时间
const date = dayjs('2024-01-01')
console.log(date.fromNow()) // "5个月前"

2. 高级格式化插件

import advancedFormat from 'dayjs/plugin/advancedFormat'
dayjs.extend(advancedFormat)

// 使用高级格式化
const date = dayjs()
console.log(date.format('Q')) // 季度
console.log(date.format('Do')) // 带序数的日期

3. 时区处理插件

import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'

dayjs.extend(utc)
dayjs.extend(timezone)

// 时区转换
const utcDate = dayjs.utc('2024-01-01 12:00:00')
const localDate = utcDate.tz('Asia/Shanghai')

实战案例:博客文章时间组件

让我们创建一个完整的博客文章时间显示组件:

import React, { useMemo } from 'react'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import 'dayjs/locale/zh-cn'

dayjs.extend(relativeTime)
dayjs.locale('zh-cn')

const ArticleTime = ({ 
  publishTime, 
  updateTime, 
  showRelative = true,
  showUpdate = true 
}) => {
  const publishDate = useMemo(() => dayjs(publishTime), [publishTime])
  const updateDate = useMemo(() => 
    updateTime ? dayjs(updateTime) : null, 
    [updateTime]
  )

  const isRecent = useMemo(() => 
    publishDate.isAfter(dayjs().subtract(7, 'day')), 
    [publishDate]
  )

  const isUpdated = useMemo(() => 
    updateDate && updateDate.isAfter(publishDate.add(1, 'day')), 
    [publishDate, updateDate]
  )

  return (
    <div className="article-time">
      <div className="publish-time">
        <span className="label">发布时间: </span>
        <time dateTime={publishDate.toISOString()}>
          {showRelative ? publishDate.fromNow() : publishDate.format('YYYY-MM-DD')}
        </time>
        {isRecent && <span className="badge new">新</span>}
      </div>
      
      {showUpdate && isUpdated && (
        <div className="update-time">
          <span className="label">最后更新: </span>
          <time dateTime={updateDate.toISOString()}>
            {showRelative ? updateDate.fromNow() : updateDate.format('YYYY-MM-DD')}
          </time>
          <span className="badge updated">已更新</span>
        </div>
      )}
    </div>
  )
}

export default ArticleTime

高级应用场景

1. 日期范围选择器

import React, { useState } from 'react'
import dayjs from 'dayjs'
import isBetween from 'dayjs/plugin/isBetween'

dayjs.extend(isBetween)

const DateRangePicker = ({ onRangeChange }) => {
  const [startDate, setStartDate] = useState('')
  const [endDate, setEndDate] = useState('')

  const handleRangeChange = () => {
    if (startDate && endDate) {
      const start = dayjs(startDate)
      const end = dayjs(endDate)
      
      if (start.isAfter(end)) {
        alert('开始日期不能晚于结束日期')
        return
      }
      
      const daysDiff = end.diff(start, 'day')
      
      onRangeChange({
        start: start.format('YYYY-MM-DD'),
        end: end.format('YYYY-MM-DD'),
        days: daysDiff + 1
      })
    }
  }

  return (
    <div className="date-range-picker">
      <input
        type="date"
        value={startDate}
        onChange={(e) => setStartDate(e.target.value)}
        max={endDate || dayjs().format('YYYY-MM-DD')}
      />
      <span>到</span>
      <input
        type="date"
        value={endDate}
        onChange={(e) => setEndDate(e.target.value)}
        min={startDate}
        max={dayjs().format('YYYY-MM-DD')}
      />
      <button onClick={handleRangeChange}>确认</button>
    </div>
  )
}

2. 倒计时组件

import React, { useState, useEffect } from 'react'
import dayjs from 'dayjs'
import duration from 'dayjs/plugin/duration'

dayjs.extend(duration)

const Countdown = ({ targetDate, onComplete }) => {
  const [timeLeft, setTimeLeft] = useState(null)

  useEffect(() => {
    const target = dayjs(targetDate)
    
    const updateCountdown = () => {
      const now = dayjs()
      const diff = target.diff(now)
      
      if (diff <= 0) {
        setTimeLeft(null)
        onComplete && onComplete()
        return
      }
      
      const duration = dayjs.duration(diff)
      setTimeLeft({
        days: Math.floor(duration.asDays()),
        hours: duration.hours(),
        minutes: duration.minutes(),
        seconds: duration.seconds()
      })
    }

    updateCountdown()
    const timer = setInterval(updateCountdown, 1000)

    return () => clearInterval(timer)
  }, [targetDate, onComplete])

  if (!timeLeft) {
    return <div className="countdown finished">时间到!</div>
  }

  return (
    <div className="countdown">
      <div className="time-unit">
        <span className="number">{timeLeft.days}</span>
        <span className="label">天</span>
      </div>
      <div className="time-unit">
        <span className="number">{timeLeft.hours}</span>
        <span className="label">时</span>
      </div>
      <div className="time-unit">
        <span className="number">{timeLeft.minutes}</span>
        <span className="label">分</span>
      </div>
      <div className="time-unit">
        <span className="number">{timeLeft.seconds}</span>
        <span className="label">秒</span>
      </div>
    </div>
  )
}

性能优化技巧

1. 使用useMemo缓存计算结果

import React, { useMemo } from 'react'
import dayjs from 'dayjs'

const OptimizedDateComponent = ({ dates }) => {
  const sortedDates = useMemo(() => {
    return dates
      .map(date => dayjs(date))
      .sort((a, b) => a.valueOf() - b.valueOf())
      .map(date => date.format('YYYY-MM-DD'))
  }, [dates])

  return (
    <ul>
      {sortedDates.map((date, index) => (
        <li key={index}>{date}</li>
      ))}
    </ul>
  )
}

2. 避免重复创建实例

// 不推荐
const BadExample = ({ timestamp }) => {
  return (
    <div>
      <p>{dayjs(timestamp).format('YYYY-MM-DD')}</p>
      <p>{dayjs(timestamp).format('HH:mm:ss')}</p>
      <p>{dayjs(timestamp).fromNow()}</p>
    </div>
  )
}

// 推荐
const GoodExample = ({ timestamp }) => {
  const date = useMemo(() => dayjs(timestamp), [timestamp])
  
  return (
    <div>
      <p>{date.format('YYYY-MM-DD')}</p>
      <p>{date.format('HH:mm:ss')}</p>
      <p>{date.fromNow()}</p>
    </div>
  )
}

最佳实践

1. 统一的日期格式管理

// utils/dateFormats.js
export const DATE_FORMATS = {
  DISPLAY: 'YYYY年MM月DD日',
  INPUT: 'YYYY-MM-DD',
  DATETIME: 'YYYY-MM-DD HH:mm:ss',
  TIME: 'HH:mm:ss',
  MONTH: 'YYYY-MM',
  YEAR: 'YYYY'
}

// 使用
import { DATE_FORMATS } from './utils/dateFormats'
const formattedDate = dayjs(date).format(DATE_FORMATS.DISPLAY)

2. 创建自定义Hook

import { useState, useEffect } from 'react'
import dayjs from 'dayjs'

export const useCurrentTime = (updateInterval = 1000) => {
  const [currentTime, setCurrentTime] = useState(dayjs())

  useEffect(() => {
    const timer = setInterval(() => {
      setCurrentTime(dayjs())
    }, updateInterval)

    return () => clearInterval(timer)
  }, [updateInterval])

  return currentTime
}

// 使用
const MyComponent = () => {
  const currentTime = useCurrentTime()
  return <div>{currentTime.format('HH:mm:ss')}</div>
}

3. 错误处理

const SafeDateComponent = ({ dateString }) => {
  const formatDate = (date) => {
    try {
      const dayObj = dayjs(date)
      
      if (!dayObj.isValid()) {
        return '无效日期'
      }
      
      return dayObj.format('YYYY-MM-DD')
    } catch (error) {
      console.error('日期格式化错误:', error)
      return '日期格式错误'
    }
  }

  return <div>{formatDate(dateString)}</div>
}

常见问题和解决方案

1. 时区问题

// 始终使用UTC时间进行存储和传输
const saveDate = dayjs().utc().toISOString()

// 在显示时转换为本地时间
const displayDate = dayjs.utc(saveDate).local()

2. 本地化问题

import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
import 'dayjs/locale/en'

// 根据用户设置切换语言
const setLanguage = (lang) => {
  dayjs.locale(lang)
}

3. 服务端渲染(SSR)问题

import { useEffect, useState } from 'react'
import dayjs from 'dayjs'

const SSRSafeDateComponent = ({ date }) => {
  const [mounted, setMounted] = useState(false)

  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) {
    return <div>加载中...</div>
  }

  return <div>{dayjs(date).format('YYYY-MM-DD')}</div>
}

网站公告

今日签到

点亮在社区的每一天
去签到