JS知识体系梳理-7

975 阅读21分钟

基础概念

内网IP(局域网)

在一个区域内,大家连接的是同一个网络(准确来说:连接同一个wifi不一定是同一个网络,连接不同的wifi也可能是相同的网络,一切都看路由交换机的配置),这就是局域网

在同一个局域网下,成员可以互相访问(你的电脑连接了A网络,手机也连接了A网络,那么手机可以访问电脑上的一些信息=>这样可以做移动端开发时候的手机联调,推荐anywhere)

HBuilder也提供了联调的功能(代码上加断点,手机访问,程序会走断点)

外网IP

任何用户通过这个IP地址都可以访问到对应的服务器

域名DNS解析

把域名和服务器关联在一起,解析时需要填写服务器的外网IP

DNS系统:万维网上作为域名和IP地址相互映射的一个分布式数据库,域名解析成功后,会在DNS系统中记录一条信息"www.baudu.com(域名) 192.23.42.01(服务器外网IP地址)",将域名和服务器关联在一起,保证以后访问域名,可以直接找到外网IP,通过外网IP访问到服务器

FTP

把本地代码传送到服务器上的的工具,工作中一般都是基于FTP(FileZilla)上传的

服务器上是不允许安装除了开发需要的环境以外的任何东西(保持服务器的干净)

web发布工具

用来在服务器上进行项目发布的工具,将域名与服务器上的项目关联在一起,指定当前域名访问服务器后,到底执行的是哪个项目的源代码

