应用场景
前端开发中常常会在一个页面中需要显示多个不同位置或容器内的实时时间。例如,在仪表板、监控面板或多个计时器应用程序中,希望在不同的区域或组件中显示实时时间。
本文通过构建TimerC
组件,可以将多个时间计算组件TimeComponent
实例化并渲染到指定的容器中。这样,就完成了整个前端界面时间的统一管理和更新。
这种方法可以提供灵活性和可扩展性,能够在一个页面上管理和显示多个实时时间,同时保持代码的整洁性和可维护性。
实现思路
-
首先,定义了一个
TimeComponent
组件,用于显示当前时间。该组件接收一个初始时间作为属性,并使用useState
来保存当前时间和初始时间。 -
在
useEffect
钩子中,我们创建一个定时器,每200毫秒更新一次当前时间。通过计算当前时间与初始时间的差值,可以得到经历的时间,并使用dayjs
库将其格式化为小时、分钟和秒。 -
接下来,定义了一个名为
TimerC
的父组件。它接收一个ID数组作为属性,表示要渲染TimeComponent
的容器的ID。 -
在
TimerC
的useEffect
钩子中,对传入的ID数组进行遍历,并在每个容器中使用ReactDOM.render
方法将TimeComponent
组件渲染出来。这样,根据ID数组的长度,就会生成相应数量的TimeComponent
实例。 -
如果ID数组发生变化,首先卸载先前渲染的
TimeComponent
实例,然后再根据新的ID重新渲染相应数量的TimeComponent
。 -
最后,
TimerC
组件返回null
,因为实际的渲染和卸载逻辑已经在useEffect
中处理。
通过以上步骤,能够在一个页面中动态渲染多个实时时间组件,并且可以根据传入的ID数组来控制渲染和卸载的数量。
组件优势
上述例子的优点包括:
-
灵活性:通过使用
TimerC
组件,可以在一个页面中管理和展示多个实时时间。可以根据需要提供不同的容器ID数组,从而灵活地控制渲染和卸载的数量以及每个时间组件的位置。 -
可扩展性:由于代码结构清晰,可以轻松地扩展功能,例如添加更多的定时组件或根据需求进行自定义样式和布局。
-
可维护性:将逻辑分离到单独的组件中使得代码更易于维护。
TimeComponent
和TimerC
之间的职责分工清晰,每个组件都专注于特定的功能,并且代码的可读性更高。 -
重复利用:通过将
TimeComponent
作为内部组件,可以在其他地方重复使用它,以显示单个实时时间。 -
实时更新:每个
TimeComponent
独立更新自己的时间,并且更新频率较快(默认为每200毫秒)。这确保了实时时间的精确度和准确性。
TimerC组件具有灵活性、可扩展性、可维护性和重复利用的优势。它提供了一种简洁而有效的方式来同时处理和展示多个实时时间,并能够满足各种需求和场景。
精确计算
定时器被搁置的问题
在切换至后台时,定时器会暂停执行,这可能导致时间不准确。然而,在大多数现代浏览器中,当将页面切换至后台时,浏览器会将定时器的最小间隔增加到一定值(例如1000毫秒),以降低后台页面对系统资源的占用。
这种策略被称为最小化定时器降频(Throttling)或最小化定时器挂起(Timer Throttling)。它旨在节省电池寿命和减少性能影响。
当页面重新回到前台时,定时器会恢复正常的间隔,并继续按照设定的时间间隔触发。
所以,在切换至后台时,时间组件的更新频率可能会降低,但当页面重新进入前台时,时间会再次正常更新并保持准确性。
准确计算时间的策略
更新时间的核心逻辑在TimeComponent
组件中实现。以下是它的核心逻辑步骤:
-
在
TimeComponent
组件中,使用useState
来保存当前时间和初始时间。 -
在
useEffect
钩子中,创建一个定时器,每200毫秒触发一次。这个定时器会计算当前时间与初始时间的差值, 并根据差值计算经过的小时、分钟和秒数。 -
使用
dayjs
库,将经过的时间格式化为时、分、秒的字符串形式,并通过setCurrentTime
函数更新当前时间的状态。 -
当组件被销毁时(即返回的清理函数被调用),清除定时器,确保不再执行未处理的计时器回调。
计算时间的核心逻辑包括使用定时器周期性地计算当前时间与初始时间的差值,然后通过格式化函数将其转换为可读的字符串表示形式,并将其更新为组件的状态。这样,每200毫秒,TimeComponent
组件就会更新一次显示的实时时间。
代码实现
js
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import dayjs from 'dayjs';
function TimeComponent({ initialTime }) {
const _initialTime = initialTime || dayjs(0);
const [storedInitialTime] = useState(_initialTime);
const [currentTime, setCurrentTime] = useState(_initialTime.format('HH:mm:ss'));
useEffect(() =>{
const intervalId = setInterval(() => {
const elapsedTime = dayjs.duration(+new Date() - storedInitialTime);
setCurrentTime(elapsedTime.format('HH:mm:ss'));
}, 200);
return () =>{
clearInterval(intervalId);
};
}, []);
return (
<span>
Current Time: {currentTime}
</span>
);
}
function TimerC({ ids, initialTime}) {
useEffect(() => {
ids.forEach((containerId) => {
const container = document.getElementById(containerId);
ReactDOM.render(<timecomponent initialTime={initialTime} />, container);
});
return () =>{
ids.forEach((containerId) =>{
const container = document.getElementById(containerId);
ReactDOM.unmountComponentAtNode(container);
});
};
}, [ids]);
return null;
}
export default TimerC;
组件使用
js
import React from 'react';
import TimerC from './TimerC';
function App() {
return (
<div>
{/* ...其他组件内容 */}
<div id="container1"></div>
<div id="container2"></div>
<div id="container3"></div>
{/* ...其他组件内容 */}
<TimerC ids={'container1','container2','container3']} initialTime={}>
</TimerC></div>
);
}
export default App;