JS 中也可以通过 new Date() 获取时间,但是获取的是客户端时间,这个事件客户端可以任意修改。

所以在项目中,如果时间作为重要参考标准,需要使用服务器时间。

思路:

  • 向服务器发送一个请求

  • 通过服务器返回的响应头获取到服务器时间

  • 对获取的时间进行处理

  • 将时间保存为全局变量

  • 每过 1s,这个全局时间累加 1s,然后根据这个全局时间进行倒计时,避免一直发送请求

关于 new Date()

new Date() 获取当前客户端本机时间(是标准的事件格式数据 => 对象)

new Date(TimeStr) 把指定的时间字符串转换为标准时间数据,时间字符串支持很多格式,例如:YYYY-MM-DD HH:MM:SSYYYY/MM/DD

两个 Date 实例相减得到的是时间相差的毫秒差

实现

解决时差

由于服务器端返回数据需要时间,所以客户端拿到返回的时间时,与现在的时间会存在一定误差,这个误差只能让其变小,无法彻底解决。

解决方案:

(1)在 Ajax 状态码为 2 的时候,即接受到响应头的时候,就可以从中获取信息,而不是等到更靠后的状态

(2)把请求方式直接设置为 head,只获取请求头信息即可,响应主体内容不需要

【特别的】即使我们向服务器发送一个不存在的请求地址,返回的是 404 状态码,但是响应头信息中都会存在服务器时间(不建议使用,产生 404 请求不友好)

解决持续请求

一个客户端每间隔 1 秒都会向服务器发送一个新的请求,如果访问用户增多,服务器压力就非常大,很容易瘫痪(负载均衡)

解决方案:

  • 只在页面打开的时候,获取服务器时间

  • 将获取的时间存储为全局变量

  • 每一秒刷新的时候,都将其在原来的基础上一直累加,而不是重新从服务器获取最新时间

代码

  1. let oTime = document.querySelector('.time'),
  2. oTimeSpan = oTime.querySelector('span'),
  3. autoTimer = null,
  4. nowTime = null;
  5. //=> 从服务器端获取时间
  6. let queryTime = function queryTime() {
  7. return new Promise(reslove => {
  8. //=> 向服务器发请求,把结果存储起来
  9. let xhr = new XMLHttpRequest();
  10. xhr.open('head', 'date.json');
  11. xhr.onreadystatechange = () => {
  12. if (xhr.readyState === 2 && /^(2|3)\d{2}$/.test(xhr.status)) {
  13. nowTime = new Date(xhr.getResponseHeader('date'));
  14. reslove();
  15. }
  16. };
  17. xhr.send(null);
  18. });
  19. }
  20. // 计算时间
  21. let computedTime = function computedTime() {
  22. let tarTime = new Date('2018-09-21 22:00:00'),
  23. diffTime = tarTime - nowTime;
  24. if (diffTime >= 0) {
  25. let hours = Math.floor(diffTime / 3600000);
  26. diffTime = diffTime - hours * 3600000;
  27. let minutes = Math.floor(diffTime / 60000);
  28. diffTime = diffTime - minutes * 60000;
  29. let seconds = Math.floor(diffTime / 1000);
  30. hours < 10 ? hours = '0' + hours : null;
  31. minutes < 10 ? minutes = '0' + minutes : null;
  32. seconds < 10 ? seconds = '0' + seconds : null;
  33. oTimeSpan.innerHTML = `${hours} : ${minutes} : ${seconds}`;
  34. nowTime = new Date(nowTime.getTime() + 1000);
  35. return;
  36. }
  37. oTimeSpan.innerHTML = '开始抢购!!';
  38. clearInterval(autoTimer);
  39. }
  40. let promise = queryTime();
  41. promise.then(computedTime);
  42. autoTimer = setInterval(computedTime, 1000);