常用web发布工具有:IIS(c#..->windows)、APACHE TOMCAT(PHP/JAVA...->LINUX)、NGINX

服务器

服务器就是一台高性能的电脑,每一个服务器都对应一个外网的IP地址,而且每个服务器有自己的用户名和密码

服务器端

所有可以接收客户端的请求,并且给其响应一些内容的都是服务器,服务器其实就是一台性能较高的电脑

客户端

所有可以向服务器发送请求的一端都是客户端

问题:

打开一个浏览器,在地址栏输入一个网址,按下enter键,到看到整个页面,中间都经理了哪些事情?

HTTP请求阶段:向服务器发送请求

  • 1、浏览器首先向DNS域名解析服务器发送请求
  • 2、DNS反解析:根据浏览器请求地址中的域名,到DNS服务器中找到对应的服务器外网IP地址
  • 3、通过找到的外网IP,向对应的服务器发送请求(首先访问的是服务器的WEB站点管理工具:准确来说是我们先基于工具在服务器上创建很多服务,当有客户端访问的时候,服务器会匹配出具体是请求哪个服务)
  • 4、通过URL地址中携带的端口号找到服务器上对应的服务,以及服务所管理的项目源文件

HTTP响应阶段:服务器把客户端需要的内容准备好,并且返回给客户端

  • 5、服务器端根据请求地址中的路径名称、问号传参或者哈希值,把客户端需要的内容进行准备和处理
  • 6、把准备的内容响应给客户端(如果请求的是HTML或者CSS等这样的资源文件,服务器返回的是资源文件中的源代码,不是文件本身(音视频和图片除外,这些使用的是文件流传输))

浏览器渲染阶段

  • 7、客户端浏览器接收到服务器返回的源代码,基于自己内部的渲染引擎(内核)开始进行页面的绘制和渲染
    • 首先计算DOM结构、生成DOM TREE
    • 自上而下运行代码,加载CSS等资源内容
    • 根据获取的CSS生成带样式的RENDER TREE
    • 开始渲染和绘制

我们把一次完整的请求+响应称之为"HTTP事务",事务就是完整的一次操作,请求和响应缺一不可

一个页面完全加载完成,需要向服务器发起很多次HTTP事务操作,一般来说首先把HTML源代码拿回来解析,加载HTML的时候运动link,script,img中的src,iframe,video,audio[没有设置preload="none"]....都会重新和服务器端建立HTTP事务交互

特殊情况:如果我们做了资源缓存处理(304),而且即将加载的资源在之前已经加载过了,这样的操作和传统的HTTP事务有所不一样,他们是从服务器和浏览器的缓存中读取数据,比传统的读取快很多

在客户端向服务器发送请求,以及服务器把内容响应给客户端的时候,中间相互传递了很多内容(客户端把一些内容传递服务器,服务器把一些内容响应给客户端),我们把传递的内容统称为"HTTP报文"

前端性能优化

1、减少HTTP请求次数及请求内容的大小,例如将所有的css都放在一个文件里,将所有的script放在一个文件里,图片的懒加载

URL的组成

URI=URL+URI

URI:统一资源标示符

URL:统一资源定位符

URN:统一资源名称


www.zhufengpeixun.cn:80/stu/index.h…

传输协议

用来传输客户端和服务器端交互的信息的(类似于快递小哥)

传输协议的类型

HTTP:超文本传输协议(除了传递普通的文本,还可以传递文件流或者进制编码等信息),是目前最常用的WEB传输协议

HTTPS:基于SSL(Secure Sockets Layer 安全套接层)加密的HTTP传输协议,比HTTP更加的安全(涉及支付的网站,一般都是基于HTTPS完成的)

FTP:文件传输协议,一般用来实现资源文件在服务器上的上传下载

域名(Domain Name)

> 一级域名(顶级域名)qq.com
> 二级域名  sports.qq.com
> 三级域名  kbs.sports.qq.com
>qq.com是域名,买域名的时候只是买了qq.com,对应的二级域名和三级域名是自己分的

`.com`:供商用的国际域名
`.cn`:供商用的中文域名
`.net`:用于网络供应服务商(系统类的经常使用net域名)
`.org`:用于官方组织
`.edu`:用于教育院校
`.gov`:用于政府机构

端口号

用来区分同一台服务器上不同服务的标识(基于web服务管理器创建服务的时候可以指定),不同服务之间一般是不能使用相同的端口号的

`HTTP`:默认端口号80
`HTTPS`:默认端口号443
`FTP`:默认端口号21

如果当前网站的服务,采用的是协议对应的默认端口管理,那么当用户输入网址的时候可以不指定端口号,浏览器会默认把默认的端口传递给服务器,但是使用web发布工具发布服务的时候要写端口号

一台服务器上的端口号范围:0~65535之间

webstorm预览页面:WS把自己的电脑当做服务器,在服务器上创建一个服务,端口号是63342,自己电脑上的浏览器预览自己电脑上的服务,属于本机服务请求,用localhost(127.0.0.1)本地域名即可

http://localhost:63342/JS/第六周/新浪App 自写/index.html?_ijt=361p9s43qdn13nvv9trnnu6u7n

服务器上安装一款应用都可能会作为一个服务,占用一个端口号,所以一般情况下服务器除了开发环境下要用的东西,其他都不下载的

请求路径名称

path pathname

例如:/stu/index.html 一般都是请求当前服务对应的项目目录中,STU文件夹中的INDEX.HTML页面。

也存在特殊情况,就是当前的URL是被"伪URL重写"的,我们看到的URL请求其实不是真实的请求,例如 item.jd.com/4679424.htm… 这个URL就是被重写的,它的真实URL地址很可能是http://item.jd.com/detail.jsp?id=4679424,其实就是跳转到详情页,通过问号传递不同的产品编号,展示不同的产品详情信息,但是.jsp这种服务器渲染的动态页面不能被搜索引擎收录,不利于页面的SEO,所以我们会把动态页面静态化,这也就用到了URL重写技术

DHTML

动态页面,泛指当前页面中的内容不是写死的而是动态绑定的,例如.jsp/.php/.aspx...这些页面的数据都是基于AJAX或者是后台编程语言处理,由服务器端渲染,最后把渲染后的结果返回给客户端呈现的

例如:/stu/info 这种没有任何后缀信息,一般都不是用来请求资源文件的,而是用于AJAX数据请求的接口地址(如果后缀是.json类的,也是同理),但是有一种除外 /stu/info/ 这种的很可能不是接口地址,而是没有指定请求的资源名称,服务器会请求默认的资源文件,一般都是index.html/default.html

问号传参及哈希值

?xxx=xxx&...#xxx

在HTTP事务中,问号传参是客户端把信息传递给服务器的一种方式(也有可能是跳转到某一个页面,把参数值传递给页面用来标识的)

哈希值一般都跟客户端服务器交互没啥关系,主要用于页面中的锚点定位和哈希路由切换

HTTP报文

起始行

  • 请求起始行:描述当前请求的一些基本信息
  • 响应起始行:HTTP状态码

首部(头)

  • Request Headers 请求头 [客户端设置,服务器接收]
GET /?ref=qipaoxian HTTP/1.1 =>起始行(描述当前请求的一些基本信息 用的是1.1版本传输协议进行内容传输的)
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: ... //=>cookie信息一般都是放到头文件中实现和服务器端的数据通信的
  • Response Headers 响应头 [服务器端设置,客户端获取]
HTTP/1.1 200 OK //=>响应起始行(HTTP状态码)
Date: Tue, 22 May 2018 09:20:51 GMT //=>服务器响应内容时候的"服务器端时间"(客户端获取这个时间的时候已经和真实的时间产生误差了,因为服务器返回内容到客户端接收到,也是需要时间的),并且这个时间是格林尼治时间(比北京时间慢8个小时,北京时间是GMT+0800)
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Last-Modified: Sun, 06 May 2018 10:02:42 GMT
Vary: Accept-Encoding,User-Agent
Server: yunjiasu-nginx //=>管理WEB服务的工具
CF-RAY: 41ee358b1165b1ee-HKG
Content-Encoding: gzip
  • General Headers通用头
Request URL: http://www.baudu.com/?  //=>请求地址ref=qipaoxian
Request Method:  //=>请求方式:GET/POST/DELETE/PUT/HEAD/OPTION
Status Code: 200 OK  //=>响应的HTTP状态码
Remote Address: 162.159.210.54:80  主机地址(服务器外网IP地址)
Referrer Policy: no-referrer-when-downgrade

主体

  • Requeset Payload / Form Data 求主体
//=>客户端传递给服务器的内容
  • Response 响应主体
//=>服务器返回的是什么就是什么,如HTML的源码

工作开发和BUG调试

A:打开控制台,在NET-WORK中找到当前交互的请求地址,点击进去看详情

B:如果是传递给服务器的参数或者方式错误 [前端问题]

C:如果服务器返回的信息有错误或者和API接口文档规定的内容不一样 [后台问题]

D:如果返回数据是对的,但是展示有问题 [前端问题]

确实是自己前端的问题后,基于断点(或者代码中的debugger)或者控制台输出等方式,开始逐步调试即可

客户端和服务器端信息交互的方式

客户端传递给服务器

问号传参

请求的URL地址末尾通过问号传参方式,把一些信息传给服务器 /stu/info?id=12&lx=man

设置请求头

客户端把需要传递给服务器的内容设置到请求头信息中(自定义请求头),服务器可以通过接收请求头信息把内容得到

设置请求主体

xhr.send([AJAX SEND中传递的内容,就是客户端设置的请求主体内容,服务器端可以接受到这些信息的])

服务器返回客户端

设置响应头信息

例如把服务器端时间通过响应头返回给客户端,客户端通过获取响应头信息得到这个时间(响应头返回的速度是优先于响应主体的) 不管HTTP请求是否发送成功(status的值),都会有响应头,即使是404;

设置响应主体

主要的返回信息都在响应主体中

AJAX

async javascript and xml 异步的JS和XML

在AJAX中的异步不是我们理解的同步异步编程,而是泛指"局部刷新",但是我们在以后的AJAX请求中尽可能使用异步获取数据(因为异步数据获取不会阻塞下面代码的执行)

XML是一种文件格式(我们可以把HTML理解为XML的一种):可扩展的标记语言,它的作用是用自己扩展的一些语义标签来存储一些数据和内容,这样存储的好处是清晰的展示出数据的结构

<?xml version="1.0" encoding="utf-8"?>
<root>
    <student>
        <name>张三</name>
        <age>25</age>
        <score>
            <english>98</english>
            <chinese>100</chinese>
            <math>100</math>
        </score>
    </student>
    <student>
        <name>李四</name>
        <age>24</age>
        <score>
            <english>8</english>
            <chinese>90</chinese>
            <math>80</math>
        </score>
    </student>
</root>

AJAX刚刚兴起的时候,客户端从服务器获取数据,服务器为了清晰的表达数据结构,都是返回XML格式的内容,当下,我们获取的数据一般都是JSON格式的内容,JSON相对于XML来说,也能清晰表达数据结构,而且访问里面数据的时候操作起来比XML更简便(但是现在某些项目中,服务器返回客户端的数据不单纯是数据,而是数据和需要展示的结构拼接好的结果(类似于我们自己做的字符串拼接),换句话说,是服务器端把数据和结构拼接好返回给我们,此时返回的数据格式一般都是XML格式的字符串)

前后端项目交互形态

1、:基于AJAX向服务器发送请求获取数据(不管是JSON还是服务器渲染好的XML文档)都是前后端分离的项目

  • 不利于SEO优化(源代码中看不到动态增加的数据)
  • 都可以实现"局部刷新"

例如:京东首屏数据是基于AJAX从服务器端获取的XML字符串(服务器端渲染),其它屏数据是从服务器获取JSON,客户端拼接字符串插入到指定区域中(客户端渲染)

2、不基于AJAX,直接通过浏览器向服务器发送请求,服务器把需要呈现的内容返回,页面由服务器端进行渲染,这种模式是非前后端分离的项目

  • A:有利于SEO优化
  • B:只能实现"全局刷新"
  • C:请求页面的后缀名一般都不是.html,而是.php/.jsp/.aspx/.asp...,也有.html,这个是基于node做后台实现服务器渲染的(注意原始地址可能被重新修改"URL重写")

服务器端渲染的好处

  • 1、有利于SEO优化(服务器渲染好的内容到客户端呈现,在页面源代码中可以看到绑定的内容,有利于引擎的收录),但是客户端做字符串拼接,呈现出来的内容在页面源代码中是没有的,不利于SEO优化

  • 2、只要服务器端抗压能力强,使用服务器端渲染的方式页面加载速度会比客户端渲染更快 很多大网站(例如:京东、淘宝)首屏内容都是基于服务器端渲染的,客户端获取XML数据后直接呈现,增加页面第一次打开速度,而剩下屏中的内容都是基于AJAX获取数据,在客户端进行数据拼接渲染的

AJAX操作

一、创建一个XMLHttpRequest的实例

let xhr=new XMLHttpRequest();

//=>IE6中是不兼容的,使用的是new ActiveXObject来实现的

let xhr=new ActiveXObject();

二、打开请求

//=>打开请求:发送请求之前的一些配置项
xhr.open([HTTP&emsp;METHOD],[URL],[ASYNC],[USER-NAME],[USER-PASS]);

URL

向服务器端发送请求的API(Application Programming Interface)接口地址

ASYNC

设置AJAX请求的同步异步,默认是异步(true为异步),false是同步,项目中都使用异步编程,防止阻塞后续代码执行

USER-NAME/USER-PASS

用户名密码,一般不用

HTTP的请求方式

GET:从服务器端获取数据(给的少拿的多)

POST:向服务器端推送数据(给的多拿的少)

DELETE:删除服务器端的某些内容(一般是删除一些文件)

HEAD:只想获取服务器返回的响应头信息,不要响应主体中的内容

PUT:向服务器上存放一些内容(一般也是存放文件)(post系列)

OPTIONS:一般都是使用它向服务器发送一个探测性请求,如果服务器端返回了信息,说明当前客户端和服务器端建立了连接,我们可以继续执行其它请求了(TRACE是干这件事的,但是axios这个AJAX类库在基于cross domain进行跨域请求的时候,就是先发送options请求进行探测尝试,如果能连接服务器,才会继续发送其它的请求)

TRACE

CONNECT

所有的请求都可以给服务器端传递内容,也都可以从服务器端获取内容

GET 和 POST的区别

1、:传递给服务器信息的方式不一样

  • GET是基于URL地址问号传参的方式把信息传递给服务器
xhr.open("GET","/temp/list?xxx=xxx&xxx=xxx")
  • POST是基于请求主体把信息传递给服务器,基本上客户端传递给服务器的信息也是字符串
let xhr = new XMLHttpRequest();
xhr.open('POST', 'https://www.easy-mock.com/mock/5b0412beda8a195fb0978627/temp/list');
    xhr.onreadystatechange = () => {
    if(!/^(2|3)\d{2}$/.test(xhr.status)) return;//=>证明服务器已经返回内容了(HTTPqing)
        if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(JSON.parse(xhr.responseText));
        }
    };
