React antd的datePicker自定义,封装成组件
一、antd的datePicker自定义
需求:用户需要为日期选择器的每个日期单元格添加一个Tooltip,当鼠标悬停时显示日期、可兑换流量余额和本公会可兑流量。这些数据需要从接口获取。我需要结合之前的代码,确保Tooltip正确显示,并且数据来自接口。
主要汉化点:
- 整个日期选择器面板中文化
- 星期显示为中文(周一 到 周日)
- 月份显示为中文格式
- 操作按钮汉化("确定"、"现在" 等)
- 日期格式统一使用中文年月日
- 加载提示中文化
- Tooltip内容中文化
效果包含:
- 月份显示为 "2024年5月"
- 星期列显示为 "一、二、三、四、五、六、日"
- 今天按钮显示为 "今天"
- 确定按钮显示为 "确定"
- 十年范围显示为 "2020-2029"
- 时间列显示为 "时","分","秒"
- index.tsx文件
import React, { useState, useEffect } from 'react';
import { DatePicker, Tooltip, Spin, ConfigProvider } from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
import './custom-datepicker.css';
import 'dayjs/locale/zh-cn';
import zhCN from 'antd/locale/zh_CN';
dayjs.extend(isSameOrAfter);
dayjs.extend(isSameOrBefore);
dayjs.locale('zh-cn'); // 设置dayjs为中文interface TrafficData {date: string;personal: string;guild: string;
}const App: React.FC = () => {const [selectedDate, setSelectedDate] = useState<Dayjs | null>(null);const [trafficData, setTrafficData] = useState<Record<string, TrafficData>>({});const [loading, setLoading] = useState(true);const today = dayjs().startOf('day');const sevenDaysLater = today.add(6, 'day');useEffect(() => {const mockApi = async () => {const data: Record<string, TrafficData> = {};Array.from({ length: 7 }).forEach((_, i) => {const date = today.add(i, 'day').format('YYYY-MM-DD');data[date] = {date,personal: `${[0, 20, 30, 40, 80, 100, 50][i]}%`,guild: `${Math.floor(Math.random() * 100000).toLocaleString()}`,};});await new Promise(resolve => setTimeout(resolve, 500));setTrafficData(data);setLoading(false);};mockApi();}, []);const disabledDate = (current: Dayjs) => current.isBefore(today, 'day') || current.isAfter(sevenDaysLater, 'day');if (loading) return <Spin tip="数据加载中..." />;return (<ConfigProvider locale={zhCN}> {/* 设置Ant Design为中文 */}<DatePickervalue={selectedDate}disabledDate={disabledDate}onChange={setSelectedDate}dropdownClassName="custom-picker-dropdown"dateRender={current => {const dateStr = current.format('YYYY-MM-DD');const data = trafficData[dateStr];const isInRange = current.isSameOrAfter(today) && current.isSameOrBefore(sevenDaysLater);const isSelected = selectedDate?.isSame(current, 'day');return (<div className="custom-cell-wrapper"><div className="native-cell-content">{current.date()}</div><Tooltiptitle={data ?`${dayjs(dateStr).format('YYYY年M月D日')}\n可兑流量余额: ${data.personal}\n本公会可兑流量: ${data.guild}`: '无可用数据'}overlayStyle={{whiteSpace: 'pre-line',pointerEvents: 'none',}}placement="bottom"mouseEnterDelay={0}mouseLeaveDelay={0.1}trigger={['hover']}getPopupContainer={trigger => trigger.parentElement!}><div className={`custom-cell ${isInRange ? 'recent-date' : ''}`}><div className={`date-number ${isSelected ? 'selected' : ''}`}>{current.date()}</div>{data && (<div className={`availability ${data.personal === '0%' ? 'empty' : ''}`}>余{data.personal}</div>)}</div></Tooltip></div>);}}/></ConfigProvider>);
};export default App;
-
custom-datepicker.css文件
/* custom-datepicker.css */
.custom-picker-dropdown {z-index: 1001;
}.custom-cell-wrapper {position: relative;height: 100%;width: 100%;
}.native-cell-content {visibility: hidden;
}.custom-cell {position: absolute;top: 0;left: 0;width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: center;cursor: pointer;z-index: 2;padding: 3px 0;
}.date-number {width: 24px;height: 24px;line-height: 24px;text-align: center;color: #000;transition: all 0.2s;border-radius: 50%;
}.date-number.selected {background: #1890ff;color: white !important;
}.recent-date:hover .date-number:not(.selected) {color: #1890ff;
}.availability {font-size: 10px;line-height: 14px;color: #1890ff;margin-top: 2px;
}.availability.empty {color: #ff4d4f !important;
}.ant-picker-cell-inner {padding: 0 !important;height: 100% !important;
}.ant-picker-cell:hover .ant-picker-cell-inner {background: transparent !important;
}/* 添加中文面板样式调整 */
.ant-picker-date-panel {font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}.ant-picker-header-view button {font-weight: 500;
}.ant-picker-cell-inner::before {border-radius: 50% !important;
}

