文章目錄
- 引言
- 什么是Day.js?
- Day.js的核心特性
- 安裝和基礎配置
- 安裝Day.js
- 基礎導入和使用
- 在React中的基礎使用
- 1. 顯示格式化日期
- 2. 實時時鐘組件
- 常用插件配置
- 1. 相對時間插件
- 2. 高級格式化插件
- 3. 時區處理插件
- 實戰案例:博客文章時間組件
- 高級應用場景
- 1. 日期范圍選擇器
- 2. 倒計時組件
- 性能優化技巧
- 1. 使用useMemo緩存計算結果
- 2. 避免重復創建實例
- 最佳實踐
- 1. 統一的日期格式管理
- 2. 創建自定義Hook
- 3. 錯誤處理
- 常見問題和解決方案
- 1. 時區問題
- 2. 本地化問題
- 3. 服務端渲染(SSR)問題
引言
在現代前端開發中,日期和時間處理是一個常見且重要的需求。無論是顯示文章發布時間、處理用戶輸入的日期,還是實現復雜的時間計算,我們都需要一個強大而輕量的日期庫。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"><inputtype="date"value={startDate}onChange={(e) => setStartDate(e.target.value)}max={endDate || dayjs().format('YYYY-MM-DD')}/><span>到</span><inputtype="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>
}