xhr.send(JSON.stringify({id:1000,lx:2000}));//=>请求主体中传递给服务器的是JSON格式的字符串,但是真实项目中常用的是URL-ENCODE格式的字符串"id=1000&lx=2000"(为了保证和GET的格式一样,都是用的问号传参的形式,只是一个在url里,一个在send里);

GET一般应用于拿(给服务器的会少一些),而POST给服务器的很多,如果POST是基于问号传参方式搞会出现一些问题:URL会拼接很长,浏览器对于URL的长度有最大限度(谷歌8KB 火狐7KB IE2KB ...),超出的部分浏览器就把它截掉了,所以GET请求可以基于URL传参,而POST都是使用请求主体传递(请求主体理论上是没有限制的,真实项目中我们会自己做大小限制,防止上传过大信息,导致请求迟迟完不成)

2、:GET不安全,POST相对安全

  • 因为GET是基于"问号传参"把信息传递给服务器的,容易被骇客进行URL劫持
  • POST是基于请求主体传递的,相对来说不好被劫持,所以登录、注册等涉及安全性的交互操作,我们都应该用POST请求;

3、:GET会产生不可控制的缓存,POST不会

不可控:不是想要就要,想不要就不要的,这是浏览器自主记忆的缓存,我们无法基于JS控制,真实项目中我们把这个缓存干掉

