案例:倒计时
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>倒计时</title>
</head>
<body>
<div id="box"></div>
<script>
//new Date() 获取客户端本地当前时间(不能拿它做重要依据,因为用户可以随意修改)
/*
* 倒计时抢购需要从服务器获取当前时间 AJAX
* 问题:时间差(从服务器把时间给客户端,到客户端获取到这个信息,中间经历的时间就是时间差,而时间差是不可避免的,我们应尽可能减少这个误差)
* - 从响应头获取时间(AJAX异步)
* - 基于 HEAD 请求(只获取响应头信息)
*/
let target = new Date('2019/09/14 13:27:00'),
now = null,
timer = null;
// 从服务器获取时间:获取到时间后再做其他的事情
function func(callback) {
let xhr = new XMLHttpRequest;
xhr.open('HEAD', 'json/data.json', true);
xhr.onreadystatechange = function () {
if (!/^(2|3)\d{2}$/.test(xhr.status)) return;
if (xhr.readyState === 2) {
now = new Date(xhr.getResponseHeader('Date'));
callback && callback();
}
}
xhr.send(null);
}
// 开启倒计时模式
function computed() {
let spanTime = target - now;
if (spanTime <= 0) {
// 到抢购时间:结束定时器
clearInterval(timer);
timer = null;
box.innerHTML = "开抢~~";
return;
}
let hours = Math.floor(spanTime / (60 * 60 * 1000));
spanTime -= hours * 60 * 60 * 1000;
let minutes = Math.floor(spanTime / (60 * 1000));
spanTime -= minutes * 60 * 1000;
let seconds = Math.floor(spanTime / 1000);
box.innerHTML =
`距离抢购还剩 ${hours<10?'0'+hours:hours}:${minutes<10?'0'+minutes:minutes}:${seconds<10?'0'+seconds:seconds}`;
// 每一次计算完,我们需要让NOW在原来的基础上加上一秒(第一次从服务器获取到时间,后期直接基于这个时间自己加即可,不要每隔一秒重新从服务器拿)
now = new Date(now.getTime() + 1000);
}
func(() => {
// 已经从服务器获取时间了
computed();
timer = setInterval(computed, 1000);
});
</script>
</body>
</html>
封装AJAX库
/*
* 支持的参数配置项
* url
* method:'GET'
* data:null
* dataType:'json'
* async:true
* cache:true
* success:null
* error:null
* headers:null
* timeout:null
*/
~ function () {
function ajax(options) {
return new init(options);
}
/* == AJAX处理的核心 == */
let regGET = /^(GET|DELETE|HEAD|OPTIONS)$/i;
let defaults = {
url: '', // 请求的API接口地址
method: 'GET', // 请求方式 GET / POST / DELETE / PUT / HEAD / OPTIONS
data: null, // 传递给服务器的信息:支持格式 STRING 和 OBJECT,如果是 OBJECT,我们需要把其处理为 x-www-form-urlencoded 格式;GET 请求是把信息作为问号参数传递给服务器,POST 请求是放到请求主体中传递给服务器;
dataType: 'JSON', // 把服务器返回结果处理成为对应的格式 JSON / TEXT / XML
async: true, // 是否异步请求
cache: true, // 只对 GET 请求有作用:设置为 FALSE,在 URL 的末尾加随机数来清除缓存
timeout: null, // 超时时间
headers: null, // 设置请求头信息(请求头信息不能是中文,所以我们需要为其编码)
success: null, // 从服务器获取成功后执行 把获取的结果、状态信息、XHR 传递给它
error: null // 获取失败后执行 把错误信息传递给它
};
function init(options = {}) {
// 参数初始化:把传递的配置项替换默认的配置项
this.options = Object.assign(defaults, options);
this.xhr = null;
this.send();
}
ajax.prototype = {
constructor: ajax,
version: 1.0,
// 发送AJAX请求
send() {
let xhr = null,
{url, method, async, data, cache, timeout, dataType, headers, success, error} = this.options;
this.xhr = xhr = new XMLHttpRequest;
// 处理DATA:如果是GET请求把处理后的DATA放在URL末尾传递给服务器
data = this.handleData();
if (data !== null && regGET.test(method)) {
url += `${this.checkASK(url)}${data}`;
data = null;
}
// 处理CACHE:如果是GET并且CACHE是FALSE需要清除缓存
if (cache === false && regGET.test(method)) {
url += `${this.checkASK(url)}_=${Math.random()}`;
}
xhr.open(method, url, async);
// 超时处理
timeout !== null ? xhr.timeout = timeout : null;
// 设置请求头信息
if (Object.prototype.toString.call(headers) === "[object Object]") {
for (let key in headers) {
if (!headers.hasOwnProperty(key)) break;
xhr.setRequestHeader(key, encodeURIComponent(headers[key]));
}
}
xhr.onreadystatechange = () => {
let {
status,
statusText,
readyState: state,
responseText,
responseXML
} = xhr;
if (/^(2|3)\d{2}$/.test(status)) {
// 成功
if (state === 4) {
switch (dataType.toUpperCase()) {
case 'JSON':
responseText = JSON.parse(responseText);
break;
case 'XML':
responseText = responseXML;
break;
}
success && success(responseText, statusText, xhr);
}
return;
}
// 失败的
typeof error === "function" ? error(statusText, xhr) : null;
}
xhr.send(data);
},
// 关于DATA参数的处理
handleData() {
let {
data
} = this.options;
if (data === null || typeof data === "string") return data;
// 只有DATA是一个对象,我们需要把它变为xxx=xxx&xxx=xxx这种格式字符串
let str = ``;
for (let key in data) {
if (!data.hasOwnProperty(key)) break;
str += `${key}=${data[key]}&`;
}
str = str.substring(0, str.length - 1);
return str;
},
// 检测URL中是否存在问号
checkASK(url) {
return url.indexOf('?') === -1 ? '?' : '&';
}
};
init.prototype = ajax.prototype;
window._ajax = ajax;
}();