perf: ⚡ use setInterval instead of setTimeout chain in MkTime
This commit is contained in:
parent
be83ac8f43
commit
c462d48bdb
|
@ -1,21 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<time :title="absolute">
|
<time :title="absolute">
|
||||||
<template v-if="mode === 'relative'">{{ relative }}</template>
|
<template v-if="invalid">{{ i18n.ts._ago.invalid }}</template>
|
||||||
|
<template v-else-if="mode === 'relative'">{{ relative }}</template>
|
||||||
<template v-else-if="mode === 'absolute'">{{ absolute }}</template>
|
<template v-else-if="mode === 'absolute'">{{ absolute }}</template>
|
||||||
<template v-else-if="mode === 'detail'"
|
<template v-else-if="mode === 'detail'"
|
||||||
>{{ absolute }} ({{ relative }})</template
|
>{{ absolute }} ({{ relative }})</template
|
||||||
>
|
>
|
||||||
<slot></slot>
|
|
||||||
</time>
|
</time>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onUnmounted } from "vue";
|
import { onMounted, onUnmounted } from "vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
import { dateTimeFormat } from "@/scripts/intl-const";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
time: Date | string;
|
time: Date | string | number | null;
|
||||||
|
origin?: Date | null;
|
||||||
mode?: "relative" | "absolute" | "detail";
|
mode?: "relative" | "absolute" | "detail";
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
|
@ -24,12 +26,23 @@ const props = withDefaults(
|
||||||
);
|
);
|
||||||
|
|
||||||
const _time =
|
const _time =
|
||||||
typeof props.time === "string" ? new Date(props.time) : props.time;
|
props.time == null
|
||||||
const absolute = _time.toLocaleString();
|
? NaN
|
||||||
|
: typeof props.time === "number"
|
||||||
|
? props.time
|
||||||
|
: (props.time instanceof Date
|
||||||
|
? props.time
|
||||||
|
: new Date(props.time)
|
||||||
|
).getTime();
|
||||||
|
const invalid = Number.isNaN(_time);
|
||||||
|
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
|
||||||
|
|
||||||
let now = $shallowRef(new Date());
|
let now = $ref((props.origin ?? new Date()).getTime());
|
||||||
const relative = $computed(() => {
|
const relative = $computed<string>(() => {
|
||||||
const ago = (now.getTime() - _time.getTime()) / 1000; /*ms*/
|
if (props.mode === "absolute") return ""; // absoluteではrelativeを使わないので計算しない
|
||||||
|
if (invalid) return i18n.ts._ago.invalid;
|
||||||
|
|
||||||
|
const ago = (now - _time) / 1000; /*ms*/
|
||||||
return ago >= 31536000
|
return ago >= 31536000
|
||||||
? i18n.t("_ago.yearsAgo", { n: Math.round(ago / 31536000).toString() })
|
? i18n.t("_ago.yearsAgo", { n: Math.round(ago / 31536000).toString() })
|
||||||
: ago >= 2592000
|
: ago >= 2592000
|
||||||
|
@ -49,22 +62,36 @@ const relative = $computed(() => {
|
||||||
: i18n.ts._ago.future;
|
: i18n.ts._ago.future;
|
||||||
});
|
});
|
||||||
|
|
||||||
function tick() {
|
|
||||||
// TODO: パフォーマンス向上のため、このコンポーネントが画面内に表示されている場合のみ更新する
|
|
||||||
now = new Date();
|
|
||||||
|
|
||||||
tickId = window.setTimeout(() => {
|
|
||||||
window.requestAnimationFrame(tick);
|
|
||||||
}, 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tickId: number;
|
let tickId: number;
|
||||||
|
|
||||||
if (props.mode === "relative" || props.mode === "detail") {
|
function tick() {
|
||||||
tickId = window.requestAnimationFrame(tick);
|
const _now = new Date().getTime();
|
||||||
|
const agoPrev = (now - _time) / 1000; /*ms*/ // 現状のinterval
|
||||||
|
|
||||||
|
now = _now;
|
||||||
|
|
||||||
|
const ago = (now - _time) / 1000; /*ms*/ // 次のinterval
|
||||||
|
const prev = agoPrev < 60 ? 10000 : agoPrev < 3600 ? 60000 : 180000;
|
||||||
|
const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000;
|
||||||
|
|
||||||
|
if (!tickId) {
|
||||||
|
tickId = window.setInterval(tick, next);
|
||||||
|
} else if (prev < next) {
|
||||||
|
window.clearInterval(tickId);
|
||||||
|
tickId = window.setInterval(tick, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!invalid &&
|
||||||
|
props.origin === null &&
|
||||||
|
(props.mode === "relative" || props.mode === "detail")
|
||||||
|
) {
|
||||||
|
onMounted(() => {
|
||||||
|
tick();
|
||||||
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.cancelAnimationFrame(tickId);
|
if (tickId) window.clearInterval(tickId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { lang } from "@/config";
|
||||||
|
|
||||||
|
export const versatileLang = (lang ?? "ja-JP").replace("ja-KS", "ja-JP");
|
||||||
|
export const dateTimeFormat = new Intl.DateTimeFormat(versatileLang, {
|
||||||
|
year: "numeric",
|
||||||
|
month: "numeric",
|
||||||
|
day: "numeric",
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
second: "numeric",
|
||||||
|
});
|
||||||
|
export const numberFormat = new Intl.NumberFormat(versatileLang);
|
Loading…
Reference in New Issue