GET请求产生缓存是因为:连续多次向相同的地址(并且传递的参数信息也是相同的)发送请求,浏览器会把之前获取的数据从缓存中拿到返回,导致无法获取服务器最新的数据(POST不会)

解决方案

	xhr.open("GET",`/temp/lsit?lx=1000&_=${Math.random()}`);

//=>保证每次请求的地址不完全一致:在每一次请求的末尾追加一个随机数即可(使用_作为属性名就是不想和其他的属性名冲突)

案例 基于JS完成一个AJAX请求,要求每间隔10秒钟就重新从服务器端获取最新的数据展示在页面中

 获取所有成交客户信息
      接口地址:/custom/list
      请求方式:GET
      传递标识:type = chengjiao
      返回结果:[{
         id:1,
         name:'张三'
      },...]
-----------------------
let queryData=function(){
	let xhr=new XMLHttpRequest();
	xhr.open("get",`/custom/list?type=chengjiao&_=${Math.random()}`,true);
	xhr.onreadystatechange=function(){
		if(xhr.readyState===4 && xhr.status===200){
			//=>响应主体内容已经返回客户端,并且请求发送成功
			let data=JOSN.parse(xhr.responseText);
		}
	}
	xhr.send(null);
}
let autoTimer=setInterval(queryData,10000);

