一、封装 ajax 库
- 模拟 jq 的 ajax 方法,封装我们自己的 ajax 库
调用:
ajax({
url: 'aside.json?a=b',
type: 'POST',
data: {
user: 111,
pwd: 1234546
},
cache: false,
async: false,
error(err) {
console.log(err);
},
success (data) {
console.log(data)
}
});
根据调用分析方法
- 根据请求方式处理传递给服务器的参数;如果是 get 请求参数拼接到 url 末尾,如果是 post 请求直接放到请求体中;
- 根据是否需要缓存,判断是否给 get 请求拼接时间戳;
- 发送 ajax;
- 当成功后执行成功的回调,并且传入返回数据;
- 如果失败处理失败的回调,传入失败的信息;
function ajax({url = '', type = 'GET', data = null, async = true, cache = false, success, error}) {
// 1. 根据 get 向 url 末尾拼接
if (type.toUpperCase() === 'GET') {
let qs = new URLSearchParams();
for (let key in data) {
qs.append(key, data[key])
}
// 处理缓存
if (!cache) {
qs.append('_', Date.now())
}
qs = qs.toString();
// 判断原来的 url 是否有参数,如果有参数就有?
url.includes('?')
? url += `&${qs}`
: url += `?${qs}`;
data = null; // get 请求不需要请求体,这里处理成 null
} else {
data && (data = JSON.stringify(data))
}
// 处理 ajax
let xhr = new XMLHttpRequest();
xhr.open(type, url, async);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (/^(2\d{2}|304)$/.test(xhr.status)) {
success(JSON.parse(xhr.responseText))
} else {
let {status, statusText} = xhr;
error({status, statusText});
}
}
};
xhr.send(data);
}
二、宏任务微任务
- 主任务队列和等待任务队列:
- 主任务队列存储的都是同步任务;
- 等待任务存储的都是异步任务;
- 当主任务对列执行完成之后,接下来会把异步任务中到达执行条件的任务拿出来放到主任务队列中执行;
setTimeout(function () {
console.log(1)
}, 0);
console.log(5);
let p = new Promise((resolve, reject) => {
console.log(2);
resolve();
console.log(3);
});
console.log(6);
p.then(() => {
console.log(4)
});
console.log(7);
// 5 2 3 6 7【同步任务执行完了】 4 1
思考?
为什么是4先打印出来?
- 在异步任务队列中又将异步任务分为两类:宏任务和微任务;
当同步任务执行完毕后,先去等待任务队列中的所有的微任务找出来,挨个执行,当微任务执行完毕,再把所有的宏任务执行;
哪些是微任务:
- Promise 的 then
- async 函数中 await 后面的代码;
- process.nextTick
哪些是宏任务:
定时器 setInterval 和 setTimeout
三、async和await
async 和 await 是什么?
async / await 是 ES6 新增的关键字,用于把异步处理程序变为同步;
- async 在函数定义时使用,用 async 函数声明的函数默认返回一个 promise 实例,返回值可以直接 .then
- await 右边如果是 Promise 就等着 Promise 状态发生变更,如果是同步代码直接执行;如果 await 执行的方法返回的 promise 对象,我们可以直接在对象使用 then 方法;此外,await 下面的代码都会变成微任务
示例:
function fx() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('123')
}, 3000)
})
}
async function fn() {
fx();
console.log(1)
}
fn().catch((e) => console.log(e));
- 真实项目中我们经常将 async、Promise 和 ajax 结合起来一起使用:
function getAside() {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('get', 'aside.json', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
}
};
xhr.send();
})
}
function getBanner() {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('get', 'banner.json', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
}
};
xhr.send();
})
}
async function bindHTML() {
let aside = await getAside(); // await 会返回 promise resolve 后的值
let [,,third] = aside;
let banner = await getBanner(third);
console.log(aside);
console.log(banner);
}
bindHTML();
四、综合宏任务微任务练习
- 请写出下面代码的输出顺序;
async function async1() {
console.log(1);
await async2();
console.log(2);
}
async function async2() {
console.log(3)
}
console.log(4)
setTimeout(function() {
console.log(5)
}, 0);
async1();
new Promise( function( resolve ) {
console.log(6)
resolve();
} ).then( function() {
console.log(7)
} )
console.log(8);
// 4 1 3 6 8 2 7 5
五、cookie
cookie 是什么?
HTTP 协议是无状态的,即服务器向客户端发送数据结束后,连接被关闭,服务器会忘记这一切;因为 HTTP 协议不会记录这些东西,这就有一个问题,当我们登录后向服务器请求了一次资源。但当我们请求第二次时,服务器就已经不知道我已经登陆过了,所以它认为我还没登录,这就导致每次请求都需要登录;
为了解决这个问题,HTTP 协议发明了 cookie;所以 cookie 严格说是 http 协议的一部分,不属于客户端也不属于服务端;
Cookie 存储在客户端,但是服务端和客户端都可以操作 cookie;
COOKIE 用于记录用户的信息,并且在每次请求服务器的时候都会自动带上 cookie 的信息;等服务器接收到请求就可以从 cookie 获取到用户的信息,从这些信息中获知用户是否登录;
用 js 操作 cookie
- js 获取 cookie document.cookie
console.log(document.cookie);
- js 设置 cookie document.cookie = ‘key=value’;
document.cookie = 'age=18;title=宇宙集团军总司令;path=/';
document.cookie = 'title=宇宙集团军总司令';
document.cookie = 'name=mabin; expires=Thu, 11 Jul 2019 14:00:00 GMT; path=/';
document.cookie = 'name=mabin1; path=/'
- 设置 cookie 时需要注意:
- cookie 是个字符串,并且值要写成 ‘key=value;’的形式;
- cookie 设置可以同名,后面的并不会覆盖前面的
cookie 的属性
- domain 当前可以访问 cookie 的域
- path 可以访问当前 cookie 的路径,一般设置为 / 表示根目录; 这个路径的子级可以访问上面的,但是高层级的路径不能访问低层级的 cookie;例如 path 设置为/a ,/a/b 可以使用/a的 cookie;但是如果设置为 /a/b,/a就不能使用 /a/b 的 cookie;
- expires: cookie 的过期时间,cookie 都是有时效的,是个 GMT 时间,如果超过这个时间,cookie 失效,浏览器会删除它;(这也是删除 cookie 的原理,将 cookie 的过期时间设置为一个过去的时间,一般设置为 1970 年)
document.cookie = "name=mabin1; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
- maxAge cookie 有效期,和 expires 不同,这个是一个以毫秒为单位的绝对时间,如 1000ms 或者 5000ms;(这个东西只能服务端设置)
- http-only: 表示该属性只能由 http 使用,不允许客户端通过 js 代码操作;
六、session
session 是什么?
session:服务端技术,意为会话控制;
和 cookie 不同,session 是保存在服务器上的,而且并不会随着 http 传递;因为 cookie 保存在客户端,还是很不安全的,服务器为了杜绝这种事情,在服务器上也搞了一个存储用户信息的东西,这就是 session。一般用于用户的登录状态,用户 id 等敏感信息的保存;
session 怎么使用:
- 以登录为例,当我们登录后,服务端会在 cookie 中设置当前用户的登录状态为已经登录,
- 同时在服务器上生成一份 session 文件,session 中存储用户 id,登录状态的有效期限等;这个 session 文件有一个 id,生成 session 文件后,服务器还会把 session-id 写进 cookie。然后返回给客户端,此时用户的客户端收到的 cookie 中包含登录状态和 session-id;
- 等下一次再去请求时,http 协议会自动带着所有的 cookie 去请求;等服务器收到请求后,从 cookie 中把用户信息拿出来,然后再根据 session-id 把 session 读取出来,再从 session 中查询登录状态,如果登录状态有效,就继续正常的响应,否则就返回登录失效的状态,要求用户登录;
session 和 cookie 的区别:
- session 存在服务器上,是服务器的技术;
- cookie 存在客户端,是 http 协议的一部分;
session 和 cookie 的联系:
cookie 中保存着 session 的 id,在 http 请求中会带着 cookie 请求,服务器从 cookie 中获取 session 的 id;
七、token 机制
什么是 token
token 称为令牌,是一种身份校验机制;
- token 不是和 cookie 或者 session 一样的一种技术,只是一种机制;
token 原理
一般在用户登录时,客户端把用户的用户名和密码传递给服务器,服务端进行校验,如果没有问题,就会生成一个 token;
token 是一个字符串,这个字符串一般是加密过的,一般包含了用户 id,登录的时间戳,以及 sign (签名,生成 token 的前几位进行加密的结果,可以防止 token 被篡改,一旦篡改了后面这一段和前面的对不上了);
生成 token 后,服务器把 token 返回给前端,然后前端下次请求的时候需要带上这个 token,然后服务端在接收到请求后会首先校验这个 token 是否有效,如果有效继续受理请求,如果无效则直接拒绝;
使用 token 的方式
- 服务端也可以直接把 token 写进 cookie 中,下次客户端发起请求时会自动带上;
- 服务端也可以作为数据返给前端,但是前端此时需要手动的保存这个 token,可以保存在 localStorage 中,下次发请求时从 ls 中取出作为参数传给服务器即可
- 服务端返回 token 后,客户端可以把这个 token 写进请求头中,然后服务端每次从请求头中获取;