第一部分:前后端交互与HTTP
1.前后端通信
1.1 什么是前后端通信?
前后端通信是指浏览器和服务器之间数据交互(请求-响应)的过程:前端向后端发送请求数据,后端接收到前端的请求之后,响应数据请求
- 前端向后端发送数据(用户注册):浏览器把用户注册信息以表单形式提交,点击注册后传输给服务器,服务器进行处理
- 后端向前端发送数据(访问页面):用户输入网址按下回车之后,请求一些资源加载,后端返回服务器的资源,让浏览器渲染
1.2 前后端通信方式(三种)
1.使用浏览器访问页面进行通信
在浏览器地址栏输入网址,按下回车
2.HTML 的标签进行通信
浏览器在解析 HTML 标签的时候,遇到一些
特殊的标签(link/img/script/iframe)
,会再次向服务器发送请求
(如果过有很多这样的标签要一个一个请求吗?chrom会允许同一域名下一次六个并发请求,所以要把这些标签里面的资源放到不同域名下增加请求效率)还有一些标签,浏览器解析的时候,不会向服务器发送请求,但是用户可以使用他们向服务器发送请求如
a/form
3.Ajax 和 Fetch
2.HTTP协议
2.1什么是http协议?
- 全称(HyperText Transfer Protocol)超文本传输协议
- 超文本:原先一个个单一的文本,通过超链接将其联系起来。由原先的单一的文本变成了可无限延伸、扩展的超级文本、立体文本
- HTML、JS、CSS、图片、字体、音频、视频等等文件,都是通过 HTTP(超文本传输协议) 在服务器和浏览器之间传输
- 每一次前后端通信,前端需要主动向后端发出请求,后端接收到前端的请求后,可以给出响应
2.2 HTTP 浏览器渲染过程(图解)
2.3 HTTP报文
- 浏览器向服务器发送请求时,请求本身就是信息,叫
请求报文
;- 服务器向浏览器发送响应时传输的信息,叫
响应报文
;
HTTP 报文格式
请求
请求头:起始行+首部
请求体
// GET 请求,没有请求体,数据通过请求头携带
// POST 请求,有请求体,数据通过请求体携带
响应
响应头:起始行+首部
响应体
复制代码
2.4 常用HTTP方法(增删改查)
http方法用来定义对于资源采取什么样的操作的,有各自的语义,浏览器发送请求时采用的方法,和响应无关
-
1.GET 获取数据
- GET 通过地址在请求头中携带数据
- 能携带的数据量和地址的长度有关系,一般最多就几K
- GET通过请求头也就是url发送数据, 可以被缓存
不安全 url?username=alex
GET的数据在URL中,通过历史记录,缓存很容易查到数据信息。- GET无害: 刷新、后退等浏览器操作GET请求是无害的,
-
2.POST 创建数据(注册)
向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
- POST 既可以通过地址在请求头中携带数据,也可以通过请求体携带数据
- 能携带的数据量理论上是无限的
- POST一般通过请求体发送数据, 所以不会被缓存
- POST的数据因为在请求主体内,不会被缓存,所以有一定的安全性保证
- POST可能重复提交表单
-
3.PUT 更新数据(修改个人信息,修改密码)
从客户端向服务器传送的数据取代指定的文档的内容。如更新一篇文章
-
4.DELETE 删除数据(删除一条评论)
请求服务器删除指定的页面。
-
5.RESTful 接口设计
一种接口设计风格,充分利用 HTTP 方法的语义,比如设置用户接口,增删改查的接口都是一样的,但是后端可以根据你的请求方式判断你对应的操作
2.5 常用HTTP状态码
HTTP 状态码是定义服务器对请求的处理结果,是服务器返回的。
- 100~199 消息:代表请求已被接受,需要继续处理 // websocket
- 100 客户必须继续发出请求
- 101 客户要求服务器根据请求转换HTTP协议版本
- 200~299 成功
- 200 (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
- 201 (已创建) 请求成功并且服务器创建了新的资源。
- 202 (已接受) 服务器已接受请求,但尚未处理。
- 300~399 重定向:给你返回一个新的请求地址,你需要用新的地址重新发送请求
- 301 Moved Permanently
- 302 Move Temporarily 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
- 304 Not Modified(没有被修改你可以继续使用)
- 400~499 请求错误
- 400 (错误请求) 服务器不理解请求的语法。
- 401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
- 403 (禁止) 服务器拒绝请求。
- 404 Not Found(请求代码写的有问题)
- 500~599 服务器错误(服务端搞错了)
- 500 Internal Server Error
第二部分:本地存储
1.Cookie
- Cookie (HTTP Cookie),是浏览器存储数据在用户本地的一种方式, 一般会自动随着浏览器每次请求发送到服务器端
- 利用 Cookie 跟踪统计用户访问该网站的习惯,比如什么时间访问,访问了哪些页面,在每个网页的停留时间等
- 在浏览器中操作 Cookie,不要在 Cookie 中保存密码等敏感信息.
2.Cookie的基本用法(代码中设置、读取)
2.1.写入 Cookie(不能一起设置,只能一个一个设置)
document.cookie = 'username=zs';
document.cookie = 'age=18';
复制代码
2.2.读取 Cookie
读取的是一个由名值对构成的字符串,每个名值对之间由“; ”(一个分号和一个空格)隔开
console.log(document.cookie);
// username=zs; age=18
复制代码
3.Cookie的属性
3.1 Cookie 的名称(Name)和值(Value)
- 最重要的两个属性,创建 Cookie 时必须填写,其它属性可以使用默认值
- Cookie 的名称或值如果包含非英文字母,则写入时需要使用 encodeURIComponent() 编码,读取时使用 decodeURIComponent() 解码
document.cookie = `username=${encodeURIComponent('张三')}`;
document.cookie = `${encodeURIComponent('用户名')}=${encodeURIComponent(
'张三'
)}`;
复制代码
3.2 失效(到期)时间
- 对于失效的 Cookie,会被浏览器清除
- 如果没有设置失效(到期)时间,这样的 Cookie 称为会话 Cookie,它存在内存中,当会话结束,也就是浏览器关闭时,Cookie 消失
- 想长时间存在,设置 Expires 或 Max-Age
// expires属性,值为 Date 类型
document.cookie = `username=alex; expires=${new Date(
'2100-1-01 00:00:00'
)}`;
// max-age值为数字,表示当前时间 + 多少秒后过期,单位是秒
// 如果 max-age 的值是 0 或负数,则 Cookie 会被删除
document.cookie = 'username=alex; max-age=5';
document.cookie = `username=alex; max-age=${24 * 3600 * 30}`;
复制代码
3.3 Domain 域
Domain 限定了访问 Cookie 的范围(不同域名) 使用 JS 只能读写当前域或父域的 Cookie,无法读写其他域的 Cookie
document.cookie = 'username=alex; domain=www.imooc.com';
复制代码
3.4 Path 路径
Path 限定了访问 Cookie 的范围(同一个域名下) 使用 JS 只能读写当前路径和上级路径的 Cookie,无法读写下级路径的 Cookie
document.cookie = 'username=alex; path=/course/list';
document.cookie = 'username=alex; path=/1.Cookie/';
// 当 Name、Domain、Path 这 3 个字段都相同的时候,才是同一个 Cookie
复制代码
3.5HttpOnly
设置了 HttpOnly 属性的 Cookie 不能通过 JS 去访问
3.6 Secure 安全标志
Secure 限定了只有在使用了 https 而不是 http 的情况下才可以发送给服务端
Domain、Path、Secure 都要满足条件,还不能过期的 Cookie 才能随着请求发送到服务器端
4.Cookie的封装
在cookie.js里封装一个cookie,向外提供三个方法,set,get,remove设置获取删除
// import { set, get, remove } from './cookie.js';其他地方引入cookie方法
// 1.写入 Cookie的方法:
const set = (name, value, { maxAge, domain, path, secure } = {}) => {
let cookieText = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
if (typeof maxAge === "number") {
cookieText += `; max-age=${maxAge}`;
}
if (domain) {
cookieText += `; domain=${domain}`;
}
if (path) {
cookieText += `; path=${path}`;
}
if (secure) {
cookieText += `; secure`;
}
document.cookie = cookieText;
// document.cookie='username=alex; max-age=5; domain='
};
// 2.get方法通过 name 获取 cookie 的值
const get = (name) => {
name = `${encodeURIComponent(name)}`;
const cookies = document.cookie.split("; ");
for (const item of cookies) {
const [cookieName, cookieValue] = item.split("=");
if (cookieName === name) {
return decodeURIComponent(cookieValue);
}
}
return;
};
// get('用户名');获取
// remove方法根据 name、domain 和 path 删除 Cookie
const remove = (name, { domain, path } = {}) => {
set(name, "", { domain, path, maxAge: -1 });
};
export { set, get, remove };
复制代码
在其他文件中使用cookie.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Cookie 的封装</title>
</head>
<body>
<script type="module">
import { set, get, remove } from './cookie.js';
//设置cookie名值
set('age', 18);
set('用户名', '张三');
set('sex', 'male', {
maxAge: 30 * 24 * 3600
});
//获取cookie
get('sex');
//删除cookie
remove('username');
remove('用户名');
</script>
</body>
</html>
复制代码
5. localStorage
- localStorage 也是一种浏览器存储数据的方式(本地存储),它只是存储在本地,不会发送到服务器端。
- 一种持久化的存储方式,也就是说如果不手动清除,数据就永远不会过期。它是采用键值对的方式存储数据,按域名将数据分别保存到对应数据库文件里。相比 Cookie 来说,它能保存更大的数据。
- sessionStorage 是一种会话级别的缓存,关闭浏览器时数据会被清除。需要注意的是 sessionStorage 的作用域是窗口级别的,也就是说不同窗口之间保存的 sessionStorage 数据是不能共享的。sessionStorage 的数据只存在于当前浏览器的标签页;
localStorage 的特点:
大小限制为 5MB ~10MB;
在同源的所有标签页和窗口之间共享数据;
数据仅保存在客户端,不与服务器进行通信;
数据持久存在且不会过期,重启浏览器后仍然存在;
对数据的操作是同步的。
5.1 localStorage方法:
// 通过setItem()增加一个数据项
localStorage.setItem('myName', 'Semlinker');
// 通过getItem()获取某个数据项
// 获取不存在的返回 null
let me = localStorage.getItem('myName');
// 通过removeItem()移除某个数据项
// 删除不存在的 key,不报错
localStorage.removeItem('myName');
// 移除所有数据项
localStorage.clear();
复制代码
5.1 localStorage的键值:
localStorage 存储的键和值只能是字符串类型,不是字符串类型,也会先转化成字符串类型再存进去
第三部分 Ajax基础
1.什么是Ajax?
Ajax 是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的简写.
- Ajax 中的异步:可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己的事情。直到成功获取响应后,浏览器才开始处理响应数据
- XML(可扩展标记语言)是前后端数据通信时传输数据的一种格式,XML 现在已经不怎么用了,现在比较常用的是 JSON
- Ajax 其实就是浏览器与服务器之间的一种异步通信方式
- 使用 Ajax 可以在不重新加载整个页面的情况下,对页面的某部分进行更新
实例 ① 慕课网注册检测 ;② 慕课网搜索提示
2.搭建 Ajax 开发环境
Ajax 需要服务器环境,非服务器环境下,很多浏览器无法正常使用 Ajax;
vscode中安装插件Live Server就可以快速搭建服务器环境
3.Ajax 的使用步骤
Ajax 想要实现浏览器与服务器之间的异步通信,需要依靠 XMLHttpRequest,它是一个构造函数. 发送Ajax请求核心四部曲:
- 1.创建一个XMLHttpRequest实例对象。
- 2.打开请求连接,配置请求信息。
- 3.监听请求状态——不同的状态做不同的事。
- 4.发送AJAX请求,AJAX任务开始,一直到响应主体信息返回代表任务结束。
3.1.创建XMLHttpRequest实例对象
const xhr = new XMLHttpRequest();
复制代码
3.2.打开请求连接,配置请求信息
调用 open 并不会真正发送请求,而只是做好发送请求前的准备工作
xhr.open('GET','url',true)
有三个参数:第一个参数为http请求方式:GET、POST、PUT、DELETE
第二个参数为请求地址
第三个参数为是否为异步请求,true表示是异步请求
复制代码
3.3.监听事件,处理响应
当获取到响应后(readyState 改变),会触发 xhr 对象的 readystatechange 事件,可以在该事件中对响应进行处理判断请求状态是否完成,并且请求是否成功了
AJAX状态码——xhr.readyState
HTTP状态码——xhr.status
readystatechange 事件监听 readyState 这个状态的变化,它的值从 0 ~ 4,一共 5 个状态:
0:未初始化。尚未调用 open() 1:启动。已经调用 open(),但尚未调用 send() 2:发送。已经调用 send(),但尚未接收到响应 3:接收。已经接收到部分响应数据 4:完成。已经接收到全部响应数据,而且已经可以在浏览器中使用了 复制代码
xhr.onreadystatechange = () => {
// 4:完成。已经接收到全部响应数据,而且已经可以在浏览器中使用了
if (xhr.readyState !== 4) return;
//200-299:成功,请求被成功接收并处理;
//304:Not Modified 未修改。返回此状态码时,不会返回任何资源,而是让浏览器去获取缓存中的资源。
if ((xhr.status >= 200) & (xhr.status < 300) || xhr.status === 304) {
// console.log('正常使用响应数据');
console.log(xhr.responseText);
}
};
复制代码
或者:
xhr.addEventListener('readystatechange', () => {......}, fasle);
复制代码
- 为了兼容性,readystatechange 中不使用 this,而是直接使用 xhr
- 由于兼容性的原因,最好放在 open 之前
3.4.发送请求
// send() 的参数是通过请求体携带的数据,所以GET请求的send方法不传参或传null
xhr.send(null);
复制代码
4.使用 Ajax 完成前后端通信-小案例
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.responseText);
console.log(typeof xhr.responseText);
//XMLHttpRequest.responseText 在一个请求被发送后,从服务器端返回文本。
}
};
xhr.open('GET', url, true);
xhr.send(null);
复制代码
5.ajax的GET请求
1.携带数据
GET 请求不能通过请求体携带数据,但可以通过请求头携带
const url =
'https://www.imooc.com/api/http/search/suggest?words=js&username=alex&age=18';
2.数据编码
如果携带的数据是非英文字母的话,比如说汉字,就需要编码之后再发送给后端,不然会造成乱码问题
可以使用 encodeURIComponent() 编码
const url = `https://www.imooc.com/api/http/search/suggest?words=${encodeURIComponent(
'前端'
)}`;
复制代码
6.ajax的POST请求
1.携带数据
POST 请求主要通过请求体携带数据,同时也可以通过请求头携带
如果想发送数据,直接写在 send() 的参数位置,一般是字符串
xhr.send('username=alex&age=18');
不能直接传递对象,需要先将对象转换成字符串的形式
xhr.send({
username: 'alex',
age: 18
});
复制代码
7.JSON
JSON是Ajax 发送和接收数据的一种格式(前端认识,后端也认识) JSON 有 3 种形式,每种形式的写法都和 JS 中的数据类型很像,可以很轻松的和 JS 中的数据类型互相转换
7.1 JSON的三种形式
- 1.简单值形式
xxx.json
JSON 的简单值形式就对应着 JS 中的基础数据类型: 数字、字符串、布尔值、null
注意事项
① JSON 中没有 undefined 值
② JSON 中的字符串必须使用双引号
③ JSON 中是不能注释的
复制代码
- 2.对象形式
JSON 的对象形式就对应着 JS 中的对象
注意事项:
JSON 中对象的属性名必须用双引号,属性值如果是字符串也必须用双引号
JSON 中只要涉及到字符串,就必须使用双引号
不支持 undefined
复制代码
- 3.数组形式
JSON 的数组形式就对应着 JS 中的数组
[1, "hi", null]
注意事项:
数组中的字符串必须用双引号
JSON 中只要涉及到字符串,就必须使用双引号
不支持 undefined
复制代码
7.2 两种JSON的基本用法
1. JSON.parse() 可以将 JSON 格式的字符串解析成 JS 中的对应值
一定要是合法的 JSON 字符串,否则会报错
2.JSON.stringify()可以将 JS 的基本数据类型、对象或者数组转换成 JSON 格式的字符串
xhr.send(JSON.stringify({ username: 'alex', age: 18 })) 复制代码
7.3 JSON封装localStorage
localStorage里面的键和值是字符串,但是有的时候我们想传一些数组对象,可以封装一下:
const storage = window.localStorage;
// 设置
const set = (key, value) => {
// {
// username: 'alex'
// }
storage.setItem(key, JSON.stringify(value));
};
// 获取
const get = key => {
// 'alex'
// {
// "username": "alex"
// }
return JSON.parse(storage.getItem(key));
};
// 删除
const remove = key => {
storage.removeItem(key);
};
// 清空
const clear = () => {
storage.clear();
};
export { set, get, remove, clear };
复制代码
第四部分 跨域
跨域
就是不同域之间发送请求;不同域
协议、域名、端口号,任何一个不一样,就是不同域;同源策略
是浏览器的一种安全策略,阻止跨域请求.跨域解决方案: ① CORS 跨域资源共享 ② JSONP
优先使用 CORS 跨域资源共享,如果浏览器不支持 CORS 的话,再使用 JSONP
1.跨域的第一种方案:CORS跨域资源共享(后端要做的 IE10)
后端在响应头设置:允许跨域
Access-Control-Allow-Origin: * // 表明允许所有的域名来跨域请求它
Access-Control-Allow-Origin: http://127.0.0.1:5500 // 只允许指定域名的跨域请求
CORS 跨域的过程
① 浏览器发送请求
② 后端在响应头中添加 Access-Control-Allow-Origin 头信息
③ 浏览器接收到响应
④ 如果是同域下的请求,浏览器不会额外做什么,这次前后端通信就圆满完成了
⑤ 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
⑥ 如果允许跨域,通信圆满完成
⑦ 如果没找到或不包含想要跨域的域名,就丢弃响应结果
复制代码
2.跨域的第二种方案: Jsonp
JSONP 的原理:script 标签跨域不会被浏览器阻止,JSONP 主要就是利用 script 标签,加载跨域文件。
使用 JSONP 实现跨域:
1.后端创建一个接口,注意后面的aaaa是前端随便定义的:`https://www.imooc.com/api/http/jsonp?callback=aaaa`
2.这个接口打开的数据是这样的,以函数调用形式传输数据:
aaaa(
{ "code": 200,
"data": [
{ "word": "jsp" },
{ "word": "js" },
{ "word": "json" },
{ "word": "js 入门" },
{ "word": "jstl" } ]
}
);
3.前端只需要在script标签里面引入这个接口,然后根据我们在接口自己定义的callback=aaaa声明一个aaaa函数
4.当发送请求时就调用了这个函数,把数据通过函数调用传递过来
复制代码
/*
服务器端准备好 JSONP 接口:
https://www.imooc.com/api/http/jsonp?callback=handleResponse
*/
/*
前端手动加载 JSONP 接口或动态加载 JSONP 接口
*/
const script = document.createElement('script');
script.src ='https://www.imooc.com/api/http/jsonp?callback=handleResponse';
document.body.appendChild(script);
//声明函数
const handleResponse = data => {
console.log(data);//拿到数据了
};
//相当于通过函数执行传递参数给函数,函数内部获取数据
复制代码
第五部分 XMLHttpRequest对象
1. XMLHttpRequest属性
1.responseType:表示文本形式的响应内容:
- responseType是在准备好发送和发送之前设置
- responseType 没有设置时候,responseText和response一样,相当于
xhr.responseType='text'
;- responseType设置为json之后,就需要使用response属性获取了;
- 知道后端返回来的格式是json,设置json了,就不需要编译了,系统自动编译;
2.timeout 属性:设置请求的超时时间(单位 ms)xhr.timeout = 10000; 3.withCredentials 属性:指定使用 Ajax 发送请求时是否携带 Cookie
使用 Ajax 发送请求,默认情况下,同域时,会携带 Cookie;跨域时,不会 xhr.withCredentials = true; 最终能否成功跨域携带 Cookie,还要看服务器同不同意
2. XMLHttpRequest方法
1.abort()
终止当前请求
一般配合 abort 事件一起使用
2.setRequestHeader()
可以设置请求头信息,只有发送post请求时,才有必要设置这个属性
xhr.setRequestHeader(头部字段的名称, 头部字段的值);
xhr.setRequestHeader('Content-Type', 'application/json');
// 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的
1.发送json数据
xhr.setRequestHeader('Content-Type', 'application/json');//浏览器发送的是json数据
xhr.send(
JSON.stringify({
username: 'alex'
})
);
2.发送名值对数据
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('username=alex&age=18');
//若果这样做报错了,就让后端改接口
复制代码
3. XMLHttpRequest事件
3.1.load 事件- 响应数据可用时触发
load事件可以替代xhr.onreadystatechange事件,只需监听响应(xhr.status)是否成功,不需要监听请求(readyState)是否成功。
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response);
}
},
false
);
//不支持ie6-8
复制代码
3.2.error 事件-请求发生错误时触发(如同地址错误,压根就没有发出请求)
xhr.addEventListener(
'error',
() => {
console.log('error');
},
false
);
复制代码
3.3.abort 事件-调用 abort() 终止请求时触发(ie10+)
3.4.timeout 事件-请求超时后触发(ie8+)
第六部分 Ajax进阶
1.FormData
使用 Ajax 提交表单的时候,我们会用到FormData;
FormData 的基本用法:
//通过 HTML 表单元素创建 FormData 对象
const fd = new FormData(表单元素);
xhr.send(fd);
//通过 append() 方法添加数据
const fd = new FormData(表单元素);
fd.append('age', 18);
fd.append('sex', 'male');
xhr.send(fd);
IE10 及以上可以支持
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>FormData</title>
</head>
<body>
<form
id="login"
action="https://www.imooc.com/api/http/search/suggest?words=js"
method="POST"
enctype="multipart/form-data"
>
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<input id="submit" type="submit" value="登录" />
</form>
<script>
const login = document.getElementById('login');
const { username, password } = login;
const btn = document.getElementById('submit');
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
btn.addEventListener(
'click',
e => {
// 阻止表单自动提交
e.preventDefault();
// 表单数据验证
// 发送 Ajax 请求
const xhr = new XMLHttpRequest();
xhr.addEventListener(
'load',
() => {
if (
(xhr.status >= 200 && xhr.status < 300) ||
xhr.status === 304
) {
console.log(xhr.response);
}
},
false
);
xhr.open('POST', url, true);
// 收集表单数据
const data = new FormData(login);
console.log(data); //直接打印不出来
for (item of data) {
console.log(item);
}
/*
["username", "1231"]
["password", "21313131"]
*/
xhr.send(data);
},
false
);
</script>
</body>
</html>
复制代码
2.Ajax封装
// 工具函数-把对象变成字符串(序列化)
/* 对象变成数组,数组变成字符串
对象不能遍历,利用Object.entries()
Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)
*/
//数据序列化成urlencoded格式的字符串
const serialize=param=>{
const results=[];
for(const [key,value] of Object.entries(param)){
results.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
}
//['username=alex','age=18']
//join() 方法用于把数组中的所有元素放入一个字符串。
return results.join("&")
}
复制代码
给url添加参数
//www.muke.com
//www.muke.com?words=js
//www.muke.com?words=js&
const addURLData=(url,data)=>{
if(!data) return '';
const mark=url.includes('?')?'&':'?'
return `${mark}${data}`
}
复制代码
第一步:封装:ajax函数封装在ajax.js里面了
//第一步:默认参数设置:
const DEFAULTS = {
method: "GET",
//请求头携带的数据
params: null,
// params: {
// username: "alex",
// age: 14,
// },
//会自动转化为:username=alex&age=18
//请求体携带的数据
data: null,
// data: {
// username: "alex",
// age: 14,
// },
//data:FormData数据
contentType: "application/x-www-form-urlencoded",
responseType: "",
timeoutTime: 0,
withCredentials: false,
//方法:
//成功的方法
success() {},
// 状态码异常处理
httpCodeError() {},
//出现错误,比如地址不对压根没有发出去
error() {},
//中止了
abort() {},
//超时了
timeout() {},
};
//第二步:创建Ajax类
class Ajax {
constructor(url, options) {
//外界传来的url地址
this.url = url;
//默认参数设置
this.options = Object.assign({}, DEFAULTS, options);
//1.初始化,这里面进行四部曲操作,让代码不那么拥挤
this.init();
}
init() {
const xhr = new XMLHttpRequest();
//绑定到类的共有属性
this.xhr = xhr;
//2.把监听方法写到里面
this.bindEvents();
// 3.处理xhr.open('POST', url, true);
xhr.open(this.options.method, this.url + this.addParam(), true);
//4.设置responseType
this.setResponseType();
//5.设置跨域是否携带cookie
this.setCookie();
//5.设置超时
this.timeout();
//6.发送请求 xhr.send();
this.sendData();
}
//绑定响应事件处理程序的区域
bindEvents() {
//偷懒
xhr = this.xhr;
//把方法结构出来在这里面调用
const { success, httpCodeError, error, abort, timeout } = this.options;
xhr.addEventListener(
"load",
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
success(xhr.response, xhr);
} else {
httpCodeError(xhr.status, xhr);
}
},
false
);
// error
// 当请求遇到错误时,将触发 error 事件
xhr.addEventListener(
"error",
() => {
error(xhr);
},
false
);
// abort
xhr.addEventListener(
"abort",
() => {
abort(xhr);
},
false
);
// timeout
xhr.addEventListener(
"timeout",
() => {
timeout(xhr);
},
false
);
}
//下面是工具函数库
//在地址上添加数据,就是`url?name=zs`
/*
要先借用两个函数:
先序列化函数,转化成'username=alex&age=18';
再addURLData:统一数据格式加&还是❓
*/
serialize(param) {
const results = [];
/*
若果是对象就Object.entries(param)弄成二维数组然后用for..of遍历
若果是数组就直接遍历了
*/
for (const [key, value] of Object.entries(param)) {
results.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
}
//处理之后变成这样了['username=alex','age=18']
return results.join("&");
//处理之后变成'username=alex&age=18'了
}
addURLData = (url, data) => {
if (!data) return "";
//有数据加&,没数据加?
const mark = url.includes("?") ? "&" : "?";
return `${mark}${data}`;
};
// 上面两个工具函数都是为他服务的;
addParam() {
//解构参数
const { params } = this.options;
if (!params) return "";
return addURLData(this.url, serialize(params));
}
//设置responseType
setResponseType() {
this.xhr.responseType = this.options.responseType;
}
// 设置跨域是否携带cookie
setCookie() {
if (this.options.withCredentials) this.xhr.withCredentials = true;
}
// 设置超时
setTimeout() {
const { timeoutTime } = this.options;
if (timeoutTime > 0) this.xhr.timeout = timeoutTime;
}
//发送数据
sendData() {
const xhr = this.xhr;
//若果不需要再send中携带数据,直接发送
if (!this.isSendData()) return xhr.send(null);
//发送数据xhr.send(数据)
let resultData = null;
//// 发送FormData数据,content-type不需要设置,自动添加
if (this.isFormData()) {
resultData = data;
// //是不是名值对格式的:username=alex&age=18
} else if (this.isFormURLEncodedData()) {
// 设置contentType
this.setContentType("application / x - www - form - urlencoded");
//发送这种格式的数据application / x - www - form - urlencoded
resultData = this.serialize(data);
} else if (this.isJsonData()) {
// 设置contentType
this.setContentType("application /json");
resultData = this.serializeJson(data);
} else {
//其他格式
this.setContentType();
resultData = data;
}
xhr.send(resultData);
}
//是否需要在send中携带数据
isSendData() {
//data是请求体携带的数据
const { data, method } = this.options;
//如果data不存在返回false,上级函数直接return xhr.send(null);
if (!data) return false;
//若果是get请求,传不传都没用,返回false
if (method.toLowerCase() === "GET".toLowerCase()) return false;
return true;
}
////判断是不是formdata数据,是就进行处理
isFormData() {
return this.options.data instanceof FormData;
}
////判断是不是名值对格式的数据
isFormURLEncodedData() {
return this.options.contentType
.toLowerCase()
.includes("application / x - www - form - urlencoded");
}
////判断是不是json格式的数据
isJsonData() {
return this.options.contentType.toLowerCase().includes("application/json");
}
////转化为json格式的字符串
serializeJson(param) {
return JSON.stringify(param);
}
setContentType(contentType = this.options.contentType) {
if (!contentType) return;
this.xhr.setRequestHeader("Content-Type", contentType);
}
//获取XHR对象
getXHR() {
return this.xhr;
}
}
export default Ajax;
复制代码
第二步:创建一个index.js实例化几个方法供外界使用
import Ajax from ./ajax.js;
const ajax=(url,options)=>{
return new Ajax(url,options).getXHR()
}
//定义get请求,覆盖用户传过来的参数
const get=(url,options)=>{
return ajax(url,{...options,method:'GET'})
}
//定义get请求发送json数据,覆盖用户传过来的参数
const getJSON=(url,options)=>{
return ajax(url,{...options,responseType:'json'})
}
//post请求
const post=(url,options)=>{
return ajax(url,{...options,method:'POST'})
}
export {
ajax,get,getJSON,post
}
复制代码
第三步:模拟发送请求
<script>
import {ajax,get,getJSON,post} from './index.js'
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
const xhr = ajax(url, {
method: 'GET',
params: { username: 'alex' },
data: {
age: 18
},
responseType: 'json',
// timeoutTime: 10,
success(response) {
console.log(response);
},
httpCodeError(err) {
console.log('http code error', err);
},
error(xhr) {
console.log('error', xhr);
},
abort(xhr) {
console.log('abort', xhr);
},
timeout(xhr) {
console.log('timeout', xhr);
}
});
xhr.abort();
<script/>
复制代码
3.使用promise封装ajax
import Ajax from "./ajax.js";
const ajax = (url, options) => {
// return new Ajax(url, options).getXHR();
let xhr;
const p = new Promise((resolve, reject) => {
xhr = new Ajax(url, {
...options,
...{
success(response) {
resolve(response);
},
httpCodeError(status) {
reject({
type: "ERROR_HTTP_CODE",
text: `${status}`,
});
},
error() {
reject({
type: "ERROR_REQUEST",
text: "请求错误",
});
},
abort() {
reject({
type: "ERROR_ABORT",
text: "取消发送",
});
},
timeout() {
reject({
type: "ERROR_TIMEOUT",
text: "请求超时",
});
},
},
}).getXHR();
});
p.xhr = xhr;
return p;
};
const get = (url, options) => {
return ajax(url, { ...options, method: "GET" });
};
const getJSON = (url, options) => {
return ajax(url, { ...options, method: "GET", responseType: "json" });
};
const post = (url, options) => {
return ajax(url, { ...options, method: "POST" });
};
export { ajax, get, getJSON, post };
复制代码
发送请求:
<script>
import { ajax, get, getJSON, post } from "./ajax/index.js";
const url = "https://www.imooc.com/api/http/search/suggest?words=js";
const p = getJSON(url, {
params: { username: "alex" },
data: { age: 18 },
// timeoutTime: 10,
}).then((res) => {
console.log(res);
});
p.catch((err) => {
console.log(err);
});
</script>
复制代码
4.Ajax应用
Ajax应用-搜索提示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>搜索提示</title>
</head>
<body>
<input id="search" type="text" />
<ul id="result"></ul>
<script type="module">
import { getJSON } from "./ajax/index.js";
const url = "https://www.imooc.com/api/http/search/suggest?words=";
const searchInput = document.getElementById("search");
const resultList = document.getElementById("result");
function handInputEvent() {
if (searchInput.value.trim() !== "") {
getJSON(`${url}${searchInput.value}`).then((res) => {
console.log(res);
let html = "";
for (const item of res.data) {
html += `<li>${item.word}<li>`;
}
resultList.innerHTML = html;
resultList.style.display = "";
res;
});
} else {
resultList.innerHTML = "";
resultList.style.display = "none";
}
}
let timer = null;
searchInput.addEventListener("input", () => {
// handInputEvent(searchInput);
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(handInputEvent, 2000);
});
</script>
</body>
</html>
复制代码
Ajax应用-二级菜单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>二级菜单</title>
<style>
/* css reset */
* {
padding: 0;
margin: 0;
}
li {
list-style: none;
}
/* menu */
.menu {
width: 100px;
background-color: rgba(0, 0, 0, 0.1);
margin: 10px;
}
.menu-item {
position: relative;
padding: 5px;
cursor: pointer;
}
.menu-content {
display: none;
position: absolute;
left: 100%;
top: 0;
width: 200px;
height: 100px;
padding: 0 5px;
background-color: rgba(0, 0, 0, 0.1);
}
.menu-item:hover {
background-color: rgba(0, 0, 0, 0.4);
}
.menu-item:hover .menu-content {
display: block;
}
.menu-loading {
margin: 45px 0 0 92px;
}
</style>
</head>
<body>
<ul id="menu" class="menu">
<!-- <li class="menu-item" data-key="hot" data-done="done">
<span>热门</span>
<div class="menu-content">
<p><img class="menu-loading" src="./loading.gif" alt="加载中" /></p>
</div>
</li> -->
</ul>
<script type="module">
// https://www.imooc.com/api/mall-PC/index/menu/hot
// https://www.imooc.com/api/mall-PC/index/menu
import { getJSON } from './ajax/index.js';
const menuURL = 'https://www.imooc.com/api/mall-PC/index/menu';
const menuEl = document.getElementById('menu');
getJSON(menuURL)
.then(repsonse => {
// console.log(repsonse);
let html = '';
for (const item of repsonse.data) {
html += `
<li class="menu-item" data-key="${item.key}">
<span>${item.title}</span>
<div class="menu-content">
<p><img class="menu-loading" src="./loading.gif" alt="加载中" /></p>
</div>
</li>
`;
}
menuEl.innerHTML = html;
// [{key: "hot", title: "热门出发地", subTitles: Array(5)}]
// ...
})
.then(() => {
const items = menuEl.querySelectorAll('.menu-item');
for (const item of items) {
item.addEventListener(
'mouseenter',
() => {
// console.log(item.getAttribute('data-key'));
// IE11 开始支持
// console.log(item.dataset.key);
//防止重复发送,已经发送了的就不发送了
if (item.dataset.done === 'done') return;
getJSON(
`https://www.imooc.com/api/mall-PC/index/menu/${item.dataset.key}`
)
.then(repsonse => {
// console.log(repsonse);
// [{title: "内地热门城市", cities: Array(27)}]
item.dataset.done = 'done';
let html = '';
for (const item of repsonse.data) {
html += `<p>${item.title}</p>`;
}
item.querySelector('.menu-content').innerHTML = html;
})
.catch(err => {
console.log(err);
});
},
false
);
}
})
.catch(err => {
console.log(err);
});
</script>
</body>
</html>
复制代码
Ajax应用-多个ajax请求的并发执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>多个 Ajax 请求的并发执行</title>
<style>
/* css reset */
* {
padding: 0;
margin: 0;
}
li {
list-style: none;
}
/* menu */
.menu {
width: 100px;
background-color: rgba(0, 0, 0, 0.1);
margin: 10px;
}
.menu-item {
position: relative;
padding: 5px;
cursor: pointer;
}
.menu-content {
display: none;
position: absolute;
left: 100%;
top: 0;
width: 200px;
height: 100px;
padding: 0 5px;
background-color: rgba(0, 0, 0, 0.1);
}
.menu-item:hover {
background-color: rgba(0, 0, 0, 0.4);
}
.menu-item:hover .menu-content {
display: block;
}
.menu-loading {
margin: 45px 0 0 92px;
}
/* loading-page */
.loading-page {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
background-color: #eee;
text-align: center;
}
.loading-img {
position: absolute;
top: 50%;
}
.ad img {
display: inline-block;
width: 25%;
}
.none {
display: none;
}
</style>
</head>
<body>
<div id="loading-page" class="loading-page">
<img class="loading-img" src="./loading.gif" alt="加载中" />
</div>
<div id="ad" class="ad"></div>
<ul id="menu" class="menu">
<!-- <li class="menu-item" data-key="hot" data-done="done">
<span>热门</span>
<div class="menu-content">
<p><img class="menu-loading" src="./loading.gif" alt="加载中" /></p>
</div>
</li> -->
</ul>
<script type="module">
import { getJSON } from './ajax/index.js';
const menuURL = 'https://www.imooc.com/api/mall-PC/index/menu';
const adURL = 'https://www.imooc.com/api/mall-PC/index/ad';
const loadingPageEl = document.getElementById('loading-page');
const adEl = document.getElementById('ad');
const menuEl = document.getElementById('menu');
const p1 = getJSON(menuURL)
.then(repsonse => {
// console.log(repsonse);
let html = '';
for (const item of repsonse.data) {
html += `
<li class="menu-item" data-key="${item.key}">
<span>${item.title}</span>
<div class="menu-content">
<p><img class="menu-loading" src="./loading.gif" alt="加载中" /></p>
</div>
</li>
`;
}
menuEl.innerHTML = html;
// [{key: "hot", title: "热门出发地", subTitles: Array(5)}]
// ...
})
.then(() => {
const items = menuEl.querySelectorAll('.menu-item');
for (const item of items) {
item.addEventListener(
'mouseenter',
() => {
// console.log(item.getAttribute('data-key'));
// IE11 开始支持
// console.log(item.dataset.key);
if (item.dataset.done === 'done') return;
getJSON(
`https://www.imooc.com/api/mall-PC/index/menu/${item.dataset.key}`
)
.then(repsonse => {
// console.log(repsonse);
// [{title: "内地热门城市", cities: Array(27)}]
item.dataset.done = 'done';
let html = '';
for (const item of repsonse.data) {
html += `<p>${item.title}</p>`;
}
item.querySelector('.menu-content').innerHTML = html;
})
.catch(err => {
console.log(err);
});
},
false
);
}
})
.catch(err => {
console.log(err);
});
const p2 = getJSON(adURL)
.then(response => {
// console.log(response);
// [{ url: 'http://alimc.img.imooc.com/class/' }];
let html = '';
for (const item of response.data) {
html += `<img src="${item.url}" alt="" />`;
}
adEl.innerHTML = html;
})
.catch(err => {
console.log(err);
});
Promise.all([p1, p2]).then(() => {
// loadingPageEl.style.display = 'none';
// IE10 开始支持
loadingPageEl.classList.add('none');
// loadingPageEl.classList.remove('none');
});
</script>
</body>
</html>
复制代码
第七部分 axios
axios官网:传送门
1.什么是axios?
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
特性
从浏览器中创建 XMLHttpRequests
从 node.js 创建 http 请求
支持 Promise API
拦截请求和响应
转换请求数据和响应数据
取消请求
自动转换 JSON 数据
客户端支持防御 XSRF
复制代码
2. axios使用步骤
第一步:引入
// npm install axios
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
const url = "https://www.imooc.com/api/http/search/suggest?words=js";
//1.使用axios()方法
axios(url, {
//请求方式
method: "post",
// 请求头信息
headers: {
"Content-Type": "application/x-www-form-urlencoded",
// "Content-Type": "application/json",
},
// 通过请求头携带的数据
params: {
username: "alex",
},
// 通过请求体携带的数据
//如果传的是这种对象,就会以json格式传过去,Content-Type就需要设置为指定的json格式
// data: {
// age: 19,
// sex: "male",
// },
data: "age=18&sex=male",
})
.then((response) => {
console.log(response);
console.log(response.data.data);
})
.catch((err) => {
console.log(err);
});
//2.使用axios.get()方法:
axios
.get(url, {
params: { username: "alex" },
})
.then((response) => {
console.log(response);
});
//不需要设置content-type,而是根据你传的参数的格式自动设置
//3.axios.post()方法:
axios.post(url, { username: "alex" }).then((response) => {
console.log(response);
});
复制代码
3. axios()的一些属性
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
axios({
url: "https://www.imooc.com/api/http/search/suggest?words=js",
baseURL: "",
method: "post", // default
// `headers` 是即将被发送的自定义请求头
//给服务器说明我的格式,服务器拿到之后就会针对这种格式做不同处理
headers: { "Content-Type": "application/x-www-form-urlencoded" },
// headers: { "Content-Type": "application/json" },
// `params` 是即将与请求一起发送的 URL 参数,必须是一个无格式对象(plain object)或 URLSearchParams 对象
//写到这这里面的会自动编译成'?' "&"传参的格式给请求地址
params: {
ID: 12345,
name: "蔡徐坤",
},
/*
data: POST是作为请求主体被发送的数据(默认是按照json格式发送的,需要先处理成字符串形式才能和header保持一致)
*/
data: {
firstName: "Fred111",
},
transformRequest:function(data){
return Qs.stringify(data)
},
// timeout 指定请求超时的毫秒数(0 表示无超时时间),请求将被中断
timeout: 0,
// withCredentials表示跨域请求时是否需要使用凭证
withCredentials: false, // default
// responseType表示把服务器返回数据类型的格式转化为我们预设的格式,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: "json", // default
}).then((response) => {
console.log(response);
});
</script>
复制代码
4.axios()的公供属性剥离出来
<!-- 第一步:引入 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
// 第二步:配置默认信息
axios.defaults.baseURL = "https://www.imooc.com";
axios.defaults.headers["Content-Type"] =
"application/x-www-form-urlencoded";
// 添加请求拦截器
axios.interceptors.request.use(
function (config) {
// 在发送请求之前做些什么
return config;
},
function (error) {
// 对请求错误做些什么
return Promise.reject(error);
}
);
/*
添加响应拦截器
拦截器:在请求或响应被 then 或 catch 处理前拦截它们设置响应拦截器:
[成功状态]把从服务器获取的结果中的响应主体信息获取到即可,[失败状态]手动把错误信息抛出异常
*/
axios.interceptors.response.use(
function (response) {
// 从服务器获取了数据
return response.data;
},
function (error) {
// 从服务器没有获取数据
return Promise.reject(error);
}
);
//第三步:发送自己单独的请求
axios
.get("/api/http/search/suggest?words=js", {
params: {
name: "xxxx",
age: 19,
},
})
.then((response) => {
console.log(response);
});
axios
.post(
"/api/http/search/suggest?words=js",
{
name: "cccccc",
age: 123213,
},
{}
)
.then((s) => {
console.log(s);
});
</script>
复制代码
第八部分 fetch
- Fetch 也是前后端通信的一种方式
- Fetch 是 XMLHttpRequest对象的一种替代方案,它是基于 Promise 的,浏览器原生提供这个对象
1.fetch()与XMLHttpRequest的区别:
- 调用fetch(url)直接返回了 Promise对象。
- fetch()采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象),更合理一些;相比之下,XMLHttpRequest 的 API 设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码
- fetch()通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHTTPRequest 对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性吐出来。
2.fetch 的基本用法
- 1.fetch()可以只接受接受一个 URL 字符串作为参数,默认向该网址发出 GET 请求,返回一个 Promise 对象
const url = "https://www.imooc.com/api/http/search/suggest?words=";
fetch(url)
.then(function (response) {
// console.log(response);只能读取一次
//请求是否成功
if (response.ok) {
return response.json();
} else {
throw new Error(`状态码异常${response.status}`);
}
})
.then(function (responseData) {
console.log(responseData.data);
})
.catch((err) => {
console.log(err);
});
复制代码
用await改写:
async function getJSON() {
let url = 'https://api.github.com/users/ruanyf';
try {
let response = await fetch(url);
return await response.json();
} catch (error) {
console.log('Request Failed', error);
}
}
复制代码
3.fetch()的第二个参数:定制 HTTP 请求
3.1发送POST请求和json数据
const user = { name: 'John', surname: 'Smith' };
const response = await fetch('/article/fetch/post/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(user)
});
复制代码
3.2发送POST请求和键值数据
const response = await fetch(url, {
method: 'POST',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body: 'foo=bar&lorem=ipsum',
});
复制代码
3.3 post提交表单
const form = document.querySelector('form');
const response = await fetch('/users', {
method: 'POST',
body: new FormData(form)
})
复制代码
3.4 post上传文件
const input = document.querySelector('input[type="file"]');
const data = new FormData();
data.append('file', input.files[0]);
data.append('user', 'foo');
fetch('/avatars', {
method: 'POST',
body: data
});
复制代码
4.Response 对象:处理 HTTP 回应
- fetch()发出请求以后,只有网络错误,或者无法连接时,fetch()才会报错,其他情况都不会报错,而是认为请求成功。
- 只有fetch()发出请求通过Response.status属性,得到 HTTP 回应的真实状态码,才能判断请求是否成功.
- 第一种判断方法:response.status属性只有等于 2xx (200~299),才能认定请求成功。这里不用考虑网址跳转(状态码为 3xx),因为fetch()会将跳转的状态码自动转为 200。
- 另一种方法是判断response.ok是否为true。Response.ok属性返回一个布尔值,
表示请求是否成功
,true对应 HTTP 请求的状态码 200 到 299,false对应其他的状态码。
async function fetchText() {
let response = await fetch('/readme.txt');
if (response.status >= 200 && response.status < 300) {
return await response.text();
} else {
throw new Error(response.statusText);
}
}
//或者
if (response.ok) {
// 请求成功
} else {
// 请求失败
}
复制代码
- response.headers.get(),用于读取某个标头的值。
let response = await fetch(url);
response.headers.get('Content-Type')
// application/json; charset=utf-8
复制代码
- 读取文本内容:
- response.text():得到文本字符串。
- response.json():得到 JSON 对象。