三、事件监听

一般监听的都是readystatechange事件(AJAX状态改变事件),只要AJAX的状态发生改变就会触发这个事件,基于这个事件可以获取服务器返回的响应头响应主体内容,响应头返回的速度优先于响应主体的

xhr.onreadystatechange=()=>{
    if(xhr.readyState===4 && xhr.status===200){
       xhr.responseText;
    }
};
AJAX状态
`0`:UNSENT  刚开始创建XHR,还没有发送
`1`:OPENED 已经执行了OPEN这个操作
`2`:HEADERS_RECEIVED 已经发送AJAX请求(AJAX任务开始),响应头信息已经被客户端接收了(响应头中包含了:服务器的时间、返回的HTTP状态码...)
`3`:LOADING 响应主体内容正在返回
`4`:DONE 响应主体内容已经被客户端接收

不管请求是成功或者是失败(HTTP网络状态码),AJAX的四个步骤,也就是全部的状态码是要走一遍的

status(HTTP网络状态码)

根据状态码能够清楚的反映出当前交互的结果及原因

200: ok,成功(只能证明服务器成功返回信息了,但是信息不一定是你业务需要的)

301:Moved Permanently 永久转移

  • 域名更改,访问原始域名重定向到新的域名,例如京东原域名是www.360buy.com,如果现在输入这个域名也会直接跳到现有的域名www.jd.com

302:Move temporarily 临时转移

  • 一般用作服务器负载均衡:当一台服务器达到最大并发数的时候,会把后续访问的用户临时转移到其它的服务器机组上处理
  • 偶尔真实项目中会把所有的图片放到单独的服务器上"图片处理服务器",这样减少主服务器的压力,当用户向主服务器访问图片的时候,主服务器都把它转移到图片服务器上处理

307:临时重定向

  • 网站现在是基于HTTPS协议运作的,如果用户访问的是HTTP协议,会基于307重定向到HTTPS协议上

304:Not Modified 设置缓存

  • 对于不经常更新的资源文件,例如CSS/JS/HTML/IMG等,服务器会结合客户端设置304缓存,第一次加载过这些资源就缓存到客户端了,下次再获取的时候,是从缓存中获取;如果资源更新了,服务器端会通过最后修改时间来强制让客户端从服务器重新拉取;基于CTRL+F5强制刷新页面,304做的缓存就没有用了

400:Bad Request 请求参数错误

401:Unauthorized 无权限访问

404:NOT Found 找不到资源(地址不存在)

413:Request Entity Too Large 和服务器交互的内容资源超过服务器最大限制

500:Internal Server Error 未知的服务器错误

503:Service Unavailable 服务器超负荷

四、发送AJAX请求

从这步开始,当前AJAX任务开始,如果AJAX是同步的,后续代码不会执行,要等到AJAX状态成功后再执行,反之异步不会

xhr.send([请求主体内容])//=>从这一步开始AJAX任务刚刚开始

案例题

let xhr=new XMLHttpRequest();
xhr.open('GET','/temp/list',true);//=>readystate===1
xhr.onreadystatechange=()=>{
   if(xhr.readyState===2){console.log(1);}
   if(xhr.readyState===4){console.log(2);}
};
xhr.send();//=>AJAX任务开始,异步不等直接往后执行,所以先输出3,readystate的值发生变化就会触发onreadystatechange事件,当从1变为2的时候给事件绑定的方法执行,输出1,从2变到3方法也被执行,但没有对应的输出,从3变到4,事件被触发方法又被执行,输出2
console.log(3);
//=>输出3,1,2
let xhr=new XMLHttpRequest();
xhr.open('GET','/temp/list',true);//=>readystate===1
xhr.send();//=>AJAX任务开始,异步不等直接往后执行,给xhr绑定了onreadystatechange事件,事件也是异步的,不等往下执行,输出3,readystate的值发生变化就会触发onreadystatechange事件,当从1变为2的时候给事件绑定的方法执行,输出1,从2变到3方法也被执行,但没有对应的输出,从3变到4,事件被触发方法又被执行,输出2
xhr.onreadystatechange=()=>{
   if(xhr.readyState===2){console.log(1);}
   if(xhr.readyState===4){console.log(2);}
};
console.log(3);
//=>输出3,1,2
let xhr=new XMLHttpRequest();
xhr.open('GET','/temp/list',false);//=>readystate===1,同步
xhr.onreadystatechange=()=>{//=>事件绑定异步,放到等待任务队列
   if(xhr.readyState===2){console.log(1);}
   if(xhr.readyState===4){console.log(2);}
};
xhr.send();//=>AJAX任务开始,同步,要等到AJAX任务完成才能往下走,也就是当readystate的值变为4的时候才能往下走,其实当readystate值发生变化的时候onreadystatechange事件都被触发,只是因为主任务队列被AJAX任务占着,所以不能拿过来执行,直到readystate变为4的时候,才能把onreadystatechange事件触发的方法拿到主任务队列里执行,输出2,整个AJAX任务完成再输出主任务队列中的3
console.log(3);
//=>输出2,3
let xhr=new XMLHttpRequest();
xhr.open('GET','/temp/list',true);//=>readystate===1,同步
xhr.send();//=>AJAX任务开始,因为是同步的,所以要等到AJAX任务全部完成了才能往下走,readystate变到4,AJAX任务结束,然后再绑定onreadystatechange事件,事件为异步,放到等待任务队列中,输出主任务队列中的3,此时readystate已经为4,不会再发生变化了,所以这个事件根本就不会被触发,最终只输出3
xhr.onreadystatechange=()=>{
   if(xhr.readyState===2){console.log(1);}
   if(xhr.readyState===4){console.log(2);}
};
console.log(3);
//=>输出3

