HTTP、AJAX
- 为什么要本地存储?
• 真实项目中经常需要在一个网站的多个页面间共享数据,如果登录状态,购物车信息等
但是浏览器打开页面时首先形成一个顶层的作用域 window,每次页面打开都会形成一个单独的作用域,页面之间是不可以互通访问变量的;但是项目中经常会用到页面间传递数据的要求;
每个页面又都是在浏览器中打开的,如果可以把值存储到浏览器中,让浏览器作为一个中介, A 页面把值存到浏览器中,B 页面从从浏览器中把 A 存储的值取出来; - 本地存储解决方案:
• cookie (cookie 是 http 协议的一部分)
• localStorage 本地存储(HTML5 新增的,浏览器技术)
• sessionStorage 会话存储(HTML5 新增,浏览器技术) - localStorage 和 sessionStorage
HTML5 提供了本地存储方式:
• localStorage 永久存储(如果手动触发删除或者用户清除)
• sessionStorage 会话存储
localStorage
window.localStorage 对象
console.log(window.localStorage);
• localStorage.setItem(key, value) 存储数据
• localStorage 存储数据时键值的形式存储的;
• key 键
• value 值
• key 和 value 都需要是字符串类型,如果不是字符串类型,浏览器会隐式调用 toString 将其转换为字符串;
localStorage.setItem({name: 1}, {name: 2});
localStorage.setItem(1, 2)
// '[object Object]' 如果你存的 key 和 value 不是字符串类型的,它会把他们转成字符串,调用
// toString() 方法,对象 toString() -> '[object Object]';
• 不会有两个相同的 key,如果 key 相同后面的值会覆盖前面的值;
localStorage.setItem('user', 'mabin');
localStorage.setItem('user', 'mabin2');
• 如果批量存储数据太麻烦,直接存储 JSON 字符串 等你想要用的时候要记得 JSON.parse() 变成对象
let ary = {
code: 0,
data: {
page: 1,
limit: 10,
list: [
{
course: 18,
subject: 15,
fire: 3,
price: 180
},
{
course: 18,
subject: 15,
fire: 3,
price: 180
},
{
course: 18,
subject: 15,
fire: 3,
price: 180
},
{
course: 18,
subject: 15,
fire: 3,
price: 180
}
]
},
msg: 'ok'
};
// JSON.parse() 把JSON字符串变成对象
// JSON.stringify() 把对象变成JSON格式的字符串
localStorage.setItem('someJson', JSON.stringify(ary));
• localStorage.getItem(key) 获取 ls 中存储的数据。
获取回来的都是字符串类型的
let json = localStorage.getItem('someJson');
let uk = localStorage.getItem('uk'); // null
console.log(uk); // 获取不存在的 key 返回 null
console.log(json);
console.log(typeof json); // string
localStorage.clear() // 清空 localStorage,一般退出登录时可能需要你把东西都删了
localStorage.removeItem(key) // 删除指定 key 的数据
localStorage.removeItem('user');
• sessionStorage 会话存储 是客户端本地存储的一种
• setItem(key, value) 是服务端技术,存在服务器上的
• getItem(key) 获取指定 key 的值
• removeItem(key) 删除指定 key 的数据
• sessionStorage.setItem(‘ok’, ‘0’); 存储,key 和 value 是字符串类型的
• localStorage 和 sessionStorage 的区别:
注意会话存储:刷新,并不会使 sessionStorage 失效,但是关闭页面后 sessionStorage 中的数据就没有了;
localStorage 是永久存储,如果不删除或者用户不清除就会一直有。而 sessionStorage 只是会话存储,只要页面不关闭有,如果页面关闭了,就消失了。
补充:Unix 时间戳是以秒为单位的,js 的时间戳是以毫秒为单位;PHP 的时间戳就是 unix 时间戳;如果服务端让你传给它时间戳,要问一下是毫秒还是秒。如果服务端要秒,要用 js 时间戳除以1000;如果服务端返回给你的是时间戳,如果是 unix 时间戳,需要乘以1000转换 js 时间戳;【unix 时间戳比 js 少三位,一看比较短就是 unix的】;
客户端:可以向服务器发请求,并接收返回的内容进行处理
服务器端:能够接收客户端请求,并且把相关资源的信息返回给客户端的
交互过程:
- URL 地址解析
- DNS 域名解析
客户端向服务器端请求;request 请求阶段 - 和服务器建立 TCP 连接
- 把客户端信息传递给服务器(发送 HTTP 请求)
- 服务器得到并处理请求(HTTP 响应内容)
服务器响应客户端请求:response 响应阶段 - 客户端渲染内容返回的内容
- 从服务器断开 TCP 连接
一些细节
- URL / URI / URN
URL(Uniform Resource Loactor):统一资源定位符,根据这个地址能找到对应的资源
URN(Uniform Resource Name):统一资源名称,一般指国际上通用的(标准的)一些名字(例如:国际统一发版的编号)
URI(Uniform Resource Identifier):统一资源标识符,URL 和 URI 的子集
一个完整的 URL 包含的内容:
1.协议
(http://)传输协议就是能够把客户端和服务器端通信的信息进行传输的工具
• http:超文本传输协议,除了传递文本,还可以传递媒体资源文件(流文件)及 XML 格式数据
• https:更加安全的 http ,一般涉及到支付的网站都要采用 https 协议 (S:SSL 加密传输)
• FTP:文件传送协议,一般应用于把本地资源上传到服务器端
FileZilla:FTP上传工具,通过这个工具,通过 FTP 传输协议,我们可以把本地的文件上传到服务器上
2. 域名
(www.zhufengpeixun.cn)
• 顶级域名:qq.com
• 一级域名:www.qq.com
• 二级域名:sprots.qq.com
• 三级域名:kbs.sports.qq.com
• .com:国际域名
• .cn:中文域名
• .com.cn
• .edu:教育
• .gov:政府机构
• .io:博客
• .org:官方组织
• .net:系统类
一个让用户方便记忆的名字,就叫域名,不通过域名,直接用外网 ip 地址也能访问服务器,但是外网 ip 很难被记住
给服务器通网后,会有两个 ip 地址:内网 ip 局域网内访问 | 外网 ip 外部用户可以基于外网 ip 访问到服务器
3. 请求资源路径名称
(/stu/index.html)
服务器接收到请求后要做的:
1.根据端口号找到对应的项目
2.根据请求资源的路径名称找到资源文件
3.读取资源文件中的内容,把内容返回
默认的路径名称:xxx.com/stu/ 不指定资源名,服务器会找默认资源,一般默认资源名是 default.html、index.html、… 这些都可以自己配置
注意伪 URL 地址的处理:URL 重写技术是为了增加 SEO 优化的,动态的网址一般不能被搜索引擎收录,所以我们要把动态网址静态化,此时需要的是重写 URL
4. 问号传参信息
(?from=wx&lx=1)
客户端想把信息传递给服务器:很多种方式
• URL 地址问号传参
• 通过请求报文传输(请求头和请求主体)
也可以不同页面之间的信息交互 如:从列表到详情
5. 端口号
(:80)取值范围(0-65535)
• http 默认端口号:80
• https 默认端口号:443
• ftp 默认端口号:21
• 数据库默认端口号:3306
如果项目采用的就是默认端口号,我们在书写地址的时候,不用加端口号,浏览器在发送请求的时候会帮我们默认给加上
用端口号来区分同一台服务器上,的不同项目
6. HASH值(#zhenyu)
• 也能充当信息传输的方式
• 锚点定位
• 基于 HASH 实现路由管控(不同的 HASH 值,展示不同的组件和模块)
7. URL 特殊字符的处理
请求的地址中如果出现非有效 Unicode 编码内容,现代浏览器会默认的进行编码
8. 基于 encodeURI 编码,我们可以基于 decodeURI 编码。我们一般用 encodeURL 编码的是整个 URL,这样整个 URL 中的特殊字符都会自动编译
9. encodeURIComponent / decodeURLCopmonent 它相对于 encodeURL 来说 不用给整个URL编码,而是给URL部分信息进行编码(一般都是问号传参的值编码)
客户端和服务器端进行信息传输的时候,如果需要把请求的地址和信息编码,我们则基于以上两种方式来处理,服务器端也存在这些方法,这样就可以统一编码解码了
10. 客户端还存在一种方式,针对于中文的编码方式 escape / unescape 这种方式一般只应用于客户端页面之间自己的处理,例如:从列表跳转到详情,我们可以把传递的中文信息基于这个编码,详情页获取编码后的信息再解码,再比如我们再客户端中的 cookie 信息,如果信息是中文,我们也基于这种方法解码
link.onclick = function {
// 获取当前页面的 URL 地址
let url = window.location.href;
// 跳转页面
window.open("http://baidu.com");
}
DNS服务器域名解析
DNS 服务器:域名解析服务器,在服务器上存储着,域名 <=> 服务器外网 IP 的相关记录
而我们发送请求时所谓的 DNS 解析,其实就是根据域名,在 DNS 服务器上查找到对应服务器的外网 IP
DNS 优化
• DNS 缓存(一般浏览器会在第一次解析后,默认建立缓存,时间很短,只有一分钟左右)不需要网络
• 减少 DNS 解析次数(一个网站中我们需要发送请求的域名和服务器尽可能减少既可)
• DNS 预获取 (dns-prefetch):在页面加载开始的时候,就把当前页面中需要访问其他域名(服务器)的信息进行提前 DNS 解析,以后加载到具体内容部分可以不用解析了
DNS Prefetch 即 DNS 预获取
• 减少DNS的请求次数
• 进行DNS预获取
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//static.360buyimg.com">
<link rel="dns-prefetch" href="//misc.360buyimg.com">
<link rel="dns-prefetch" href="//img10.360buyimg.com">
<link rel="dns-prefetch" href="//img11.360buyimg.com">
<link rel="dns-prefetch" href="//img12.360buyimg.com">
......
TCP协议
1. HTTP报文
请求报文:所有经过传输协议,客户端传递给服务器的内容,都被成为请求报文
• 起始行
• 请求头(请求首部)
• 请求主体
响应报文:所有经过传输协议,服务器返回给客户端的内存,都被成为响应报文
• HTTP 状态码
• 响应头
• 响应主体
HTTP 报文:请求报文 + 响应报文
谷歌浏览器:F12 -> network(所有客户端和服务器端的交互信息在这里都可以看到) -> 点击某一条信息,在右侧可以看到所有的 HTTP 报文信息
2. HTTP状态码
1 - 5 开头,三位数字,1开头基本没出现过
• 200:成功(OK)
• 201:Created 成功,一般应用于告诉服务器创建一个新文件,最后服务器创建成功后返回的状态码
• 204:No Content 没有内容 对于某些请求(例如:put 或者 delete)服务器不想处理,可以返回空内容,并且用204状态码告知
• 301:Moved Permanently 永久重定向,永久转移
• 302:Moved Temporarily 临时转移,很早以前基本上用302来做,但是现在主要用307来处理这个事情
• 304:Not Modified 设置 HTTP 的协商缓存
• 307:307的意思就是临时重定向 Temporary Redirect -> 主要用于:服务器的负载均衡、视频防盗等
• 400:Bad Request 传递给服务器的参数错误
• 401:Unauthorized 无权限访问
• 404:Not Found 请求地址错误
• 500:Internal Server Reeoe 未知服务器错误
• 503:Service Unavailable 服务器超负荷
3. 三次握手 & 四次挥手
http 协议建立和断开连接时不是一次就完成的,连接时而是通过三次握手,断开时要经历四次挥手;
• 三次握手
1.第一次握手:客户端发送 syn 码数据包给服务器,客户端要求和服务器建立连接;
2.第二次握手:服务端接收到连接请求后,会发送 ack 码数据到客户端,表示你的连接请求已经收到,再次询问客户端是否确认建立连接
3.第三次握手:客户端收到服务器的 ack 码后,检验是否正确,如果正确则再次发送 ack 给服务器,表示确认连接;
三次握手如果成功,客户端和服务端的连接成功建立,才会开始传递数据;
• 四次挥手:
1.当客户端发送数据结束后,会发送 fin 告知服务器,客户端要给服务器的数据传输完了;
2.服务端返回给客户端一个 ack 码,告知客户端已经知道数据传递完成。客户端收到 ack,就会把发送到服务端的通道关闭;
3.服务端数据传输结束后,也会发送 fin 给客户端;
4.当客户端收到 fin 后,会发送 ack 给服务端,表示客户端知道服务端已经发送完毕,服务器收到 ack 后就可以放心的关闭数据传输通道;
Connection: Keep-Alive 保持 TCP 不中断
4. 报文
HTTP 报文用于 http 协议交互的信息,因为 http 通信分为请求和响应两个阶段,所以报文分为两种: 请求报文和响应报文
报文分为:报文首部 空行 报文主体;
请求报文:
1.报文首部:请求首部分为请求行和请求头;请求行中包含 请求方法、协议、版本、URI
2.空行(CR+LF)
3.报文主体:客户端传递给服务端的数据
响应报文:
1.报文首部:状态行和响应头;状态行包含 http 协议版本,响应状态码
2.空行(CR+LF)
3.报文主体:响应体(服务端返回给客户端的数据)
浏览器渲染机制
1:浏览器渲染页面的步骤
• 解析 HTML,生成 DOM 树,解析 CSS,生成 CSSOM 树
• 将 DOM 树和 CSSOM 树结合,生成渲染树(Render Tree)
• Layout (回流): 根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小,这个阶段是回流
• Painting (重绘): 根据渲染树以及回流得到的几何信息,得到节点的绝对像素
• Display:将像素发送给 GPU,展示在页面上
2:DOM的重绘和回流
• 重绘:元素样式的改变(但宽高、大小、位置等不变)
• 回流:元素的大小或者位置发生了变化(当页面布局和几何信息发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染
• 注意:回流一定会触发重绘,而重绘不一定会回流
3:前端性能优化之:避免DOM的回流
• 放弃传统操作 dom 的时代,基于 vue / react 开始数据影响视图模式
• 分离读写操作 (现代的浏览器都有渲染队列的机制)
• 样式集中改变
• 缓存布局信息
• 元素批量修改
• 动画效果应用到 position 属性为 absolute 或 fixed 的元素上(脱离文档流)
• CSS3 硬件加速(GPU加速)
• 牺牲平滑度换取速度
• 避免 table 布局和使用 css 的 javascript 表达式
AJAX
async javascript and xml 异步的 js 和 xml,此处的异步指的是:局部刷新(对应的是全局刷新)
1. 服务器端如何渲染
当服务器接收到请求后:
- 找到对应的页面,获取到页面的代码
- 根据需求从数据库中获取到需要动态展示的数据
- 把页面和数据混合在一起进行渲染,生成有结构有内容的完整页面
- 把渲染完的页面返回给客户端
2. 客户端如何渲染
当服务器接收到请求后,把需要的页面代码返回给客户端,而第一次渲染完只有结构和样式没有数据,服务器获取到数据请求后,把数据找到,返回给客户端,然后客户端把获取的数据展示在页面中,这就是异步的 js 区域页面局部刷新。
3. 服务器渲染的的特点
- 我们看到的内容都是在服务器端渲染的(JSP / PHP / ASP / ASP.NET / NODE…)客户端只是把所有渲染好的内容呈现在页面中而已,然而我们第一次渲染完,页面中的某部分数据需要更新了,我们需要让服务器整体重新的渲染一次(包含最新的数据),把最新的页面返回给客户端,客户端只能整体刷新页面展示最新的内容 -> 全局刷新性能和体验等都非常的差,而且服务器压力也很大
- 如果服务器性能比较高,页面呈现出来的速度会快一些,因为只要从服务器拿到内容,一切信息都已经准备好了
- 由于内容在服务器端就已经渲染好了,所以页面渲染完成后,在页面的源代码中都可以看到内容,有利于 SEO 搜索引擎优化
4.客户端渲染数据内容特点
- 可以实现页面中内容局部刷新,而且渲染的操作交给客户端来做 -> 体验好,减轻了服务器的压力
- 而且开始可以只把部分区域的数据获取到,滚动到某个区域后,再请求对应的数据也可以,实现数据的分批异步加载,提高性能体验
- 由于客户端渲染的内容没有出现在页面的源代码中, 不利于 SEO 优化。
AJAX基础操作
- 创建 Ajax 实例
let xhr = XMLHttpRequest;
- 打开 url (配置发送请求的信息)
xhr.open('GET','/json/xxx.json','true')
第一个参数:HTTP 请求方式
第二个参数:URL 请求地址 (api 接口地址)
第三个参数:ASYNC:设置同步或者异步,默认true 是同步,false 是异步
第四个参数:USER-NAME:传递给服务器的用户名
第五个参数:USER-PASS:传递给服务器的密码
- 监听 Ajax 状态,获取服务器响应的内容
xhr.onredaystatechange = function () {
if (xhr.readyState === 4 && /^(2|3)\d{2}&/.test(xhr.status)) {
let result = xhr.responseText;
}
}
- 发送请求
xhr.send(null);
HTTP的请求方式
本质区别:GET 系列传递给服务器信息的方式一般采用问号传参,而 POST 系列传递给服务器信息的方式一般采用设置请求主体
- GET 传递给服务器的内容比 POST 少,因为 URL 有最长大小限制(IE 浏览器一般限制 2kb ,谷歌浏览器一般限制为 4 ~ 8kb,超过长度的部分自动被浏览器截取了)
xhr.open('GET', '/list ? name = zhufeng & year = 10 & xxx = xxx...')
xhr.send('....') // 请求主体中传递的内容理论上没有大小限制,但是真实项目中,为了保证传输的速度,我们会自己限制一些
- GET 会产生缓存(缓存不是自己可控制的):因为请求的地址(尤其是问好传递的信息一样),浏览器有时候会认为你要和上次请求的数据一样,拿的是上一次信息,这种缓存我们不期望有,我们期望的缓存是自己可控制的;所以真实项目中,如果一个地址,GET 请求多次,我们要去除这个缓存
// 解决办法设置随机数
xhr.open('GET', '/list ? name = zhufeng &_ = '+Math.random());
...
xhr.open('GET', '/list ? name = zhufeng &_ = '+Math.random());
// 向服务器传递数据的方式:通过向接口末尾添加问号传参的方式传递,如: aside.json?name=mabinpwd=123456
// 由于浏览器的 url 地址是有长度限制的,IE 的 url 一般不超过 2k,谷歌的一般在 8k,超过的部分会自动被截掉,所以 get 请求传递的数据很小
- GET 相比较 POST 来说不安全,GET 是基于问号传参传递给服务器内容,有一种技术叫做 URL 劫持,这样别人可以获取或者篡改传递的信息,而 POST 基于请求主体传递信息,不容易被劫持
GET系列请求:一般用于从服务器获取信息,给的少拿得多
• GET
• DELETE 一般应用于告诉服务器,从服务器上删除点东西
• HEAD 只想要获取响应头内容,告诉服务器响应主体内容不要了
• OPTIONS 试探性请求,发个请求给服务器,看看服务器能不能收到,能不能返回
• PUT 修改某些内容
• PATH 对 PUT 的补充,用于修改某些内容
GET 请求的参数 在控制台的 headers 最下面 QueryStringParameters
POST系列请求:一般用于给服务器推送信息,给的多拿得少
• POST
• PUT 和 DELETE 对应,一般是想让服务器把我传递的信息存储到服务器上(一般应用于文件和大型数据内容)
客户端怎么把信息传递给服务器?
• 问号传参:xhr.open(‘GET’,‘getdata ? xxx = xxx & xxx = xxx’)
• 设置请求头 :xhr.setRequestHeader([key],[value])
• 设置请求主体:xhr.send(请求主体信息)
POST 请求的参数在 headers 最下面的 RequestPayload
服务器怎么把信息返回给客户端?
• 通过响应头
• 通过响应主体(大部分信息都是基于响应主体返回)
- 请求时间
xhr.timeout = 10 设置 AJAX 的等待时间,超过这个事件算 AJAX 的延迟
xhr.ontimeout = function () {} 超过请求时间做的事
xhr.abort() 手动中断 AJAX 请求
xhr.withCredentials = true 在跨域请求中允许携带证书(携带 COOKIE) - AJAX状态码
xhr.redayState获取状态码
0: UNSEND 未发送,创建一个 xhr,初始状态是0
1: OPENED 已经打开,执行了 xhr.open
2: HEADERS_RECEIVED 响应头信息已经返回给客户端,发送请求后,服务器会依次返回响应头和响应主体的信息
3: LOADING 等待服务器返回响应内容
4: DONE 响应主体信息已经返回给客户端
jQuery中AJAX的应用
$.ajax() 基于原生 js 的 ajax 四步进行封装
- $.ajax([URL],[OPTIONS])
- $.ajax([OPTIONS]) URL在配置项中(推荐)
- $.get/post/getJSON/getScript()
promise
ES6 语法规范中新加的内置类,用来处理 js 中异步编程的,而我们所谓的 Promise 设计模式,就是基于 promise 对异步操作进行管理
promose 是一个内置类,所以创建一个 promise:new Promise([executor]): 第一个执行函数必须传递,这里的 executor 是一个回调函数下面简称 exe
- new promise 的时候就会把 exe 执行,创建 promise 的一个实例(exe 是 promise 类的一个回调函数,promise 内部会把它执行)
- promise 不仅把 exe 执行,而且还给 exe 传递两个参数(两个参数也是函数类型)
resolve 函数:它执行代表 promise 处理的异步事情是成功的,把 promise 的状态改为 fulfilled
reject 函数:它执行代表 promise 处理的异步事情是失败的,把 promise 的状态改为 rejected - exe 函数中放的就是当前要处理的异步操作事情
let promiseExamp = new Promise((resolve, reject) => {这里一般存放的都是我们即将要处理的异步任务,任务成功我们执行 resolve,任务失败我们执行 reject(当然写同步的也可以)}
then
- then: 设置成功或者失败后执行的方法(成功或者失败都可以设置,也可以只设置一个)
pro.then ([success],[error])
pro.then ([success],null)
pro.then (null,[error]) - catch: 设置失败后执行的方法
- finally: 设置不论成功还是失败都会执行的方法(一般不用)
new Promise((resolve, reject) => {
// resolve(100); // 把第一个 promise 实例的 value 值改为100 / -100
reject(-100);
}).then(result => {
console.log(result);
return result * 10; // then 中 return 的结果相当于把当前这个新的 promise 实例中的 value 值改为返回值
}, err => {
console.log(err);
return err / 10;
}).then(A => {
console.log('A:' + A);
}, B => {
console.log('B:' + B);
}).then(C => {
}, D => {
});
then 链
1.执行 then / catch / finally 返回的结果是一个全新的 promise 实例,所以可以链式写下去;下一个 then 中哪个方式会被执行,由上一个 then 中某个方法执行的结果来决定
2.上一个 then 中某个方法的返回值会传递给下一个 then 的某个方法中
3.如果当前 promise 实例的状态确定后,都会到对应的 then 中找方法,如果 then 中没有对应的这个方法,则会向下顺延
4.then 方法中如果返回的是一个 promise 实例,则当前返回实例的成功或者失败状态,影响着下一个 then 中哪个方法会被触发执行;如果返回的是非 promise 实例,则看当前方法执行是否报错,来决定下一个 then 中哪个方法执行;
promise.all
- Promise.all([promise1, promise2,…]):all 中存放的是多个 promise 实例(每一个实例管理者一个异步操作),执行 all 方法返回的结果是一个新的 promise 实例 “PROA”
- 当所有 promise 实例的状态都为 Fulfilled 的时候(成功),让 PROA 的状态也变为 Fulfilled,并且把所有 promise 成功获取的结果,存储为成为一个数组(顺序和最开始编写的顺序一致)“result=[result1,result2,…]”,让 PROA 这个数组的 value 值等于这个数组
- 都成功(PROA 状态是 fulfilled)才会通知 then 中第一个方法执行,只要有一个失败(PROA 状态是 recected),就会通知 then 中第二个方法或者 catch 中的方法执行
axios
axios:一款基于 promise 设计模式封装的 AJAX 库(JQ 中的 AJAX 就是最普通的 AJAX 库,没有基于 promise 管理)
axios.post([URL], [DATA], [OPTIONS]):DATA 通过请求主传递给服务器的内容
- options
options 的参数 - baseURL:基础的URL路径
- transformRequest:处理请求参数(对POST系列有作用)
- transformResponse:把返回的结果进行处理
- params:GET 系列请求传递给服务器的内容(会把 PARAMS 中的内容拼接为 X-WWW-FORM-URLENCODED 这种格式,基于 URL 问号传参传递给服务器)
- paramsSerializer:传递参数的序列化
- timeout:超时时间
- withCredentials:跨域请求中是否允许携带凭证
- responseType:预设服务器返回结果的格式,默认是 JSON,支持 BUFFER / TEXT / STREAM / DOCUMENT…
- validateStatus:AXIOS 本身只有在 HTTP 状态码以2开头的时候才认为是成功,其余都认为是失败状态,当然我们可以自己来设置,基于 validateStatus 这个来修改
// 执行 axios.xxx() 都会返回一个 promise 实例,AJAX 请求成功会把实例的状态改为 FULFILLED,请求失败状态改为 REJECTED;并且获取的结果或者错误原因作为 promise 的 value
axios.get(‘http://127.0.0.1:5500/json/data2.json’, {
headers: {
AAA: encodeURIComponent(‘珠峰哈哈哈’)
},
params: {
lx: 1,
from: ‘WX’
}
}).then(result => {
// result:从服务器获取的结果
return result.data;
}).catch(reason => {
console.log(reason);
throw new Error(reason);
}).then(data => {
// data:从服务器获取的响应主体内容
// CONFIG:我们自己配置的选项信息
// DATA:存储的是响应主体内
// HEADERS:存储响应头的信息
// REQUEST:AJAX实例
// STATUS:响应状态码
// STATUS-TEXT:状态码的描述
console.log(data);
}); - 如何使用axios
在使用 AXIOS 之前,我们一般都需要配置默认的配置项 - 基础 URL,后期再发送请求的时候,URL 请求地址最前面的公共部分就不需要再写了
axios.defaults.baseURL = "http://127.0.0.1:5500";
- 跨域请求中允许携带资源凭证(例如 COOKIE 信息)
axios.defaults.withCredentials = true;
- 设置请求头:POST 系列中,我们传递给服务器数据的格式一般以 x-www-form-urlencoded 格式为主
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
- 设置请求拦截器(只对 POST 系列有用):把基于请求主体传递给服务器的内容进行拦截,把内容格式变为 x-www-form-urlencoded 这种格式,再传递给服务器
axios.defaults.transformRequest = function (data) {
if (!data) return data;
let str = ``;
for (let key in data) {
if (!data.hasOwnProperty(key)) break;
str += `&${key}=${data[key]}`;
}
return str.substring(1);
};
- 设置响应拦截器:[成功状态]把从服务器获取的结果中的响应主体信息获取到即可,[失败状态]手动把错误信息抛出异常
axios.interceptors.response.use(function (response) {
return response.data;
}, function (error) {
throw new Error(error);
});
- 配置什么才算成功(把 PROMISE 状态改为 FULFILLED)
axios.defaults.axios.defaults.validateStatus = function (status) {
return /^(2|3)\d{2}$/.test(status);
}headers['Content-Type'] = 'application/x-www-form-urlencoded';
// Promise.all
let promise1 = Promise.resolve(100);
let promise2 = Promise.resolve(200);
axios.all([promise1, promise2]).then(results => {
let [val1, val2] = results;
console.log(val1, val2);
});
/*
axios.all([promise1, promise2]).then(axios.spread(function (val1, val2) {
// axios.spread:把基于 axios.all 获取的结果一项项的单独获取到
console.log(val1, val2);
*/
跨域
同源策略(Same-origin Policy):为了保证浏览器的信息安全,浏览器采用同源策略,保证当前源中的资源只能在当前的源中使用;其他源如果需要使用当前源资源,需要特殊技术,这种 A 源访问 B 源的资源的通信称为跨域;
同源策略要求通信的两个源的协议、域名、端口号要相同,如果三者中任意一个不同就是不满足同源策略;不满足同源策略的通信就是跨域;
常用的跨域解决方案:
- JSONP
- 服务端转发,因为同源策略只在客户端存在,在服务端是不存在的;所以可以由服务端转发请求;
- nginx 转发,nginx 是服务器应用程序,它可以接受客户端的请求,然后根据规则可以配置自动转发;
- CORS: Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;
JSONP 是一种常用的解决跨域的方式;
原理:利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据
5. 提前声明一个叫做 fn 的函数,给 fn 设置一个形参;
6. 在页面给 script 的 src 的指向的路径拼接一个 callback 属性,callback=fn;当浏览器解析到这个 script 标签时,会向 src 指向的路径发起 http 请求;
7. 服务器收到这个请求后,会返回一个 fn (这里面是服务器返回的数据)
8. fn({xxx}) 这个是让 fn 执行,小括号里面就是服务器发送给我们的数据
宏任务和微任务
宏任务和微任务是等待任务队列中的异步任务的处理机制,浏览器的任务队列中,主任务队列存储的都是同步任务,等待任务队列中存储的都是异步任务。
微任务:
- Promise 的 then 回调函数
- async 函数 await 下面的代码
- process.nextTick
宏任务:定时器(setInterval 和 setTimeout)
首先浏览器会把主动任务队列中的同步任务挨个全部执行完,然后再去等待任务队列中看那个任务可以执行了,然后把该执行的任务放到主任务队列中去执行,等这个任务执行完,再去等待任务队列中看谁可以执行了,再把这个任务放到主任务对了中去执行… 如此循环。这种循环叫做事件循环(Event Loop)
异步任务都是谁先到达条件谁先执行,但是谁先到达条件也有优先级的问题,这个优先级要看这个任务与是宏任务还是微任务,微任务的优先级高于宏任务。
async 和 await
async 和 await 是 es6 新增的关键字,用于把异步变成同步
- async
async 在函数定义时使用,用 async 定义的函数默认返回一个 Promise 实例,可以直接 .then
async function fx () {
console.log(1);
}
let obj = {
async getName () {
// async 还可以定义对象的方法
}
};
fx().then(() => {
// 因为 async 函数返回了 Promise 实例,所以可以 .then
})
function g() {
return ‘abc’;
}
function h() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(‘xyz’);
},300)
})
} - await (要和 async 一起使用)
async function fn() {
// await 等待,等右侧的代码执行完
// await 用法:
// let x = await g();
// console.log(x);
// 1. 如果 await 右侧是同步的代码,就会让同步代码执行,如果执行的是一个函数,还会把函数的返回值给到 await 左边的变量
let y = await h() {
console.log(y);
console.log('123');
}
// h().then((res) => console.log(res)) 有了 async 和 await,就不用写 then 了;因为 await 可以取得 promise resolve 时传入的值
// 2. 如果 await 右侧是一个 Promise 实例,或者一个方法返回了Promise 实例,await 会等着 Promise 的实例 resolve,并且在实例resolve 之前,await 后面的代码不执行;并且还会拿到 Promise 在 resolve 时传入的值,并且赋值给等号左侧变量;
// 3. await 会把 await 下面的代码变成微任务;
}
fn();
真实项目中,async 和 await 常常结合 AJAX 和 Promise 一起使用