week14 HTTP协议、存储、Ajax
前端数据交互与HTTP协议
前后端通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>初识前后端通信</title>
</head>
<body>
<script>
// 1.前后端通信是什么
// 前端和后端数据交互的过程
// 浏览器和服务器之间数据交互的过程
// 2.后端向前端发送数据
// 访问页面
// 3.前端向后端发送数据
// 用户注册
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>前后端通信的过程与概念解释</title>
</head>
<body>
<script>
// 1.前后端通信的过程
// 前后端的通信是在‘请求-响应’中完成的
// 2.概念解释
// 前端:浏览器端
// 客户端:只要能和服务器通信的就叫客户端
// 命令行工具
// curl https:www.imooc.com
// 后端:服务器端
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>前后端的通信方式</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<a href="https://www.imooc.com">慕课网</a>
<form action="https://www.imooc.com" method="post">
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<input type="submit" value="注册" />
</form>
<img src="./index.png" alt="" />
<script src="./index.js"></script>
<script>
// 1.使用浏览器访问网页
// 在浏览器地址栏输入网址,按下回车
// 2.HTML 的标签
// 浏览器在解析 HTML 标签的时候,遇到一些特殊的标签,会再次向服务器发送请求
// link/img/script/iframe
// 还有一些标签,浏览器解析的时候,不会向服务器发送请求,但是用户可以使用他们向服务器发送请求
// a/form
// 3.Ajax 和 Fetch
</script>
</body>
</html>
HTTP协议
初识 HTTP
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>初识 HTTP</title>
</head>
<body>
<script>
// 1.HTTP 是什么
// HyperText Transfer Protocol
// 超文本传输协议
// HTML:超文本标记语言
// 超文本:原先一个个单一的文本,通过超链接将其联系起来。由原先的单一的文本变成了可无限延伸、扩展的超级文本、立体文本
// HTML、JS、CSS、图片、字体、音频、视频等等文件,都是通过 HTTP(超文本传输协议) 在服务器和浏览器之间传输
// 每一次前后端通信,前端需要主动向后端发出请求,后端接收到前端的请求后,可以给出响应
// HTTP 是一个请求-响应协议
// 2.HTTP 请求响应的过程
// https://www.imooc.com
</script>
</body>
</html>
HTTP报文
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>HTTP 报文</title>
</head>
<body>
<form action="https://www.imooc.com" method="get">
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<input type="submit" value="注册" />
</form>
<form action="https://www.imooc.com" method="post">
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<input type="submit" value="注册" />
</form>
<script>
// 1.HTTP 报文是什么
// 浏览器向服务器发送请求时,请求本身就是信息,叫请求报文
// 服务器向浏览器发送响应时传输的信息,叫响应报文
// 2.HTTP 报文格式
// 请求
// 请求头:起始行+首部
// 请求体
// GET 请求,没有请求体,数据通过请求头携带
// POST 请求,有请求体,数据通过请求体携带
// 响应
// 响应头:起始行+首部
// 响应体
</script>
</body>
</html>
HTTP方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>HTTP 方法</title>
</head>
<body>
<script>
// 1.常用的 HTTP 方法
// 浏览器发送请求时采用的方法,和响应无关
// GET、POST、PUT、DELETE
// 用来定义对于资源采取什么样的操作的,有各自的语义
// 2.HTTP 方法的语义
// GET 获取数据
// 获取资源(文件)
// POST 创建数据
// 注册
// PUT 更新数据
// 修改个人信息,修改密码
// DELETE 删除数据
// 删除一条评论
// 增删改查
// 这些方法虽然有各自的语义,但是并不是强制性的
// 3.RESTful 接口设计
// 一种接口设计风格,充分利用 HTTP 方法的语义
// 通过用户 ID 获取个人信息,使用 GET 方法
// https://www.imooc.com/api/http/getUser?id=1
// GET
// https://www.imooc.com/api/http/user?id=1
// 注册新用户,使用 POST 方法
// https://www.imooc.com/api/http/addUser
// POST
// https://www.imooc.com/api/http/user
// 修改一个用户,使用 POST 方法
// https://www.imooc.com/api/http/modifyUser
// PUT
// https://www.imooc.com/api/http/user
// 删除一个用户,使用 POST 方法
// https://www.imooc.com/api/http/deleteUser
// DELETE
// https://www.imooc.com/api/http/user
</script>
</body>
</html>
GET和POST方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>GET 和 POST 方法的对比</title>
</head>
<body>
<script>
// 1.语义
// GET:获取数据
// POST:创建数据
// 2.发送数据
// GET 通过地址在请求头中携带数据
// 能携带的数据量和地址的长度有关系,一般最多就几K
// POST 既可以通过地址在请求头中携带数据,也可以通过请求体携带数据
// 能携带的数据量理论上是无限的
// 携带少量数据,可以使用 GET 请求,大量的数据可以使用 POST 请求
// 3.缓存
// GET 可以被缓存,POST 不会被缓存
// 4.安全性
// ?username=alex
// GET 和 POST 都不安全
// 发送密码或其他敏感信息时不要使用 GET,主要是避免直接被他人窥屏或通过历史记录找到你的密码
</script>
</body>
</html>
HTTP状态码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>HTTP 状态码</title>
</head>
<body>
<script>
// 1.HTTP 状态码是什么
// 定义服务器对请求的处理结果,是服务器返回的
// 2.HTTP 状态码的语义
// 100~199 消息:代表请求已被接受,需要继续处理
// websocket
// 200~299 成功
// 200
// 300~399 重定向
// http://www.imooc.com/
// https://www.imooc.com/
// 301 Moved Permanently
// 302 Move Temporarily
// 304 Not Modified
// 400~499 请求错误
// 404 Not Found
// 500~599 服务器错误
// 500 Internal Server Error
</script>
</body>
</html>
本地存储
Cookie
- 浏览器存储数据的一种方式
- 存储在用户本地,而不是存储在服务器上
- 可以随着浏览器每次请求发送到服务器端
初识cookie
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>初识 Cookie</title>
</head>
<body>
<script>
// 1.Cookie 是什么
// Cookie 全称 HTTP Cookie,简称 Cookie
// 是浏览器存储数据的一种方式
// 因为存储在用户本地,而不是存储在服务器上,是本地存储
// 一般会自动随着浏览器每次请求发送到服务器端
// 2.Cookie 有什么用
// 利用 Cookie 跟踪统计用户访问该网站的习惯,比如什么时间访问,访问了哪些页面,在每个网页的停留时间等
// 3.在浏览器中操作 Cookie
// 不要在 Cookie 中保存密码等敏感信息
</script>
</body>
</html>
cookie的基本用法
- 写入cookie
document.cookie = 'username=alex;max-age=5';
document.cookie = 'age=18;domain=.imooc.com;max-age=5';
- 读取cookie
document.cookie; //username=alex;age=18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Cookie 的基本用法</title>
</head>
<body>
<script>
// 1.写入 Cookie
// document.cookie = 'username=zs';
// document.cookie = 'age=18';
// 不能一起设置,只能一个一个设置
// document.cookie = 'username=zs; age=18';
// 2.读取 Cookie
console.log(document.cookie);
// 读取的是一个由名值对构成的字符串,每个名值对之间由“; ”(一个分号和一个空格)隔开
// username=zs; age=18
</script>
</body>
</html>
Cookie 的属性
- 名称( Name)和值(Value)
- 创建 Cookie 时必须填写,其它属性可以使用默认值
- 如果包含非英文字母,写入时要用 encodeURIComponent() 编码
- 如果写入时编码,读取时要用 decodeURIComponent() 解码
- 失效(到期)时间
- 失效的 Cookie,会被浏览器清除
- 如果没有设置失效时间,默认会话结束后,Cookie 会被清除
- 想 Cookie 长时间存在,可以设置 Expires 或 Max-Age
- Expires 值为 Date 类型,表示具体什么时间过期
- Max-Age 值为数字,表示当前时间加多少秒后过期,单位是秒
- 如果设置 Max-Age 的值是 0 或负数,则 Cookie 会被删除
- Domain
- Domain 限定了访问 Cookie 的范围(不同域下)
- 使用 JS 只能写入/读取当前域或父域的 Cookie,无法写入/读取其他域的 Cookie
- Path
- Path 限定了访问 Cookie 的范围(同域不同路径下)
- 使用 JS 只能写入/读取当前路径和上级路径的 Cookie,无法写入/读取其他路径的 Cookie
- 当 Name、Domain、Path 这 3 个字段都相同的时候,才是同一个 Cookie
- HttpOnly
- 前端不能通过 JS 去设置一个 HttpOnly 类型的 Cookie,这种类型的 Cookie 只能是后端来设置
- 只要是 HttpOnly 类型的,通过 document.cookie 是获取不到的,也不能进行修改
- Secure
- Secure 限定了只有在使用了 https 而不是 http 的情况下才可以发送给服务端
- Domain、Path、Secure 都要满足条件,还不能过期的 Cookie才能随着请求发送到服务器端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Cookie 的属性</title>
</head>
<body>
<script>
// 1.Cookie 的名称(Name)和值(Value)
// 最重要的两个属性,创建 Cookie 时必须填写,其它属性可以使用默认值
// Cookie 的名称或值如果包含非英文字母,则写入时需要使用 encodeURIComponent() 编码,读取时使用 decodeURIComponent() 解码
document.cookie = 'username=alex';
document.cookie = `username=${encodeURIComponent('张三')}`;
document.cookie = `${encodeURIComponent('用户名')}=${encodeURIComponent(
'张三'
)}`;
// 一般名称使用英文字母,不要用中文,值可以用中文,但是要编码
// 2.失效(到期)时间
// 对于失效的 Cookie,会被浏览器清除
// 如果没有设置失效(到期)时间,这样的 Cookie 称为会话 Cookie
// 它存在内存中,当会话结束,也就是浏览器关闭时,Cookie 消失
document.cookie = 'username=alex';
// 想长时间存在,设置 Expires 或 Max-Age
// expires
// 值为 Date 类型
document.cookie = `username=alex; expires=${new Date(
'2100-1-01 00:00:00'
)}`;
// max-age
// 值为数字,表示当前时间 + 多少秒后过期,单位是秒
document.cookie = 'username=alex; max-age=5';
document.cookie = `username=alex; max-age=${24 * 3600 * 30}`;
// 如果 max-age 的值是 0 或负数,则 Cookie 会被删除
document.cookie = 'username=alex';
document.cookie = 'username=alex; max-age=0';
document.cookie = 'username=alex; max-age=-1';
// 3.Domain 域
// Domain 限定了访问 Cookie 的范围(不同域名)
// 使用 JS 只能读写当前域或父域的 Cookie,无法读写其他域的 Cookie
document.cookie = 'username=alex; domain=www.imooc.com';
// www.imooc.com m.imooc.com 当前域
// 父域:.imooc.com
// 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
// 5.HttpOnly
// 设置了 HttpOnly 属性的 Cookie 不能通过 JS 去访问
// 6.Secure 安全标志
// Secure 限定了只有在使用了 https 而不是 http 的情况下才可以发送给服务端
// Domain、Path、Secure 都要满足条件,还不能过期的 Cookie 才能随着请求发送到服务器端
</script>
</body>
</html>
cookie的封装
// 写入 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='
};
// 通过 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;
};
// 'username=alex; age=18; sex=male'
// ["username=alex", "age=18", "sex=male"]
// ["username","alex"]
// get('用户名');
// 根据 name、domain 和 path 删除 Cookie
const remove = (name, { domain, path } = {}) => {
set(name, '', { domain, path, maxAge: -1 });
};
export { set, get, remove };
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Cookie 的封装</title>
</head>
<body>
<button id="cn">中文</button>
<button id="en">英文</button>
<script type="module">
import { set, get, remove } from './cookie.js';
import { set, get, remove } from './cookie.js';
set('username', 'alex');
set('username', 'zs');
set('age', 18);
set('用户名', '张三');
set('sex', 'male', {
maxAge: 30 * 24 * 3600
});
remove('username');
remove('用户名');
console.log(get('username'));
console.log(get('age'));
console.log(get('用户名'));
console.log(get('sex'));
// 使用封装好的 Cookie 实现网站语言切换
import { set } from './cookie.js';
const cnBtn = document.getElementById('cn');
const enBtn = document.getElementById('en');
cnBtn.addEventListener(
'click',
() => {
set('language', 'cn', {
maxAge: 30 * 24 * 3600
});
window.location = './2-6.Cookie 的封装.html';
},
false
);
enBtn.addEventListener(
'click',
() => {
set('language', 'en', {
maxAge: 30 * 24 * 3600
});
window.location = './2-6.Cookie 的封装.html';
},
false
);
</script>
</body>
</html>
cookie注意事项
- 前后端都可以写入和获取 Cookie
- 每个域名下的 Cookie 数量有限
- 每个 Cookie 的存储容量很小,最多只有 4KB 左右
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Cookie 的注意事项</title>
</head>
<body>
<script>
// 1.前后端都可以创建 Cookie
// 2.Cookie 有数量限制
// 每个域名下的 Cookie 数量有限
// 当超过单个域名限制之后,再设置 Cookie,浏览器就会清除以前设置的 Cookie
// 3.Cookie 有大小限制
// 每个 Cookie 的存储容量很小,最多只有 4KB 左右
</script>
</body>
</html>
localStorage
- 浏览器存储数据的一种方式
- 存储在用户本地,不会发送到服务器端
- 单个域名下的总大小有限制(一般最大 5M左右)
- setItem()
- localStorage.setItem(‘username’:‘alex’);
- getItem()
- localStorage.getItem(‘username’);
- removeItem()
- localStorage.removeItem()
- clear()
- localStorage.clear()
- length
- localStorage.length
初识 localStorage
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>初识 localStorage</title>
</head>
<body>
<form id="login" action="https://www.imooc.com" method="post">
<input type="text" name="username" />
<input type="password" name="password" />
<input type="submit" id="btn" value="登录" />
</form>
<script>
// 1.localStorage 是什么
// localStorage 也是一种浏览器存储数据的方式(本地存储),它只是存储在本地,不会发送到服务器端
// 单个域名下的 localStorage 总大小有限制
// 2.在浏览器中操作 localStorage
// 3.localStorage 的基本用法
console.log(localStorage);
// // setItem()
localStorage.setItem('username', 'alex');
localStorage.setItem('username', 'zs');
localStorage.setItem('age', 18);
localStorage.setItem('sex', 'male');
// // length
console.log(localStorage.length);
// // getItem()
console.log(localStorage.getItem('username'));
console.log(localStorage.getItem('age'));
// 获取不存在的返回 null
console.log(localStorage.getItem('name'));
// // removeItem()
localStorage.removeItem('username');
localStorage.removeItem('age');
// // // 删除不存在的 key,不报错
// // localStorage.removeItem('name');
// // clear()
localStorage.clear();
console.log(localStorage);
// 4.使用 localStorage 实现自动填充
const loginForm = document.getElementById('login');
const btn = document.getElementById('btn');
const username = localStorage.getItem('username');
if (username) {
loginForm.username.value = username;
}
btn.addEventListener(
'click',
e => {
e.preventDefault();
// 数据验证
// console.log(loginForm.username.value);
localStorage.setItem('username', loginForm.username.value);
loginForm.submit();
},
false
);
</script>
</body>
</html>
localStorage 的注意事项
- localStorage 存储的数据,除非手动清除,否则永远存在
- sessionStorage 存储的数据,当会话结束时就会被清除
- localStorage 存储的键和值只能是字符串类型
- 不同的域名不能共用 localStorage
- IE7及以下版本不支持 localStorage,IE8 开始支持
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>localStorage 的注意事项</title>
</head>
<body>
<script>
// 1.localStorage 的存储期限
// localStorage 是持久化的本地存储,除非手动清除(比如通过 js 删除,或者清除浏览器缓存),否则数据是永远不会过期的
// sessionStorage
// 当会话结束(比如关闭浏览器)的时候,sessionStorage 中的数据会被清空
sessionStorage.setItem('username', 'alex');
sessionStorage.getItem('username');
sessionStorage.removeItem('username');
sessionStorage.clear();
// 2.localStorage 键和值的类型
// localStorage 存储的键和值只能是字符串类型
// 不是字符串类型,也会先转化成字符串类型再存进去
localStorage.setItem({}, 18);
// localStorage.setItem('students', [{},{}]);
console.log(
typeof localStorage.getItem('[object Object]'),
localStorage.getItem('[object Object]')
);
// console.log({}.toString()); //[object Object]
// 3.不同域名下能否共用 localStorage
// 不同的域名是不能共用 localStorage 的
// 4.localStorage 的兼容性
// IE7及以下版本不支持 localStorage,IE8 开始支持
// caniuse.com
</script>
</body>
</html>
Ajax&Fetch跨域请求
Ajax基础
初识Ajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>初识 Ajax</title>
</head>
<body>
<script>
// 1.Ajax 是什么
// Ajax 是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的简写
// Ajax 中的异步:可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己的事情。直到成功获取响应后,浏览器才开始处理响应数据
// XML(可扩展标记语言)是前后端数据通信时传输数据的一种格式
// XML 现在已经不怎么用了,现在比较常用的是 JSON
// Ajax 其实就是浏览器与服务器之间的一种异步通信方式
// 使用 Ajax 可以在不重新加载整个页面的情况下,对页面的某部分进行更新
// ① 慕课网注册检测
// ② 慕课网搜索提示
// 2.搭建 Ajax 开发环境
// Ajax 需要服务器环境,非服务器环境下,很多浏览器无法正常使用 Ajax
// Live Server
// windows phpStudy
// Mac MAMP
</script>
</body>
</html>
Ajax基本用法
// 1.XMLHttpRequest
console.log(Ajax);
// Ajax 想要实现浏览器与服务器之间的异步通信,需要依靠 XMLHttpRequest,它是一个构造函数
// 不论是 XMLHttpRequest,还是 Ajax,都没有和具体的某种数据格式绑定
// 2.Ajax 的使用步骤
// 2.1.创建 xhr 对象
const xhr = new XMLHttpRequest();
// 2.2.监听事件,处理响应
// 当获取到响应后,会触发 xhr 对象的 readystatechange 事件,可以在该事件中对响应进行处理
xhr.addEventListener('readystatechange', () => {}, fasle);
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
// HTTP CODE
// 获取到响应后,响应的内容会自动填充 xhr 对象的属性
// xhr.status:HTTP 200 404
// xhr.statusText:HTTP 状态说明 OK Not Found
if ((xhr.status >= 200) & (xhr.status < 300) || xhr.status === 304) {
// console.log('正常使用响应数据');
console.log(xhr.responseText);
}
};
// readystatechange 事件也可以配合 addEventListener 使用,不过要注意,IE6~8 不支持 addEventListener
// 为了兼容性,readystatechange 中不使用 this,而是直接使用 xhr
// 由于兼容性的原因,最好放在 open 之前
// readystatechange 事件监听 readyState 这个状态的变化
// 它的值从 0 ~ 4,一共 5 个状态
// 0:未初始化。尚未调用 open()
// 1:启动。已经调用 open(),但尚未调用 send()
// 2:发送。已经调用 send(),但尚未接收到响应
// 3:接收。已经接收到部分响应数据
// 4:完成。已经接收到全部响应数据,而且已经可以在浏览器中使用了
// 2.3.准备发送请求
xhr.open(
'HTTP 方法 GET、POST、PUT、DELETE',
'地址 URL https://www.imooc.com/api/http/search/suggest?words=js ./index.html ./index.xml ./index.txt',
true
);
// 调用 open 并不会真正发送请求,而只是做好发送请求前的准备工作
// 2.3.发送请求
// 调用 send() 正式发送请求
// send() 的参数是通过请求体携带的数据
xhr.send(null);
// 3.使用 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);
}
};
xhr.open('GET', url, true);
xhr.send(null);
Get请求
// 1.携带数据
// GET 请求不能通过请求体携带数据,但可以通过请求头携带
const url =
'https://www.imooc.com/api/http/search/suggest?words=js&username=alex&age=18';
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);
}
};
xhr.open('GET', url, true);
xhr.send(null);
// 不会报错,但不会发送数据
xhr.send('sex=male');
// 2.数据编码
// 如果携带的数据是非英文字母的话,比如说汉字,就需要编码之后再发送给后端,不然会造成乱码问题
// 可以使用 encodeURIComponent() 编码
const url = `https://www.imooc.com/api/http/search/suggest?words=${encodeURIComponent(
'前端'
)}`;
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);
}
};
xhr.open('GET', url, true);
xhr.send(null);
POST请求
// 1.携带数据
// POST 请求主要通过请求体携带数据,同时也可以通过请求头携带
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);
}
};
xhr.open('POST', url, true);
// 如果想发送数据,直接写在 send() 的参数位置,一般是字符串
// xhr.send('username=alex&age=18');
// 不能直接传递对象,需要先将对象转换成字符串的形式
xhr.send({
username: 'alex',
age: 18
});
// [object Object]
// 2.数据编码
xhr.send(`username=${encodeURIComponent('张三')}&age=18`);
JSON
初识JSON
// 1.JSON 是什么
// Ajax 发送和接收数据的一种格式
// XML
// username=alex&age=18
// JSON
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);
}
};
xhr.open('GET', url, true);
xhr.send(null);
// {"code":200,"data":[{"word":"jsp"},{"word":"js"},{"word":"json"},{"word":"js \u5165\u95e8"},{"word":"jstl"}]}
// HTML/XML
// JSON 全称是 JavaScript Object Notation
// 2.为什么需要 JSON
// JSON 有 3 种形式,每种形式的写法都和 JS 中的数据类型很像,可以很轻松的和 JS 中的数据类型互相转换
// JS->JSON->PHP/Java
// PHP/Java->JSON->JS
JSON的三种形式
// 1.简单值形式
// .json
// JSON 的简单值形式就对应着 JS 中的基础数据类型
// 数字、字符串、布尔值、null
// 注意事项
// ① JSON 中没有 undefined 值
// ② JSON 中的字符串必须使用双引号
// ③ JSON 中是不能注释的
// 2.对象形式
// JSON 的对象形式就对应着 JS 中的对象
// 注意事项
// JSON 中对象的属性名必须用双引号,属性值如果是字符串也必须用双引号
// JSON 中只要涉及到字符串,就必须使用双引号
// 不支持 undefined
// 3.数组形式
// JSON 的数组形式就对应着 JS 中的数组
// [1, "hi", null]
// 注意事项
// 数组中的字符串必须用双引号
// JSON 中只要涉及到字符串,就必须使用双引号
// 不支持 undefined
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);
}
};
// xhr.open('GET', './plain.json', true); //"str"
// xhr.open('GET', './obj.json', true);
xhr.open('GET', './arr.json', true);
xhr.send(null);
array.json
[
{
"id": 1,
"username": "张三",
"comment": "666"
},
{
"id": 2,
"username": "李四",
"comment": "999 6翻了"
}
]
obj.json
{
"name": "张三",
"age": 18,
"hobby": ["足球", "乒乓球"],
"family": {
"father": "张老大",
"mother": "李四"
}
}
JSON的常用方法
// 1.JSON.parse()
// JSON.parse() 可以将 JSON 格式的字符串解析成 JS 中的对应值
// 一定要是合法的 JSON 字符串,否则会报错
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);
console.log(JSON.parse(xhr.responseText));
console.log(JSON.parse(xhr.responseText).data);
}
};
xhr.open('GET', './plain.json', true);
xhr.open('GET', './obj.json', true);
xhr.open('GET', './arr.json', true);
xhr.open(
'GET',
'https://www.imooc.com/api/http/search/suggest?words=js',
true
);
xhr.send(null);
// 2.JSON.stringify()
// JSON.stringify() 可以将 JS 的基本数据类型、对象或者数组转换成 JSON 格式的字符串
console.log(
JSON.stringify({
username: 'alex',
age: 18
})
);
const xhr = new XMLHttpRequest();
xhr.open(
'POST',
'https://www.imooc.com/api/http/search/suggest?words=js',
true
);
xhr.send(
JSON.stringify({
username: 'alex',
age: 18
})
);
// 3.使用 JSON.parse() 和 JSON.stringify() 封装 localStorage
import { get, set, remove, clear } from './storage.js';
set('username', 'alex');
console.log(get('username'));
set('zs', {
name: '张三',
age: 18
});
console.log(get('zs'));
remove('username');
clear();
storge.js
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 };
跨域
初识跨域
// 1.跨域是什么
// 同域,不是跨域
// const url = './index.html';
// http://127.0.0.1:5500
// 不同域,跨域,被浏览器阻止
// const url = 'https://www.imooc.com';
// const xhr = new XMLHttpRequest();
// Access to XMLHttpRequest at 'https://www.imooc.com/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
// 向一个域发送请求,如果要请求的域和当前域是不同域,就叫跨域
// 不同域之间的请求,就是跨域请求
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return;
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.responseText);
}
};
xhr.open('GET', url, true);
xhr.send(null);
// 2.什么是不同域,什么是同域
// https(协议)://www.imooc.com(域名):443(端口号)/course/list(路径)
// 协议、域名、端口号,任何一个不一样,就是不同域
// 与路径无关,路径一不一样无所谓
// 不同域
// https://www.imooc.com:443/course/list
// http://www.imooc.com:80/course/list
// http://www.imooc.com:80/course/list
// http://m.imooc.com:80/course/list
// http://imooc.com:80/course/list
// 同域
// http://imooc.com:80
// http://imooc.com:80/course/list
// 3.跨域请求为什么会被阻止
// 阻止跨域请求,其实是浏览器本身的一种安全策略--同源策略
// 其他客户端或者服务器都不存在跨域被阻止的问题
// 4.跨域解决方案
// ① CORS 跨域资源共享
// ② JSONP
// 优先使用 CORS 跨域资源共享,如果浏览器不支持 CORS 的话,再使用 JSONP
CORS 跨域资源共享
// 1.CORS 是什么 由后端负责 前端不用书写
const url = 'https://www.imooc.com';
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);
}
};
xhr.open('GET', url, true);
xhr.send(null);
// Access-Control-Allow-Origin: *
// 表明允许所有的域名来跨域请求它,* 是通配符,没有任何限制
// 只允许指定域名的跨域请求
// Access-Control-Allow-Origin: http://127.0.0.1:5500
// 2.使用 CORS 跨域的过程
// ① 浏览器发送请求
// ② 后端在响应头中添加 Access-Control-Allow-Origin 头信息
// ③ 浏览器接收到响应
// ④ 如果是同域下的请求,浏览器不会额外做什么,这次前后端通信就圆满完成了
// ⑤ 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
// ⑥ 如果允许跨域,通信圆满完成
// ⑦ 如果没找到或不包含想要跨域的域名,就丢弃响应结果
// 3.CORS 的兼容性
// IE10 及以上版本的浏览器可以正常使用 CORS
// https://caniuse.com/
JSONP跨域资源共享
// 1.JSONP 的原理
// script 标签跨域不会被浏览器阻止
// JSONP 主要就是利用 script 标签,加载跨域文件
// 2.使用 JSONP 实现跨域
// 服务器端准备好 JSONP 接口
// https://www.imooc.com/api/http/jsonp?callback=handleResponse
// 动态加载 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);
};
<!--手动加载 JSONP 接口-->
<<script>
// 声明函数
const handleResponse = data => {
console.log(data);
};
</script>
<script src="https://www.imooc.com/api/http/jsonp?callback=handleResponse"></script>
//相当于
<script>
handleResponse({
code: 200,
data: [
{
word: 'jsp'
},
{
word: 'js'
},
{
word: 'json'
},
{
word: 'js 入门'
},
{
word: 'jstl'
}
]
});
</script>
XHR对象
XHR属性
// 1.responseType 和 response 属性
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) {
// 文本形式的响应内容
// responseText 只能在没有设置 responseType 或者 responseType = '' 或 'text' 的时候才能使用
// console.log('responseText:', xhr.responseText);
// 可以用来替代 responseText
console.log('response:', xhr.response);
// console.log(JSON.parse(xhr.responseText));
}
};
xhr.open('GET', url, true);
// xhr.responseType = '';
// xhr.responseType = 'text';
xhr.responseType = 'json';
xhr.send(null);
// IE6~9 不支持,IE10 开始支持
// 2.timeout 属性
// 设置请求的超时时间(单位 ms)
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.response);
}
};
xhr.open('GET', url, true);
xhr.timeout = 10000; //单位ms
xhr.send(null);
// IE6~7 不支持,IE8 开始支持
// 3.withCredentials 属性
// 指定使用 Ajax 发送请求时是否携带 Cookie
// 使用 Ajax 发送请求,默认情况下,同域时,会携带 Cookie;跨域时,不会
// xhr.withCredentials = true;
// 最终能否成功跨域携带 Cookie,还要看服务器同不同意
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'; //跨域
// const url = './index.html'; //同域
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return;
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response);
}
};
xhr.open('GET', url, true);
xhr.withCredentials = true;
xhr.send(null);
// IE6~9 不支持,IE10 开始支持
XHR的方法
// 1.abort()
// 终止当前请求
// 一般配合 abort 事件一起使用
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.response);
}
};
xhr.open('GET', url, true);
xhr.send(null);
xhr.abort();
// 2.setRequestHeader()
// 可以设置请求头信息
// xhr.setRequestHeader(头部字段的名称, 头部字段的值);
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
// const url = 'https://www.imooc.com/api/http/json/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.response);
}
};
xhr.open('POST', url, true);
// 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// xhr.setRequestHeader('Content-Type', 'application/json');
// xhr.send(null);
xhr.send('username=alex&age=18');
// xhr.send(
// JSON.stringify({
// username: 'alex'
// })
XHR的事件
// 1.load 事件
// 响应数据可用时触发相当于onreadystatechange
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
const xhr = new XMLHttpRequest();
// xhr.onload = () => {
// if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// console.log(xhr.response);
// }
// };
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response);
}
},
false
);
xhr.open('GET', url, true);
xhr.send(null);
// IE6~8 不支持 load 事件
// 2.error 事件
// 请求发生错误时触发
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
const url = 'https://www.iimooc.com/api/http/search/suggest?words=js';
const xhr = new XMLHttpRequest();
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response);
}
},
false
);
xhr.addEventListener(
'error',
() => {
console.log('error');
},
false
);
xhr.open('GET', url, true);
xhr.send(null);
// IE10 开始支持
// 3.abort 事件
// 调用 abort() 终止请求时触发
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
const xhr = new XMLHttpRequest();
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response);
}
},
false
);
xhr.addEventListener(
'abort',
() => {
console.log('abort');
},
false
);
xhr.open('GET', url, true);
xhr.send(null);
xhr.abort();
// IE10 开始支持
// 4.timeout 事件
// 请求超时后触发
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
const xhr = new XMLHttpRequest();
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response);
}
},
false
);
xhr.addEventListener(
'timeout',
() => {
console.log('timeout');
},
false
);
xhr.open('GET', url, true);
xhr.timeout = 10;
xhr.send(null);
// IE8 开始支持
Ajax进阶
FormData
<!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>
// 1.使用 Ajax 提交表单
// 2.FormData 的基本用法
// 通过 HTML 表单元素创建 FormData 对象
// const fd = new FormData(表单元素);
const login = document.getElementById('login');
const fd = new FormData(login);
xhr.send(fd);
// 3.通过 append() 方法添加数据
const fd = new FormData(表单元素);
fd.append('age', 18);
fd.append('sex', 'male');
xhr.send(fd);
const login = document.getElementById('login');
console.log(login.username);
console.log(login.password);
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 = `username=${username.value}&password=${password.value}`;
xhr.setRequestHeader(
'Content-Type',
'application/x-www-form-urlencoded' //相当于Form表单的形式
);
xhr.send(data); // 相当于xhr.send('username=alex&password=12345');
},
false
);
// 使用formdata简单化组件的封装
xhr.addEventListener(
'load',
() => {
if (
(xhr.status >= 200 && xhr.status < 300) ||
xhr.status === 304
) {
console.log(xhr.response);
}
},
false
);
xhr.open('POST', url, true);
// FormData 可用于发送表单数据
const data = new FormData(login);
data.append('age', 18);
data.append('sex', 'male');
console.log(data);
for (const item of data) {
console.log(item);
}
xhr.send(data);
},
false
);
</script>
</body>
</html>
封装Ajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>封装 Ajax</title>
</head>
<body>
<script type="module">
// 常规写法
// const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
// const xhr = new XMLHttpRequest();
// xhr.addEventListener(
// 'load',
// () => {
// if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// console.log(xhr.responseText);
// }
// },
// false
// );
// xhr.open('GET', url, true);
// xhr.send(null);
// 封装Ajax的写法
import { ajax, get, getJSON, post } from './ajax/index.js';
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
// const url = 'https://www.iimooc.com/api/http/search/suggest?words=js';
// const url = './414.html';
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>
</body>
</html>
index.js
import Ajax from './ajax.js';
const ajax = (url, options) => {
return new Ajax(url, options).getXHR();
};
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 };
constants.js
// 常量
export const HTTP_GET = 'GET';
export const CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded';
export const CONTENT_TYPE_JSON = 'application/json';
utils.js
// 工具函数
// 数据序列化成 urlencoded 格式的字符串
const serialize = param => {
const results = [];
for (const [key, value] of Object.entries(param)) {
results.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
}
// ['username=alex', 'age=18'];
return results.join('&');
};
// 数据序列化成 JSON 格式的字符串
const serializeJSON = param => {
return JSON.stringify(param);
};
// 给 URL 添加参数
// www.imooc.com?words=js&
const addURLData = (url, data) => {
if (!data) return '';
const mark = url.includes('?') ? '&' : '?';
return `${mark}${data}`;
};
export { serialize, addURLData, serializeJSON };
defaults.js
// 常量
import { HTTP_GET, CONTENT_TYPE_FORM_URLENCODED } from './constants.js';
// 默认参数
const DEFAULTS = {
method: HTTP_GET,
// 请求头携带的数据
params: null,
// params: {
// username: 'alex',
// age: 18
// }
// username=alex&age=18
// 请求体携带的数据
data: null,
// data: {
// username: 'alex',
// age: 18
// }
// data: FormData 数据
contentType: CONTENT_TYPE_FORM_URLENCODED,
responseType: '',
timeoutTime: 0,
withCredentials: false,
// 方法
success() {},
httpCodeError() {},
error() {},
abort() {},
timeout() {}
};
export default DEFAULTS;
ajax.js
// 常量
import {
HTTP_GET,
CONTENT_TYPE_FORM_URLENCODED,
CONTENT_TYPE_JSON
} from './constants.js';
// 工具函数
import { serialize, addURLData, serializeJSON } from './utils.js';
// 默认参数
import DEFAULTS from './defaults.js';
// Ajax 类
class Ajax {
constructor(url, options) {
this.url = url;
this.options = Object.assign({}, DEFAULTS, options);
// 初始化
this.init();
}
// 初始化
init() {
const xhr = new XMLHttpRequest();
this.xhr = xhr;
// 绑定响应事件处理程序
this.bindEvents();
xhr.open(this.options.method, this.url + this.addParam(), true);
// 设置 responseType
this.setResponseType();
// 设置跨域是否携带 cookie
this.setCookie();
// 设置超时
this.setTimeout();
// 发送请求
this.sendData();
}
// 绑定响应事件处理程序
bindEvents() {
const xhr = this.xhr;
const { success, httpCodeError, error, abort, timeout } = this.options;
// load
xhr.addEventListener(
'load',
() => {
if (this.ok()) {
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
);
}
// 检测响应的 HTTP 状态码是否正常
ok() {
const xhr = this.xhr;
return (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304;
}
// 在地址上添加数据
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;
if (!this.isSendData()) {
return xhr.send(null);
}
let resultData = null;
const { data } = this.options;
// 发送 FormData 格式的数据
if (this.isFormData()) {
resultData = data;
} else if (this.isFormURLEncodedData()) {
// 发送 application/x-www-form-urlencoded 格式的数据
this.setContentType(CONTENT_TYPE_FORM_URLENCODED);
resultData = serialize(data);
} else if (this.isJSONData()) {
// 发送 application/json 格式的数据
this.setContentType(CONTENT_TYPE_JSON);
resultData = serializeJSON(data);
} else {
// 发送其他格式的数据
this.setContentType();
resultData = data;
}
xhr.send(resultData);
}
// 是否需要使用 send 发送数据
isSendData() {
const { data, method } = this.options;
if (!data) return false;
if (method.toLowerCase() === HTTP_GET.toLowerCase()) return false;
return true;
}
// 是否发送 FormData 格式的数据
isFormData() {
return this.options.data instanceof FormData;
}
// 是否发送 application/x-www-form-urlencoded 格式的数据
isFormURLEncodedData() {
return this.options.contentType
.toLowerCase()
.includes(CONTENT_TYPE_FORM_URLENCODED);
}
// 是否发送 application/json 格式的数据
isJSONData() {
return this.options.contentType.toLowerCase().includes(CONTENT_TYPE_JSON);
}
// 设置 Content-Type
setContentType(contentType = this.options.contentType) {
if (!contentType) return;
this.xhr.setRequestHeader('Content-Type', contentType);
}
// 获取 XHR 对象
getXHR() {
return this.xhr;
}
}
export default Ajax;
使用 Promise 改造封装好的 Ajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>使用 Promise 改造封装好的 Ajax</title>
</head>
<body>
<script type="module">
import { ajax, get, getJSON, post } from './ajax/index.js';
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
// const url = 'https://www.iimooc.com/api/http/search/suggest?words=js';
const p = getJSON(url, {
params: { username: 'alex' },
data: { age: 18 }
// timeoutTime: 10
// success(){},error(){}
});
p.xhr.abort();
const { ERROR_HTTP_CODE, ERROR_REQUEST, ERROR_TIMEOUT, ERROR_ABORT } = p;
p.then(repsonse => {
console.log(repsonse);
}).catch(err => {
// console.log(err);
switch (err.type) {
case ERROR_HTTP_CODE:
console.log(err.text);
break;
case ERROR_REQUEST:
console.log(err.text);
break;
case ERROR_TIMEOUT:
console.log(err.text);
break;
case ERROR_ABORT:
console.log(err.text);
break;
}
});
</script>
</body>
</html>
index.js
import Ajax from './ajax.js';
// 常量
import {
ERROR_HTTP_CODE,
ERROR_REQUEST,
ERROR_TIMEOUT,
ERROR_ABORT,
ERROR_HTTP_CODE_TEXT,
ERROR_REQUEST_TEXT,
ERROR_TIMEOUT_TEXT,
ERROR_ABORT_TEXT
} from './constants.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: `${ERROR_HTTP_CODE_TEXT}: ${status}`
});
},
error() {
reject({
type: ERROR_REQUEST,
text: ERROR_REQUEST_TEXT
});
},
abort() {
reject({
type: ERROR_ABORT,
text: ERROR_ABORT_TEXT
});
},
timeout() {
reject({
type: ERROR_TIMEOUT,
text: ERROR_TIMEOUT_TEXT
});
}
}
}).getXHR();
});
p.xhr = xhr;
p.ERROR_HTTP_CODE = ERROR_HTTP_CODE;
p.ERROR_REQUEST = ERROR_REQUEST;
p.ERROR_TIMEOUT = ERROR_TIMEOUT;
p.ERROR_ABORT = ERROR_ABORT;
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 };
constants.js
// 常量
export const HTTP_GET = 'GET';
export const CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded';
export const CONTENT_TYPE_JSON = 'application/json';
export const ERROR_HTTP_CODE = 1;
export const ERROR_HTTP_CODE_TEXT = 'HTTP 状态码异常';
export const ERROR_REQUEST = 2;
export const ERROR_REQUEST_TEXT = '请求被阻止';
export const ERROR_TIMEOUT = 3;
export const ERROR_TIMEOUT_TEXT = '请求超时';
export const ERROR_ABORT = 4;
export const ERROR_ABORT_TEXT = '请求终止';
utils.js、defaults.js、ajax.js都一致
Ajax应用
搜索提示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>搜索提示</title>
</head>
<body>
<input id="search" type="text" />
<ul id="result"></ul>
<script type="module">
import { getJSON } from './ajax/index.js';
const searchInput = document.getElementById('search');
const resultList = document.getElementById('result');
const url = 'https://www.imooc.com/api/http/search/suggest?words=';
const handleInputEvent = () => {
if (searchInput.value.trim() !== '') {
getJSON(`${url}${searchInput.value}`)
.then(response => {
console.log(response);
// [{word: "jsp"}]
let html = '';
for (const item of response.data) {
html += `<li>${item.word}</li>`;
}
resultList.innerHTML = html;
resultList.style.display = '';
// resultList.innerHTML = '<li>jsp</li><li>js</li>';
})
.catch(err => {
console.log(err);
});
} else {
resultList.innerHTML = '';
resultList.style.display = 'none';
}
};
let timer = null;
// IE9 开始支持
searchInput.addEventListener(
'input',
() => {
// handleInputEvent();
if (timer) {
clearTimeout(timer);
}
// jsa
timer = setTimeout(handleInputEvent, 500);
},
false
);
</script>
</body>
</html>
二级菜单
<!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 请求的并发执行
<!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>
Ajax扩展
axios
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>axios</title>
</head>
<body>
<!-- <script src="https://unpkg.com/axios/dist/axios.min.js"></script> -->
<script src="https://unpkg.com/axios@0.19.2/dist/axios.min.js"></script>
<script>
// 1.axios 是什么
// axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中
// 第三方 Ajax 库
console.log(axios);
// http://www.axios-js.com/zh-cn/docs/
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
// 2.axios 的基本用法
axios(url)
.then(response => {
console.log(response);
})
.catch(err => {
console.log(err);
});
// 引入 axios
axios(url, {
method: 'post',
// 请求时的头信息
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
// 'Content-Type': 'application/json'
},
// 通过请求头携带的数据
params: {
username: 'alex'
},
// 通过请求体携带的数据
// application/json
// data: {
// age: 18,
// sex: 'male'
// }
// application/x-www-form-urlencoded
data: 'age=18&sex=male'
// timeout: 10
// withCredentials: true
})
.then(response => {
console.log(response);
console.log(response.data.data);
})
.catch(err => {
console.log(err);
});
// axios
// .get(url, {
// params: {
// username: 'alex'
// }
// })
// .then(response => {
// console.log(response);
// });
// axios //自动匹配Content-Type
// .post(url, 'username=alex&age=18')
// .then(response => {
// console.log(response);
// })
// .catch(err => {
// console.log(err);
// });
axios //自动匹配Content-Type,无需手动设置
.post('https://www.imooc.com/api/http/json/search/suggest?words=js', {
username: 'alex'
})
.then(response => {
console.log(response);
})
.catch(err => {
console.log(err);
});
// axios.put()
// axios.delete()
</script>
</body>
</html>
Fetch
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Fetch</title>
</head>
<body>
<script>
// 1.Fetch 是什么
// Fetch 也是前后端通信的一种方式
// Fetch 是 Ajax(XMLHttpRequest)的一种替代方案,它是基于 Promise 的
// Ajax 的兼容性比 Fetch 好
// abort timeout
// 2.Fetch 的基本用法
// console.log(fetch);
// console.log(ajax);
// fetch() 调用后返回 Promise 对象
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
fetch(url) //使用fetch的默认值,默认使用get方法请求
.then(response => {
console.log(response);
// body/bodyUsed
// body 只能读一次,读过之后就不让再读了
// ok
// 如果为 true,表示可以读取数据,不用再去判断 HTTP 状态码了
if (response.ok) {
// console.log(response.json());
return response.json();
// return response.text();
} else {
throw new Error(`HTTP CODE 异常 ${response.status}`);
}
})
.then(data => {
console.log(data);
})
.catch(err => {
console.log(err);
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Fetch</title>
</head>
<body>
<script>
// fetch() 调用后返回 Promise 对象
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
// 第二个参数是对象,用来配置fetch,使用POST方法时不能使用请求头携带数据 只能使用请求体
const fd = new FormData();
fd.append('username', 'alex');
fetch(url, {
method: 'post',
// body: null
body: 'username=alex&age=18',
// body: JSON.stringify({ username: 'alex' }) //不可以直接使用对象,需要使用JSON.stringify()方法来将JS中的值转化为JSON中对应形式的字符串
// body: fd, //不需要设置Content-Type
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
// 'Content-Type': 'application/json'
},
mode: 'cors' //允许跨域 默认值
// credentials:'include' //跨域是否携带Cookies
})
.then(response => {
console.log(response);
if (response.ok) {
// console.log(response.json());
return response.json();
// return response.text();
} else {
throw new Error(`HTTP CODE 异常 ${response.status}`);
}
})
.then(data => {
console.log(data);
})
.catch(err => {
console.log(err);
});
</script>
</body>
</html>