关于xhr的属性和方法

`xhr.response`:响应主体内容
`xhr.responseText`:响应主体的内容是字符串
`xhr.responseXML`:响应主体的内容是XML文档对象
`xhr.status`:返回的HTTP状态码
`xhr.statusText`:状态码的描述
`xhr.timeout`:设置请求超时的时间
let xhr = new XMLHttpRequest();
    xhr.open('GET', 'temp.json');
    xhr.timeout=200;
    xhr.ontimeout=function(){
	    console.log("请求超时,请稍后重试");
    };
	xhr.onreadystatechange=function(){
		if(xhr.readystate===4 && xhr.status===200){
		
		}
	}
`xhr.withCredentials`:是否允许跨域(FALSE)
`xhr.abort()`:强制中断请求
`xhr.getAllResponseHeaders()`:获取所有响应头信息
`xhr.getResponseHeader([key])`:获取KEY对应的响应头信息,例如:xhr.getResponseHeader("data")就是在获取响应中的服务器时间,但是获取的结果是格林尼治时间,而且是字符串
xhr.onreadystatechange=function(){
	if(xhr.readyState===2){
	//=>响应头信息已经回来了
	let time=xhr.getResponseHeader("date");//=>格林尼治时间的字符串
	new Date(time);//=>把指定的时间字符串格式化为标准的北京时间,不再是字符串了,而是Date类的实例,也是标准的时间格式数据
	}
	if(xhr.readyState===4){
		console.log(xhr.responseText);
	}
};
xhr.send(null);
`xhr.open()`:打开URL请求
`xhr.overrideMimeType()`:重写MIME类型
`xhr.send()`:发送AJAX请求
`xhr.setRequestHeader()` :设置请求头

设置的请求头信息不能出现中文而且必须在open之后才可以设置成功

####仿jQuery重写AJAX方法

(function(){
	function AJAX(options){
		return new init(options);
	}
	let init=function init(options){
		let {
			url,
			method="get",
			data,
			dataType="json",
			async=true,
			cache=true,
			success,
			error
		}=options;
		["url","method","data","dataType","async","cache","success","error"].forEach((item)=>{
			this[item]=eval(item);
		});
		this.sendRequest();
	}
	AJAX.prototype={
		constructor:AJAX,
		sendRequest:function(){
			let queryData=function(){
				this.handleData();
				this.handleMethod();
				this.handleCache();
				return new Promise(()=>{
					let {url,method,async,data}=this
					let xhr=new XMLHttpRequest();
					xhr.open(url,method,async);
					xhr.onreadystatechange=function(){
						if(xhr.readystate===4){
							if(/^(2|3)\d{2}$/.test(xhr.status)){
								let result=this.handleDataType(xhr);
								success && success(result);
							}else{
								error && error(xhr,xhr.statusText);
							}
						}
					}
				})
			}
			queryData();
		},
		handleData:function(){
			let str=``;
			if(typeof data==="object"){
				for(let key in data){
					if(!data.hasOwnProperty(key)) return;
					str+=`${key}=${data[key]}&`;
				}
				this.data=str.substring(0,str.length-1);
			}
		},
		handleMethod:function(){
			let {url,data,method}=this,
				reg=/^(get|head|options|trace|connect)$/i;
			if(reg.test(method)){
				url.indexOf("?")>0?url+=`&${data}`:url+=`?${data}`;
				this.url=url;
				this.data=null;
			}
		},
		handleCache:function(){
			let {url,cache}=this;
			if(/^get$/i && cache===false){
				url.indexOf("?")>0?url+=`&_=${+(new Date())}`:url+=`?_=${+(new date())}`;
				this.url=url;
			}
		},
		handleDataType:function(xhr){
			let {dataType}=this,
				result=xhr.responseText;
			dataType=dataType.toUpperCase();
			switch (dataType){
				case:"json":
				result=JSON.parse(result);
				break;
				case:"text":
				break;
				case:"xml":
				result=xhr.responseXML;
				break;	
			}
			return result;
		}
	}
	init.prototyoe=AJAX.prototype;
	window.ajax=AjAX;
})()

