引言
在一些社区,比如微博、朋友圈等,发布的动态会有个相对本机时间转换后的相对时间,
如图中波浪线圈出的时间
一般在服务端的存储时间格式是 Unix 时间戳,比如 2017-01-01 00:00 00 的时间戳是1483200000。前端在拿到数据后,将它转换为可读的时间格式再显示出来。
为了显出实时性,在一些社交类产品中,甚至会实时转换为几秒钟前、几分钟前、几小时前等不同的格式,这样比直接转换为年、月、日、时、分、秒更友好。
本示例就来实现这样一个自定义指令 v-time
将表达式传入的时间戳实时转换为相对时间
。
便于演示效果,我们初始时定义了两个时间
- index.html
<body>
<div id="app">
<div v-time="timeNow"></div>
<div v-time="timeBefore"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="./time.js"></script>
<script src="./index.js"></script>
</body>
- index.js
const vm = new Vue({
el: '#app',
data: {
timeNow:(new Date()).getTime(),
timeBefore: 1488930695721
},
components: {},
created() {},
methods: {},
})
timeNow 是当前的时间, timeBefore 是一个写死的时间 2017-03-08
注意:本示例所用的时间都是毫秒级
的,如果服务端返回秒级的时间戳,需要乘以1000后
再使用
时间转换的逻辑
- 1分钟以前,显示刚刚
- 1分钟 - 1小时之间,显示“xx分钟前”
- 1小时 - 1天之间,显示“xx小时前”
- 1天 - 1个月(31天)之间,显示“xx天前”
为了使判断逻辑更简单,统一使用时间戳进行大小判断。
在写指令 v-time 之前,需要先写一系列与时间相关的函数,我们声明一个对象 Time
,把它们都封装在里面。
Time.getFormatTime()方法
就是自定义指令v-time所需要的,入参为毫秒级时间戳
,返回已经整理好时间格式的字符串
- time.js
const Time = {
// 获取当前时间戳
getUnix() {
const date = new Date();
return date.getTime();
},
// 获取今天0点0分0秒的时间戳
getTodayUnix() {
const date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
return date.getTime();
},
// 获取今年1月1日0点0分0秒的时间戳
getYearUnix() {
const date = new Date();
date.setMonth(0);
date.setDate(1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
return date.getTime();
},
// 获取标准年月日
getLastDate(time) {
const date = new Date(time);
const year = date.getFullYear();
const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
return year + '-' + month + '-' + day;
},
// 转换时间
getFormatTime(timestamp) {
const now = this.getUnix(); // 当前时间戳
const today = this.getTodayUnix(); // 今天0点0分0秒时间戳
const year = this.getYearUnix(); // 今年1月1日0点0分0秒的时间戳
const timer = (now - timestamp) / 1000; // 获取传递进来的时间和当前时间的差值,并转换为秒级时间戳
let tip;
if(timer < 60 || Math.floor(timer / 60) <= 0) {
tip = '刚刚'
} else if(timer < 3600) {
tip = Math.floor(timer / 60) + '分钟前';
} else if(timer >= 3600 && (timestamp - today) >= 0) {
tip = Math.floor(timer / 3600) + '小时前';
} else if((timer / 86400) <= 31){
tip = Math.floor(timer / 86400) + '天前';
} else {
tip = this.getLastDate(timestamp);
}
return tip;
}
}
全局注册自定义指令v-time
const vm = new Vue({
el: '#app',
data: {
timeNow:(new Date()).getTime(),
timeBefore: 1488930695721
},
components: {},
// 自定义全局指令v-time
directives: {
time:{
bind(el, binding) {
el.innerHTML = Time.getFormatTime(binding.value);
el.__timeout__ = setInterval(function(){
el.innerHTML = Time.getFormatTime(binding.value);
}, 60000)
},
unbind(el) {
clearInterval(el.__timeout__);
delete el.__timeout__;
},
}
},
created() {},
methods: {},
})
使用效果
<div v-time="timeNow" style="margin-top:40px;"></div>
<div v-time="1619750026000" style="margin-top:20px;"></div>
<div v-time="1619735626000" style="margin-top:20px;"></div>
<div v-time="1619562826000" style="margin-top:20px;"></div>