二、封装成组件
- DateSelector.tsx文件
// DateSelector.tsx
import React from 'react';
import {DatePicker, Tooltip, Spin, ConfigProvider} from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
import 'dayjs/locale/zh-cn';
import './custom-datepicker.css';
import zhCN from 'antd/locale/zh_CN';dayjs.extend(isSameOrAfter);
dayjs.extend(isSameOrBefore);export interface TrafficData {personal: string;guild: string;
}interface DateSelectorProps {value?: Dayjs | null;trafficData: Record<string, TrafficData>;onChange?: (date: Dayjs | null) => void;loading?: boolean;
}const DateSelector: React.FC<DateSelectorProps> = ({value,trafficData,onChange,loading = false,
}) => {const today = dayjs().startOf('day');const sevenDaysLater = today.add(6, 'day');const disabledDate = (current: Dayjs) => current.isBefore(today, 'day') || current.isAfter(sevenDaysLater, 'day');const handleChange = (date: Dayjs | null) => {onChange?.(date);};if (loading) {return <Spin tip="数据加载中..." style={{ padding: '8px 0' }} />;}return (<ConfigProvider locale={zhCN}> {/* 设置Ant Design为中文 */}<DatePickervalue={value}disabledDate={disabledDate}onChange={handleChange}dropdownClassName="custom-picker-dropdown"dateRender={current => {const dateStr = current.format('YYYY-MM-DD');const data = trafficData[dateStr];const isInRange = current.isSameOrAfter(today) && current.isSameOrBefore(sevenDaysLater);const isSelected = value?.isSame(current, 'day');return (<div className="custom-cell-wrapper"><div className="native-cell-content">{current.date()}</div><Tooltiptitle={data ?`${dayjs(dateStr).format('YYYY年M月D日')}\n可兑流量余额: ${data.personal}\n本公会可兑流量: ${data.guild}`: '无可用数据'}overlayStyle={{whiteSpace: 'pre-line',pointerEvents: 'none',}}placement="bottom"mouseEnterDelay={0}mouseLeaveDelay={0.1}><div className={`custom-cell ${isInRange ? 'recent-date' : ''}`}><div className={`date-number ${isSelected ? 'selected' : ''}`}>{current.date()}</div>{data && (<div className={`availability ${data.personal === '0%' ? 'empty' : ''}`}>余{data.personal}</div>)}</div></Tooltip></div>);}}/></ConfigProvider>);
};export default DateSelector;
- custom-datepicker.css
/* custom-datepicker.css */
.custom-picker-dropdown {z-index: 1001;
}.custom-cell-wrapper {position: relative;height: 100%;width: 100%;
}.native-cell-content {visibility: hidden;
}.custom-cell {position: absolute;top: 0;left: 0;width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: center;cursor: pointer;z-index: 2;padding: 3px 0;
}.date-number {width: 24px;height: 24px;line-height: 24px;text-align: center;color: #000;transition: all 0.2s;border-radius: 50%;
}.date-number.selected {background: #1890ff;color: white !important;
}.recent-date:hover .date-number:not(.selected) {color: #1890ff;
}.availability {font-size: 10px;line-height: 14px;color: #1890ff;margin-top: 2px;
}.availability.empty {color: #ff4d4f !important;
}.ant-picker-cell-inner {padding: 0 !important;height: 100% !important;
}.ant-picker-cell:hover .ant-picker-cell-inner {background: transparent !important;
}/* 添加中文面板样式调整 */
.ant-picker-date-panel {font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}.ant-picker-header-view button {font-weight: 500;
}.ant-picker-cell-inner::before {border-radius: 50% !important;
}
- index.tsx文件
// 使用示例 ParentComponent.tsx
import React, { useState, useEffect } from 'react';
import DateSelector, { TrafficData } from './DateSelector';
import dayjs from 'dayjs';const ParentComponent: React.FC = () => {const [selectedDate, setSelectedDate] = useState<dayjs.Dayjs | null>(null);const [trafficData, setTrafficData] = useState<Record<string, TrafficData>>({});const [loading, setLoading] = useState(true);const today = dayjs().startOf('day');// useEffect(() => {// // 模拟API调用// const mockFetchData = async () => {// const mockData = {// [dayjs().format('YYYY-MM-DD')]: {// personal: '80%',// guild: '100,000',// },// [dayjs().add(1, 'day')// .format('YYYY-MM-DD')]: {// personal: '50%',// guild: '75,000',// },// };//// await new Promise(resolve => setTimeout(resolve, 800));// setTrafficData(mockData);// setLoading(false);// };//// mockFetchData();// }, []);useEffect(() => {const mockApi = async () => {const data: Record<string, TrafficData> = {};Array.from({ length: 7 }).forEach((_, i) => {const date = today.add(i, 'day').format('YYYY-MM-DD');data[date] = {personal: `${[0, 20, 30, 40, 80, 100, 50][i]}%`,guild: `${Math.floor(Math.random() * 100000).toLocaleString()}`,};});await new Promise(resolve => setTimeout(resolve, 500));setTrafficData(data);setLoading(false);};mockApi();}, []);return (<div style={{ padding: 24 }}><h2>日期选择器示例</h2><div style={{ marginBottom: 16 }}>当前选择:{selectedDate?.format('YYYY年M月D日') || '未选择'}</div><DateSelectorvalue={selectedDate}trafficData={trafficData}onChange={setSelectedDate}loading={loading}/></div>);
};export default ParentComponent;