axios

定义:axios是一个基于Promise管理的AJAX类库,得到的结果是一个对象

 let promise = axios.get('A', {
        params: {
            lx: 12
        }
    });
 
 promise.then((result)=>{
	 console.log(result);
 })
`data`:从服务器获取的响应主体的内容
`headers`:从服务器获取的响应头的信息
`request`:创建AJAX的实例
`status`:状态码
`statusText`:状态码的描述
`config`:基于AXIOS发送请求的时候做的配置项

基于get或者post方法发请求,返回的结果都是promise实例,因此可以基于此来解决回调地域的问题

    axios.get('A', {
        params: {
            lx: 12
        }
    }).then(result => {
        return axios.post('b',{
            name:12,
            age:8
        });
    }).then(result => {
        console.log(result);
    });

支持单独对应的请求方法

基于get方式发送请求

问号传参的内容可以基于配置项中的params属性使用对象的方式传递,也可以直接基于URL后面拼接

 axios.get('https://www.easy-mock.com/mock/5b0412beda8a195fb0978627/temp/info?name=23', {
        params: {
            lx: 12
        }
    })

返回的headers中会有Query String Parameters

基于POST方式传参 参数为3个,第一个是url地址,第二个是请求主体(对象格式),第三个是其他配置项 请求主体传递给服务器的是RAW(JSON格式的字符串),不是X-WWW-FORM-URLENCODED

axios.post('a',{
                name:12,
                age:8
        });

返回的headers里面有Request Payload存储的是传递进去的请求主体

axios提供了all方法可以直接做到几个请求都完成了再做其他事情,并且一起得到请求的结果,以一个数组的形式返回,数组中的每一项对应着每个请求返回的对象

let sendAry = [
        axios.get('A'),
        axios.get('B'),
        axios.post('C')
    ];
axios.all(sendAry).then(result => {
        console.log(result);//=>result是一个数组
        let [resA, resB, resC] = result;
    });

对应的axios上有一个spread方法,可以将返回的结果结构成单独的结果,原理是基于JS中的柯理化函数,类似于bind方法的源码

let sendAry = [
        axios.get('A'),
        axios.get('B'),
        axios.post('C')
    ];
 axios.all(sendAry).then(axios.spread((resA,resB,resC)=>{
	 console.log(resA, resB, resC);//=>分别代表三次请求的结果
 }))
 --------
 //=>axios库中spread的源码
 module.exports = function spread(callback) {
        return function wrap(arr) {//=>arr其实是上面的result,也就是个数组,
            return callback.apply(null, arr);
        };
    };
//=>spread一执行,相当于返回了一个匿名函数,等到请求成功后,执行匿名函数,并把请求成功后得到的好几个请求的结果,也就是数组result传递给匿名函数,并且使用apply方法,将result中的每一项一个一个的传递给回调函数,把回调函数执行

axios中初始化常用配置项的方法

baseURL

设置baseURL,后期使用的时候url直接传递后面的路径

axios.defaults.baseURL="https://www.easy-mock.com/mock/5b0412beda8a195fb0978627/temp"

----
axios.get("/list",{
	params:{lx:12}
})

设置响应拦截器

分别在请求成功或者失败的时候做一些拦截处理(在执行成功后设定的方法之前,先会执行拦截器中的方法)

原理:在promise之后then这个方法,再点then成功请求成功要做的事情(原码)

axios.interceptors.response.use(function success(result){
	return result.data;//=>相当于把请求成功后返回的结果(对象)中的响应主体的内容,也就是data返回,作为实参传递给then中要执行的方法
})

设置请求头和请求主体

在POST请求中基于请求主体向服务器发送内容的格式,默认是RAW,项目中常用的是X-WWW-URL-ENCODED格式

两个要同时进行设置才能起效果,而且只对post方法有效,get方法Query String Parameters还是以对象的形式返回

axios.defaults.headers["Content-Type"]="appliction/x-www-form-urlencoded";

axios.defaults.transformRequest=function (data){//=>data是post中请求主体的内容
	let str=``;
	for(let attr in data){
		if(!data.hasOwnProperty(attr)) break;
		str+=`${attr}=${data[attr]}`
	}
	return str=str.substring(0,str.length-1);
}

