Vue.js предоставляет несколько механизмов для интеграции с timeago.js: composables (Vue 3), директивы, компоненты. Выбор зависит от требуемого уровня инкапсуляции.
// src/main.js (Vue 3)
import { createApp } from 'vue';
import App from './App.vue';
import { register } from 'timeago.js';
import ru from 'timeago.js/esm/lang/ru';
register('ru', ru);
createApp(App).mount('#app');
<script setup>
import { format } from 'timeago.js';
const props = defineProps({ date: String });
</script>
<template>
<time :datetime="date">{{ format(date, 'ru') }}</time>
</template>
// composables/useTimeAgo.js
import { ref, watchEffect, onUnmounted } from 'vue';
import { format } from 'timeago.js';
export function useTimeAgo(date, locale = 'ru') {
const label = ref(format(date, locale));
const id = setInterval(() => {
label.value = format(date, locale);
}, 60000);
onUnmounted(() => clearInterval(id));
return label;
}
Использование в компоненте:
<script setup>
import { useTimeAgo } from '@/composables/useTimeAgo';
const props = defineProps({ date: String });
const label = useTimeAgo(props.date, 'ru');
</script>
<template>
<time :datetime="props.date">{{ label }}</time>
</template>
// composables/useAdaptiveTimeAgo.js
import { ref, onUnmounted } from 'vue';
import { format } from 'timeago.js';
function getInterval(date) {
const diff = Math.abs(Date.now() - new Date(date).getTime());
if (diff < 60000) return 10000;
if (diff < 3600000) return 60000;
if (diff < 86400000) return 1800000;
return 86400000;
}
export function useAdaptiveTimeAgo(date, locale = 'ru') {
const label = ref(format(date, locale));
let timer;
function tick() {
label.value = format(date, locale);
timer = setTimeout(tick, getInterval(date));
}
timer = setTimeout(tick, getInterval(date));
onUnmounted(() => clearTimeout(timer));
return label;
}
<!-- components/TimeAgo.vue -->
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { render, cancel } from 'timeago.js';
const props = defineProps({
date: { type: String, required: true },
locale: { type: String, default: 'ru' },
});
const el = ref(null);
onMounted(() => {
if (el.value) render(el.value, props.locale);
});
onBeforeUnmount(() => {
if (el.value) cancel(el.value);
});
</script>
<template>
<time ref="el" :datetime="date" />
</template>
<script setup>
import { ref, watch } from 'vue';
import { format } from 'timeago.js';
const props = defineProps({ date: String });
const locale = inject('locale', ref('ru'));
const label = ref('');
function update() {
label.value = format(props.date, locale.value);
}
update();
watch([() => props.date, locale], update);
</script>
<template>
<time :datetime="props.date">{{ label }}</time>
</template>
<script setup>
import { computed } from 'vue';
import { format } from 'timeago.js';
const props = defineProps({ date: String });
const label = computed(() => format(props.date, 'ru'));
</script>
<template>
<time :datetime="props.date">{{ label }}</time>
</template>
computed пересчитывается только при изменении
props.date, но не обновляется со временем. Подходит для
статичных списков.
Интеграция с locale из Pinia store:
// stores/locale.js
import { defineStore } from 'pinia';
export const useLocaleStore = defineStore('locale', {
state: () => ({ current: 'ru' }),
actions: {
setLocale(locale) {
this.current = locale;
}
}
});
<script setup>
import { computed } from 'vue';
import { format } from 'timeago.js';
import { useLocaleStore } from '@/stores/locale';
const props = defineProps({ date: String });
const store = useLocaleStore();
const label = computed(() => format(props.date, store.current));
</script>
<template>
<time :datetime="date">{{ label }}</time>
</template>
<script>
import { format } from 'timeago.js';
export default {
props: {
date: String,
},
data() {
return { label: '' };
},
created() {
this.label = format(this.date, 'ru');
this.timer = setInterval(() => {
this.label = format(this.date, 'ru');
}, 60000);
},
beforeDestroy() {
clearInterval(this.timer);
},
};
</script>