三、生效时间选择完日期后,还需填入具体时间(或提供一个时间选择器),精确到分,默认为00:00;如选择的日期为今天,则填写的时间不能早于当前时间
// index.tsx
import React, { useState, useEffect } from 'react';
import { Row, Col, TimePicker, Spin, ConfigProvider } from 'antd';
import DateSelector, { TrafficData } from './DateSelector';
import dayjs, { Dayjs } from 'dayjs';
import zhCN from 'antd/locale/zh_CN';declare module 'dayjs' {interface Dayjs {isToday(): boolean;}
}dayjs.extend((o, c) => {c.prototype.isToday = function () {return this.isSame(dayjs(), 'day');}
});const DateTimePicker: React.FC = () => {const [selectedDate, setSelectedDate] = useState<Dayjs | null>(null);const [selectedTime, setSelectedTime] = useState<Dayjs>(dayjs().startOf('minute'));const [trafficData, setTrafficData] = useState<Record<string, TrafficData>>({});const [loading, setLoading] = useState(true);const today = dayjs().startOf('day');const disabledTime = (current: Dayjs | null) => {if (!current || !selectedDate?.isToday()) {return { disabledHours: () => [], disabledMinutes: () => [] };}const now = dayjs();return {disabledHours: () => {const currentHour = now.hour();return Array.from({ length: currentHour }, (_, i) => i);},disabledMinutes: (selectedHour: number) => {if (selectedHour < now.hour()) return [];return Array.from({ length: now.minute() }, (_, i) => i);},};};const handleDateChange = (date: Dayjs | null) => {setSelectedDate(date);setSelectedTime(date?.isToday() ? dayjs().startOf('minute') : dayjs().startOf('day'));};// useEffect(() => {// // 模拟API请求// const mockData = {// [dayjs().format('YYYY-MM-DD')]: { personal: '80%', guild: '100,000' },// [dayjs().add(1, 'day')// .format('YYYY-MM-DD')]: { personal: '50%', guild: '75,000' },// };//// setTimeout(() => {// setTrafficData(mockData);// setLoading(false);// }, 800);// }, []);useEffect(() => {const mockApi = async () => {const data: Record<string, TrafficData> = {};Array.from({ length: 7 }).forEach((_, i) => {const date = today.add(i, 'day').format('YYYY-MM-DD');data[date] = {personal: `${[0, 20, 30, 40, 80, 100, 50][i]}%`,guild: `${Math.floor(Math.random() * 100000).toLocaleString()}`,};});await new Promise(resolve => setTimeout(resolve, 500));setTrafficData(data);setLoading(false);};mockApi();}, []);return (<ConfigProvider locale={zhCN}> {/* 设置Ant Design为中文 */}<div style={{ padding: 24, maxWidth: 380, margin: '0 auto' }}><h2 style={{ marginBottom: 24 }}>预约时间选择</h2><Row gutter={24} align="middle"><Col span={12}>{/*<div style={{ marginBottom: 8 }}>选择日期</div>*/}<DateSelectorvalue={selectedDate}trafficData={trafficData}onChange={handleDateChange}loading={loading}/></Col><Col span={12}>{/*<div style={{ marginBottom: 8 }}>选择时间</div>*/}<TimePickervalue={selectedTime}format="HH:mm"minuteStep={1}disabledTime={disabledTime}onChange={time => setSelectedTime(time || dayjs().startOf('minute'))}placeholder="请选择时间"disabled={!selectedDate}allowClear={false}showNow={false}style={{ width: '100%' }}/></Col></Row><div style={{ marginTop: 24, padding: 16, background: '#f5f5f5', borderRadius: 4 }}>已选择时间: {selectedDate ?`${selectedDate.format('YYYY年MM月DD日')} ${selectedTime.format('HH:mm')}`: '请先选择日期'}</div></div></ConfigProvider>);
};export default DateTimePicker;