设置成功失败规则

axios中默认状态码以2开头算作成功,我们可以自定义成功失败的规则

axios.defaults.validateStatus=function validateStatus(status){
	return /^(2|3)\d{2}$/.test(status)
}

重写axios部分原码

(function(window){
	let _defaults={
		url:"",
		baseURL:"",
		method:"get",
		headers:{},
		data:null,//=>post系列请求的请求主体
		params:null,//=>get系列的问号传参
		dataType:"json",
		cache:ture//=>是否建立缓存
	}
	function axiosAJAX(options){
		let {method,url,baseURL,headers,data,params,dataType,cache}=options;
		//=>根据method的不同处理params和data
		let reg=/^(get|head|delete|options|trace|connect)$/i;
		if(reg.test(method)){
			if(typeof params==="object" && params!==null){
				let str=``;
				for(let key in params){
					if(!params.hasOwnProperty(key)) break;
					str+=`${key}=${params[key]}&`;
				}
				params=str.substring(0,str.length-1);
			}
			url.indexOf("?")>-1?url+=`&{params}`:url+=`?{params}`;
			data=null;
		}else{
			if(typeof data==="object" && data!==null){
				let str=``;
				for(let key in data){
					if(!data.hasOwnProperty(key)) break;
					str+=`${key}=${data[key]}&`; 
				}
				data=str.substring(0,str.length-1);
			}
		}
		
		//=>处理cache,如果是false则不建立缓存,get系列才存在缓存的问题
		if(cache===false && reg.test(method)){
			url.indexOf("?")>-1?url+=`&_=${new Date().getTime()}`:url+=`?_=${new Date().getTime()}`;
		}
		return new Promise((resolve,reject)=>{
			let xhr=new XMLHttpRequest();
			xhr.open(method,`${baseURL}${url}`);
			for(let key in headers){
				if(!headers.hasOwnProperty(key)) break;
				if(/[\u4e00-\u9fa5]/.test(headers[key])){
					headers[key]=encodeURIComponent(headers[key])
				}
				xhr.setRequestHeader(key,headers[key]);
			}
			xhr.onreadystatechange=function(){
				if(xhr.readyState===4){
					if(/^(2|3)\d{2}$/.test(xhr.status)){
						let result=xhr.responseText;
						dataType=dataType.toUpperCase();
						dataType==="JSON"?result=JSON.parse(result):(dataType==="XML"?result=xhr.responseXML:null);
						resolve(result);
					}else{
						reject(xhr);
					}
				}
			}
			xhr.send(data);
		});
	}
	axiosAJAX.defaults=_defaults;//=>用户可以修改默认配置项
	//=>get系列
	["get","head","delete","options","trace"].forEach((item)=>{
		options={...defaults,...options,url:url,method:item};
		return axiosAJAX(options);
	});
	//=>post系列
	["post","put"].forEach((item)=>{
		options={..._defaults,...options,url:url,method:item,data:data};
		return axiosAJAX(options);
	})
	window.axiosAJAX=axiosAJAX;
})(window)

fetch

fetch是ES2018规范中新增的API,它是JS中内置的API,基于fetch可以实现客户端和服务器端的信息通信

fetch('https://www.easy-mock.com/mock/5b0412beda8a195fb0978627/temp/info', {
        method: 'GET',
        headers: {//=>设置请求头
            'content-type': 'x-www-form-urlencoded'
        },
        //=>不管同源还是跨域请求都带着COOKIE信息
        credentials: 'include'
    })

使用fetch发送请求

  • GET/HEAD等请求不能设置BODY
  • 不管服务器返回的状态是多少,fetch都不认为是失败(哪怕是4或者5开头的状态码),都执行的是then中的方法(需要我们自己进行异常抛出处理),而且then方法的回调函数接收到的实参并不是返回结果,而是一个Response的实例,需要通过调取其原型上的方法才能获得我们需要从服务器获取的结果
fetch('https://www.easy-mock.com/mock/5b0412beda8a195fb0978627/temp/add', {
        method: 'post',
        body: 'a=1&b=2'//=>BODY中只支持字符串(GET请求还无法设置BODY,get请求要手动通过问号传参的方式加到url的末尾)
    }).then(result => {
        console.log(result);
        let {status} = result;
        if (/^(4|5)\d{2}$/.test(status)) {//=>抛出异常处理
            throw new Error('query data is error!');
            return;
        }
        return result.json();//=>这个才是我们要从服务器端获取的结果
    }).then(result => {
        console.log(result);
    }).catch(msg => {
        console.log(msg);
    });

header:包含响应头信息 redirected:是否重定向 status:状态码 statusText:对请求状态的描述 typeurl:请求地址

arrayBuffer()/blob()/json()/text():从服务器端获取数据的方法