相关文章:
React antd的datePicker自定义,封装成组件
一、antd的datePicker自定义 需求:用户需要为日期选择器的每个日期单元格添加一个Tooltip,当鼠标悬停时显示日期、可兑换流量余额和本公会可兑流量。这些数据需要从接口获取。我需要结合之前的代码,确保Tooltip正确显示,并且数据…...
C++ AVL树详解(含模拟实现)
目录 AVL树的概念 AVL树节点的定义 AVL树的插入 AVL树的旋转(难点) AVL树的验证 AVL树的删除(本文不做具体的模拟实现) AVL树的性能 AVL树的模拟实现 AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索…...
Spring Boot 3.x 系列【3】Spring Initializr快速创建Spring Boot项目
有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.0.3 源码地址:https://gitee.com/pearl-organization/study-spring-boot3 文章目录 前言安装JDK 17创建Spring Boot 项目 方式1:网页在线生成方式2&#…...
Elasticsearch:过滤 HNSW 搜索,快速模式
作者:来自 Elastic Benjamin Trent 通过我们的 ACORN-1 算法实现,探索我们对 Apache Lucene 中的 HNSW 向量搜索所做的改进。 多年来,Apache Lucene 和 Elasticsearch 一直支持使用 kNN 查询的过滤搜索,允许用户检索符合指定元数据…...
TCP长连接与短连接
TCP长连接与短连接 TCP(传输控制协议)中的长连接和短连接是两种不同的连接管理方式,各有优缺点: 短连接 短连接是指客户端与服务器完成一次数据交换后就断开连接。下次需要通信时,再重新建立连接。 特点࿱…...
【AI测试学习】AnythingLLM+Ollama+DeepSeek部署私人知识库
1.搭建DeepSeek大语言模型 1.1Ollama大预言模型部署 Ollama简化了大型语言模型的运行,让每个人都能在本地轻松体验AI的强大,打开浏览器-下载Ollama-输入命令-搞定,这是本地部署大语言模型的全新方式。 这里我们借助Ollama大预言模型部署工具进行搭建 官网如下:Ollama …...
防流、节抖、重绘、回流原理,以及实现方法和区别
防流、节抖、重绘、回流原理,以及实现方法和区别,还有就是为什么会出现这种情况? 防抖(Debounce) 原理 防抖就像是你坐电梯,如果你一直不停地按开门按钮,电梯不会每次都开门,而是…...
通义灵码插件安装入门教学 - IDEA(安装篇)
在开发过程中,使用合适的工具和插件可以极大地提高我们的工作效率。今天,我们将详细介绍如何在 IntelliJ IDEA 中安装并配置通义灵码插件,这是一款旨在提升开发者效率的实用工具。无论你是新手还是有经验的开发者,本文都将为你提供…...
ES、OAS、ERP、电子政务、企业信息化(高软35)
系列文章目录 ES、OAS、ERP、电子政务、企业信息化 文章目录 系列文章目录前言一、专家系统(ES)二、办公自动化系统(OAS)三、企业资源规划(ERP)四、典型信息系统架构模型1.政府信息化和电子政务2.企业信息…...
用大白话解释缓存Redis +MongoDB是什么有什么用怎么用
Redis和MongoDB是什么? Redis:像你家的“小冰箱”,专门存高频使用的食物(数据)。它是基于内存的键值数据库,读写速度极快(每秒超10万次操作)。比如你每次打开手机App,用…...
华为数通Datacom认证体系详解:从HCIA到HCIE的进阶路径
华为数通Datacom(Data Communication)课程是华为认证体系中的核心方向之一,聚焦企业网络通信与数据通信技术,适合从事网络规划、部署和运维的人员。 一、数通Datacom课程体系 华为数通Datacom认证分为 三个级别,逐级递…...
PyTorch 的 nn.NLLLoss:负对数似然损失全解析
PyTorch 的 nn.NLLLoss:负对数似然损失全解析 在 PyTorch 的损失函数家族中,nn.NLLLoss(Negative Log Likelihood Loss,负对数似然损失)是一个不太起眼但非常重要的成员。它经常跟 LogSoftmax 搭配出现,尤…...
《数据安全架构设计与实战》的目录
《数据安全架构设计与实战》的目录如下1: 第一部分 安全架构基础 第 1 章 架构第 2 章 安全架构 2.1 什么是安全2.2 为什么使用 “数据安全” 这个术语2.3 什么是安全架构2.4 安全架构 5A 方法论2.5 安全架构 5A 与 CIA 的关系 第二部分 产品安全架构 …...
python-leetcode-删除并获得点数
740. 删除并获得点数 - 力扣(LeetCode) 解法 1:动态规划(O(n) 时间,O(n) 空间) class Solution:def deleteAndEarn(self, nums: List[int]) -> int:if not nums:return 0# 统计每个数的贡献points Cou…...
助力DeepSeek私有化部署服务:让企业AI落地更简单、更安全
在数字化转型的浪潮中,越来越多的企业选择私有化部署AI技术,以保障数据安全、提升业务效率并实现自主可控。DeepSeek作为行业领先的AI开源技术,其技术可以支持企业私有化部署,企业需要一站式服务私有化部署,涵盖硬件采…...
Android JNI开发指南
了解Android JNI开发。首先,我需要回忆一下JNI的基本概念和作用。JNI允许Java代码与本地代码(如C/C)交互,这在需要高性能或者使用现有C/C库时非常有用。 接下来,我应该整理开发步骤。用户可能想知道如何开始ÿ…...
【每天认识一个漏洞】url重定向
🌝博客主页:菜鸟小羊 💖专栏:Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 常见应用场景 主要是业务逻辑中需要进行跳转的地方。比如登录处、注册处、访问用户信息、订单信息、加入购物车、分享、收…...
纯代码实战--用Deepseek+SQLite+Ollama搭建数据库助手
如何用Python调用本地模型实现DeepSeek提示词模板:一步步教你高效解决13种应用场景 从零到一:纯代码联合PyQt5、Ollama、Deepseek打造简易版智能聊天助手 用外接知识库武装大模型:基于Deepseek、Ollama、LangChain的RAG实战解析 纯代码实战–…...
2025 最新版鸿蒙 HarmonyOS 开发工具安装使用指南
为保证 DevEco Studio 正常运行,建议电脑配置满足如下要求: Windows 系统 操作系统:Windows10 64 位、Windows11 64 位内存:16GB 及以上硬盘:100GB 及以上分辨率:1280*800 像素及以上 macOS 系统 操作系统…...
日期时间 API
日期时间 API (java.time 包),旨在解决旧版 java.util.Date 和 java.util.Calendar 存在的一些设计缺陷,比如线程不安全、时区处理不一致等问题。新 API 基于 ISO 8601 标准,更加直观、简洁,且支持时区和区域设置。主要类有&#…...
AI数字人开发,引领科技新潮流
引言 随着人工智能技术的迅猛发展,AI 数字人在影视娱乐、客户服务、教育及医疗等多个领域展现出巨大的潜力。本文旨在为开发者提供一份详细的 AI 数字人系统开发指南,涵盖从基础架构到实现细节的各个方面,包括人物建模、动作生成、语音交互、…...
领域驱动设计:事件溯源架构简介
概述 事件溯源架构通常由3种应用设计模式组成,分别是:事件驱动(Event Driven),事件溯源(Event Source)、CQRS(读写分离)。这三种应用设计模式常见于领域驱动设计(DDD)中,但它们本身是一种应用设计的思想,不仅仅局限于DDD,每一种模式都可以单独拿出来使用。 E…...
自定义类加载器国密版本冲突
自定义类加载器国密版本冲突 对接三方接口经常使用到国密加密包(bcprov),此时系统已经引入了1.5版本,而三方提供的sdk中引用了1.6版版本,两个版本有冲突,如果系统加载到1.5版本的将会加密异常(各种奇怪的异…...
Debian 包版本号比较规则详解
1 版本号组成结构 Debian 版本号格式为:[epoch:]upstream_version[-debian_revision] 示例:2:1.18.3~betadfsg1-5b1 组件说明比较优先级Epoch冒号前的数字 (2:)最高Upstream主版本 (1.18.3~betadfsg1)中Debian修订号减号后的部分 (5)最…...
STM32之影子寄存器
预分频寄存器计数到一半的时候,改变预分频值,此时不会立即生效,会等到计数完成,再从影子寄存器即预分频缓冲器里装载修改的预分频值。 如上图,第一行是内部时钟72M,第二行是时钟使能,高电平启动…...
x64汇编下过程参数解析
简介 好久没上博客, 突然发现我的粉丝数变2700了, 真是这几个月涨的粉比我之前好几年的都多, 于是心血来潮来写一篇, 记录一下x64下的调用约定(这里的调用约定只针对windows平台) Windows下的x64程序的调用约定有别于x86下的"stdcall调用约定"以及"cdecl调用约…...
Blender调整最佳渲染清晰度
1.渲染采样调高 512 2.根据需要 开启AO ,开启辉光 , 开启 屏幕空间反射 3.调高分辨率 4096x4096 100% 分辨率是清晰度的关键 , 分辨率不高 , 你其他参数调再高都没用 4.世界环境开启体积散射 , 可以增强氛围感 5.三点打光法 放在模型和相机45夹角上 白模 白模带线条 成品...
TSMaster【第二十篇:华山论剑——知识图谱全览】
(三维思维导图「独孤九剑总诀式」技能树「经脉贯通」检测系统未来技术「武学秘境」预测) 【武侠场景导入】光明顶秘道惊变 明教光明顶密道中,张无忌面对错综复杂的甬道体系,以乾坤大挪移心法贯通九阳神功与太极拳剑,终成武林至尊。今时今日,三电工程师面对庞杂的TSMaste…...
神经性手抖是一种常见的症状
神经性手抖是一种常见的症状,表现为手部无意识或不受控制地颤抖。为了预防神经性手抖,我们可以采取以下几种方法: 1. 放松身心:压力和焦虑是导致神经性手抖的常见原因之一。因此,学会放松身心是预防手抖的关键。可以通…...
前端项目打包生成 JS 文件的核心步骤
前端项目打包生成 JS 文件的过程通常涉及以下核心步骤,以主流工具(如 Webpack、Vite、Rollup 等)为例: 一、项目准备阶段 项目结构 源代码目录(如 src/)包含 JS/TS、CSS、图片等资源配置文件(pa…...
