ajax
服务器相关的基础概念
服务器
上网看到的内容,在网络当中的一台电脑上,我们称这台电脑为 服务器。
服务器的本质:也是一台电脑。
服务器的作用:
存储一个网站的文件(HTML、CSS、JS、图片、音乐…..)
提供网站的文件给用户
如何获得服务器
购买(京东、淘宝……)
租赁(阿里云、腾讯云……)
资源
服务器上的 网页(html文件)、图片、音乐、视频、字体文件、CSS文件、JS文件等等都称之为资源。所以资源代指服务器上存储的内容。
网页中的数据,也是服务器对外提供的一种资源。例如股票数据、各行业排行榜等。
客户端
概念:在前端开发中,客户端特指“Web 浏览器”。
作用:将互联网世界中的 Web 资源加载、并呈现到浏览器窗口中供用户使用。
URL 地址(统一资源定位符)
生活中的地址,表示地球上的一个确切的地理位置
北京市 顺义区 京顺路 99号
URL 地址,表示服务器上每个资源的确切位置。
http://www.baidu.com/index.html
服务器上的每个资源,都对应着独一无二的URL地址
http://www.xxx.com/index.html → index.html
http://www.xxx.com/css/test.css → test.css
http://www.xxx.com/images/logo.png → logo.png
数据也是服务器上的资源
对数据的操作(增删改查),也对应着不同的URL地址
http://www.itcbc.com:3006/api/getbooks → 获取图书
http://www.itcbc.com:3006/api/addbook →添加图书
http://www.itcbc.com:3006/api/delbook → 删除图书
http://www.itcbc.com:3006/api/updatebook →修改图书
客户端与服务器通信的过程
客户端与服务器之间的通信过程,分为请求 - 响应两个步骤。其中:
请求的概念:客户端通过网络去找服务器要资源的过程,叫做“请求”
响应的概念:服务器把资源通过网络发送给客户端的过程,叫做“响应”

Ajax
概念
它的英文全称是 Asynchronous Javascript And XML,简称 Ajax。
异步 js 和XML
异步就是不阻塞,与其他代码不影响
同步会造成阻塞,上一段代码没有执行完毕,不会让你执行下一段代码
XML 是数据格式,类似html
早期前端后端发送数据格式是xml
目前主流发送数据格式是json,优点是轻量级
Ajax 是浏览器中的技术:用来实现客户端网页请求服务器的数据。
网页中 Ajax 的应用场景无处不在,有数据的地方就有 Ajax!
请求方式
使用 Ajax 请求数据的 5 种方式
Ajax中,客户端浏览器在请求服务器上的数据时,根据操作性质(增删改查)的不同,可以分为以下 5 种常见的操作:
| 请求方式 | 描述 |
|---|---|
| POST | 向服务器新增数据 |
| GET | 从服务器获取数据 |
| DELETE | 删除服务器上的数据 |
| PUT | 更新服务器上的数据(侧重于完整更新:例如更新用户的完整信息) |
| PATCH | 更新服务器上的数据(侧重于部分更新:例如只更新用户的手机号) |
GET 请求
GET 请求用于从服务器获取数据:
一定包含params
?做为分割?前面url地址?后面是参数!!! 数据量多的时候也可用&分隔
axios({method: 'get',url: 'http://www.itcbc.com:3006/api/getbooks?id=43&a=1&b=2',}).then((result) => {console.log('数据请求成功');})
URL地址改为 ‘ /api/getbooks’:
axios({method: 'GET',url: 'http://www.itcbc.com:3006/api/getbooks'}).then((result) => {console.log(result)})
GET 请求的查询参数
只要写get请求,参数一定会放在params
刚才查询回来的是所有图书的列表数据,如果想指定查询的条件,可以通过 params 选项来指定查询的参数:
axios({// 1. 指定请求方式为GETmethod: 'GET',// 2. 指定请求的 URL 地址url: 'http://www.itcbc.com:3006/api/getbooks',// 3. 查询参数params: {id: 1}//表示获取id=1的图书}).then(result => {console.log(result)})
如果要携带多个参数,只需要在 params 对象中指定多个查询参数项即可。示例代码如下:
axios({method: 'GET',url: 'http://www.itcbc.com:3006/api/getbooks',// 查询参数params: {id: 1,bookname: 'love'}//表示获取 id=1 并且 bookname=love 的图书}).then(result => {console.log(result)})
POST 请求
POST 请求用于向服务器新增数据:
一定包含data
携带的参数不是放在url上直接看看不见!!!
请求体-后面会讲到
使用 axios 发起 POST 请求时,只需要将 method 属
性的值设置为 ‘POST’ ,URL地址改为
‘/api/delbook’:
只要写post请求,参数就会在data里面
<div><button>新增图书</button></div><table><thead><tr><th>id</th><th>书名</th><th>作者</th><th>出版社</th></tr></thead><tbody></tbody></table><script src="./lib/axios.js"></script><script>const button = document.querySelector('button')button.addEventListener('click', function () {axios({method: 'post',url: 'http://www.itcbc.com:3006/api/addbook',data: {bookname: "防脱发指南",author: "某地中海程序员",publisher: "地中海出版社",},}).then((result) => {console.log('数据请求成功');})})
form标签
1.form标签中出现了按钮,类型 type=submit 就会导致刷新行为
解决方法
1.移除form标签
2.让按钮加上type=button
3.建议在form标签事件sbumit中 阻止默认行为即可 e.preventDefault()
form作用
以前没有ajax的时候,想要给后端提交数据,只能form标签来提交-导致页面刷新
现在主要作用是快速获取表单数据
DELETE 请求
DELETE 请求用于删除服务器上的数据:
使用 axios 发起 DELETE 请求时,需要将 method 属
性的值设置为 ‘DELETE ‘ ,URL地址改为
‘/api/addbook’:
删除图书小案例
1.给删除按钮绑定点击事件
因为按钮是动态生成异步的不能直接去获取按钮
1.把获取按钮的代码写在请求成功-标签生成成功下面 lowBB
代码不清晰不直观
2.事件委托
(1).找到一个一直存在网页中的元素,给它绑定事件
(2).判断当前触发事件的元素是不是删除按钮
2事件触发…-根据接口文档的要求来构造参数
3根据接口文档要求请求方式请求的url请求参数4发起一个删除请求
5删除成功刷新一下页面(重新获取最新的数据-少一条刚才删除过的数据)
<script>function render() {axios({url: 'http://www.itcbc.com:3006/api/getbooks',method: 'GET',params: {appkey: "luxingniao",}}).then((result) => {const list = result.data.data;const html = list.map((value) => `<tr><td>${value.id}</td><td>${value.bookname}</td><td>${value.author}</td><td>${value.publisher}</td><td><button data-id="${value.id}">删除</button></td></tr>`).join('');// console.log(html);// 插入 tbody 中document.querySelector('tbody').innerHTML = html;});};//异步,不阻塞render()//通过事件委托的方式来绑定事件const tbody = document.querySelector('tbody')tbody.addEventListener('click', function (e) {if (e.target.tagName === 'BUTTON') {//id等于当前点击的删除按钮上自定义id,获取后通过它来进行删除const id = e.target.dataset.id// console.log('你点击的是删除按钮');axios({method: 'delete',url: 'http://www.itcbc.com:3006/api/delbook',params: {appkey: "luxingniao",id: id,}}).then(result => {console.log(result);render()})}})</script>
DELETE 请求用于删除服务器上的数据:
DELETE 请求之前:客户端有 name 为 ls 的这条数据。
DELETE 请求成功之后:服务器删除了 name 为 ls 的数据。
PUT 请求
PUT 请求用于更新服务器上的数据(侧重于完整更新:例如更新用户的完整信息)
PUT 请求之前:服务器上的数据为 ls 的信息。
PUT 请求成功之后:ls 的信息被完整地更新成了 jack 的信息。
使用 axios 发起 PUT 请求时,需要将 method 属
性的值设置为 ‘PUT ‘ ,URL地址改为
‘/api/updatebook ‘:
PATCH 请求
PATCH 请求用于更新服务器上的数据(侧重于部分更新:例如只更新用户的手机号)
PATCH 请求之前:服务器上 ls 的手机号为 136。
PATCH 请求成功之后:1s 的手机号被更新成了 139。
Ajax 基础用法
通过原生代码,完全可以实现Ajax请求
为了第一天学习方便,我们选择使用 axios 库
axios 是前端圈最火的、专注于数据请求的库。
在后面的 Vue、React 课程中都会用到 axios 来请求数据。
中文官网地址:https://www.axios-http.cn/
英文官网地址:https://www.npmjs.com/package/axios
基本语法:
axios({method: '请求的类型',url: '请求的URL地址'}).then((result) => {// then 用来指定请求成功之后的回调函数// 形参中的 result 是请求成功之后的结果})
appkey身份认证
服务器存储的图书,分为通用数据和个人数据。
默认获取、添加、删除、修改的都是通用数据。
在获取、添加、删除、修改时,如果带appkey参数,则表示使用个人数据。
appkey密钥使用方法
第一步,在添加书籍内增加属性:
data: {
bookname: "魔法少女屋",author: "小魔仙",publisher: "花园城堡",**appkey: "luxingniao",**},
第二步,在搜索的时候增加搜索条件:
axios({
url: '[http://www.itcbc.com:3006/api/getbooks](http://www.itcbc.com:3006/api/getbooks)',method: 'GET',params: {**appkey: "luxingniao",**}})
动态渲染表格小案例
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"><title>动态渲染页面.html</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}table {margin: 50px 0 0 50px;border-collapse: collapse;}table,th,td {border: 1px solid black;text-align: center;}</style></head><body><table><thead><tr><th>id</th><th>书名</th><th>作者</th><th>出版社</th></tr></thead><tbody></tbody></table><script src="./lib/axios.js"></script><script>axios({method: 'get',url: 'http://www.itcbc.com:3006/api/getbooks',}).then((result) => {console.log('数据请求成功');const list = result.data.data;// console.log(list);const html = list.map(item => `<tr><th>${item.id}</th><th>${item.bookname}</th><th>${item.author}</th><th>${item.publisher}</th></tr>`).join('');document.querySelector('tbody').innerHTML = html;})</script></body></html>
接口相关的基础概念
使用 Ajax 请求数据时,被请求的 URL 地址,就叫做数据接口(简称:接口或 API 接口)。
同时,每个接口必须有对应的请求方式。例如:
http://www.itcbc.com:3006/api/getbooks 获取图书列表的接口
(GET 请求)
http://www.itcbc.com:3006/api/addbook 添加图书的接口
(POST 请求)
接口文档就是接口的使用说明书,它是我们调用接口的依据。
| 组成部分 | 说明 |
|---|---|
| 接口名称 | 接口的名称,用来快速区分每个接口的作用。如:登录接口、添加图书接口 |
| 接口 URL | 客户端发起 Ajax 调用此接口时,请求的 URL 地址 |
| 请求方式 | 接口的请求方式,如:GET、POST、PUT、DELETE 等 |
| 请求参数 | 请求此接口时,需要发送到服务器的查询参数或请求体 |
| 返回示例 | 当接口请求成功后,服务器响应回来的数据的基本格式 |
| 返回参数说明 | 接口响应结果的详细描述 |
network面板
network,翻译为 “网络”
浏览器的开发者工具中,有一个面板为 network。(新版的chrome浏览器是中文版本的)
该工具可以抓取到所有的网络请求,当然包括ajax请求
我们可以使用该工具,查看当前Ajax请求的详细信息
查看请求方式
查看请求的URL地址
查看请求参数
查看响应结果
……
注:有关Ajax方面的错误,不应只查看 “Console”面板,必须通过 “Network”面板进行排查
form 表单
表单的三个组成部分
网页中采集数据的表单由三个部分组成,分别是:表单标签、表单域、表单按钮。
表单标签:
HTML 的 就是表单标签,它是一个“容器”,用来将页面上指定的区域划定为表单区域。
表单域:
表单域提供了采集用户信息的渠道,常见的表单域有:input、textarea、select 等。
表单按钮:
当表单数据填写完毕后,用户点击表单按钮,会触发表单的提交操作,从而把采集到的数据提交给服务器。
form表单的作用是采集数据。
把表单采集到的数据提交给服务器时,需要指定请求的方式与请求的 URL 地址。
通过 Ajax 提交表单数据
通过 Ajax 提交表单采集到的数据,可以防止表单默认提交行为导致的页面跳转问题,提高用户的体验。
jQuery 的 serialize() 函数
jQuery 的 serialize() 函数能够一次性获取到表单中采集的数据。
语法:$(‘表单元素的选择器’).serialize();
该方法 能够 获取 隐藏域的值。
在使用 serialize() 函数快速获取表单数据时,必须为每个表单域添加 name 属性。
const form = document.querySelector('form')/* 如果想要快速获取表单中的数据1 必须提供form标签2 form标签中的 input标签必须要有name属性3 利用jq的方法,快速获取 */form.addEventListener('submit', function (e) {e.preventDefault();// 使用jq的方法,快速获取表单的数据// 序列化 => 快速把数据 转成 字符串的能力// 反序列化 => 字符串 => 解析成原本的样子(对象 数组)const a = $('form').serialize();// console.log(a);// JSON.stringify({})// JSON.parse()// 反序列化const data = $('form').serialize() + "&appkey=luxingniao"axios({method: 'post',url: 'http://www.itcbc.com:3006/api/addbook',data: data}).then(result => {console.log(result);})})
axios 全局配置 和 拦截器
全局配置
在 url 地址中,协议://域名:端口 对应的部分叫做“请求根路径”。
例如:http://www.itcbc.com//是一个根路径
全局配置请求根路径的好处:
提高项目的可维护性。全局配置根路径后,后续所有的请求都可以使用全局配置的根路径
若端口号更改,没有全局配置根路径,则每个请求的URL中的端口号都需要被修改!
有全局配置根路径,则只需要修改全局配置即可
基于 axios 提供的固定配置,即可轻松配置请求的根路径。语法格式如下:
//axios.defaults.baseURL = '请求根路径'// 全局配置根路径axios.defaults.baseURL = 'http://www.itcbc.com:3006';// 请求接口时,不用写根路径axios.get('/api/get').then(result => {console.log(result);})// 请求接口时,不用写根路径axios.post('/api/post', { username: 'zhangsan', password: '123456'}).then(result => {console.log(result);})
拦截器
拦截器(interceptors)用来全局拦截 axios 的每一次请求与响应。
好处:可以把每个请求中,某些重复性的业务代码封装到拦截器中,提高代码的复用性。
使用拦截器实现 loading 效果:
在请求拦截器中展示:$(‘.loading-box’).show()
在响应拦截器中隐藏:$(‘.loading-box’).hide()
FormData和文件上传
FormData
FormData 是一个浏览器对象。用于管理表单数据。
IE10+支持。
可以这样理解,FormData的作用和 jQuery中的 serialize() 作用一样,用于快速收集表单数据
并且可以将创建的FormData对象直接提交给接口。
典型应用场景:FormData + Ajax 技术实现文件上传的功能。
<body>//准备空图片,用来接收用户上传的src信息<img src="" alt="">//file类型可以进行上传图片<input type="file" name="" id="">//引入 axios库<script src="./axios.js"></script><script>//获取按钮const input = document.querySelector('input')//绑定改变事件input.addEventListener('change', function () {// 获取加载到浏览器内存中的文件数组const file = input.files[0];// 获取到具体的一个文件// 创建一个formdata对象const fd = new FormData();// 使用fd来包装 文件 !!// key 写什么 需要看文档!!! 以接口文档为准!!fd.append('avatar', file)// append 是fd中添加数据固定写法!!// 根据接口文档要求 完成文件的上传!!axios({method: 'post',url: 'http://www.itcbc.com:3006/api/formdata',data: fd,// 把参数传递给data}).then((result) => {const filename = result.data.data.filename;document.querySelector('img').src = filename})})</script></body>
请求报文 & 响应报文
最主要的作用:容易了解和调试代码
客户端与服务器通信的过程是基于请求与响应的。
其中:
请求报文规定了客户端以什么格式把数据发送给服务器
响应报文规定了服务器以什么格式把数据响应给客户端
请求报文
由请求行(request line)、请求头部( header )、空行 和 请求体 4 个部分组成。


URL参数
常用的5种请求方式,都可以在URL后面携带请求参数。
由于URL的长度有限制,所以请求参数一般都比较小,比如不能做文件上传
常用的请求参数有两种写法
第一种格式的参数:/api/xxx?参数=值&参数=值
(这种格式的字符串叫做查询字符串,所以这样的参数叫做查询参数)
// 直接写成查询字符串,并拼接到url后面axios.get('/api/xxx?key=value&key=value')// 按照axios的语法写,axios会帮我们转成查询字符串axios.get('api/xxx', { params: { key: value, key: value } }
第二种格式的参数:/api/xxx/值/值 (Restful 风格的接口用这种格式的参数)
// 只能自己拼接axios.get('/api/xxx/100/zhangsan')
axios中,如何设置不同格式的请求体
第一种格式:参数=值&参数=值
请求体
//axios.post的第二个参数,直接用查询字符串
axios.post(‘/api/xxx’, ‘key=value&key=value’)
请求头
Content-Type: application/x-www-form-urlencoded
第二种格式: ‘{ “id”: 1, “name”: “zs” }’
请求体
// axios.post的第二个参数,使用对象。axios会将它转成JSON格式
axios.post(‘/api/xxx’, { id: 1, name: ‘zs’ })
请求头
Content-Type: application/json
第三种格式: new FormData()
请求体
let fd = new FormData();
// axios.post的第二个参数,直接使用 FormData 对象
axios.post(‘/api/formdata’, fd)
请求头
Content-Type: multipart/form-data; xxsd随机字符
注意:
在浏览器中,GET 请求比较特殊,它只有请求头,没有请求体。因为它的参数是直接拼接到url上的。
在浏览器中,POST、PUT、PATCH、DELETE 请求既有请求头,又有请求体。
响应报文
响应报文由状态行、响应头部、空行 和 响应体 4 个部分组成。
http 响应状态码
概念:http 响应状态码(Status Code)由三位数字组成,用来标识响应成功与否的状态。
作用:客户端浏览器根据响应状态码,即可判断出这次 http 请求是成功还是失败了。

| 状态码 | 状态码描述 | 说明 |
|---|---|---|
| 200 | OK | 请求成功。 |
| 201 | Created | 资源在服务器端已成功创建。 |
| 304 | Not Modified | 资源在客户端被缓存,响应体中不包含任何资源内容! |
| 400 | Bad Request | 客户端的请求方式、或请求参数有误导致的请求失败! |
| 401 | Unauthorized | 客户端的用户身份认证未通过,导致的此次请求失败! |
| 404 | Not Found | 客户端请求的资源地址错误,导致服务器无法找到资源! |
| 500 | Internal Server Error | 服务器内部错误,导致的本次请求失败! |
注:2开头表示成功,3开头表示重定向,4开头表示前端错误,5开头一般表示后端错误
业务状态码
正确区分响应状态码和业务状态码的不同,是保证使用 Ajax 不迷茫的必要前提。从如下 3 个方面进行区分:
所处的位置不同
响应状态码位置
在响应头的状态行中所包含的状态码,或者请求列表中的Status,叫做“响应状态码”

业务状态码位置
在响应体的数据中所包含的状态码(案例中叫做code),叫做“业务状态码”

表示的结果不同
响应状态码只能表示这次请求的成功与否
http状态码这次请求发送给了服务端不能说明这次的业务操作就是成功!
例:小黄目的是去找小白,小黄到了小白家,说明该次请求成功,但是不意味着能找到小白(业务是否成功)
业务状态码用来表示这次业务处理的成功与否
说明当前业务已经经过了请求→响应报文两个阶段,成功处理完成该次业务
例:小黄成功找到了小白
通用性不同
响应状态码是由 http 协议规定的,具有通用性。每个不同的状态码都有其标准的含义,不能乱用。所有公司都是一样的
响应状态码
2xx 成功。
3xx 重定向。
4xx 客户端错误。 可以检查一下自己的参数是否错误
5xx 服务器错误。 需要问一下
业务状态码是后端程序员自定义的,不具有通用性。
增删改查小案例
//1.1.设置全局配置axios.defaults.baseURL = 'http://www.itcbc.com:3006';//一、新增模块//1.2.获取包含form标签,为避免与其他form冲突,所以改成获取idconst form = document.querySelector('#form1')//1.3.获取书名,作者,出版社const bookname = document.querySelector('.bookname')const author = document.querySelector('.author')const publisher = document.querySelector('.publisher')//1.4.给form标签绑定提交事件form.addEventListener('submit', function (e) {//1.5.阻止按钮默认提交e.preventDefault();//1.6.进行非空判断//1.6.1书名内容如果为空,就提示书名不能为空并且returnif (bookname.value === '') {alert('书名不能为空')return}//1.6.2作者内容如果为空,就提示作者不能为空并且returnelse if (author.value === '') {alert('作者不能为空')return}//1.6.3出版社内容如果为空,就提示出版社不能为空并且returnelse if (publisher.value === '') {alert('出版社不能为空')return}//1.11.声明常量存储原生js方法获取的表单数据,后缀加上陆行鸟密钥const data = serialize('form') + "&appkey=luxingniao"//1.12.使用axios方法,post类型,url使用简写(开头已设置全局配置)axios({method: 'post',url: '/api/addbook',//1.13.data:data 第二个data为通过原生js方法获取的表单数据data: data}).then(result => {//1.14.返回值为三个文本域清空,重新渲染页面bookname.value = ''author.value = ''publisher.value = ''render()})})// 查询模块const button = document.querySelector('#btn');const inp1 = document.querySelector('#inp1');const btn = document.querySelector('#btn1');btn.addEventListener('click', function () {render()inp1.value = ''bookname.value = ''author.value = ''publisher.value = ''})button.addEventListener('click', function () {// 获取输入框的值const value = inp1.value;axios({method: 'get',url: '/api/getbooks',// 参数params: {bookname: value,appkey: "luxingniao",},}).then((result) => {const list = result.data.data;const html = list.map((item) => `<tr><td>${item.id}</td><td>${item.bookname}</td><td>${item.author}</td><td>${item.publisher}</td><td><button class="btn-update" data-id="${value.id}">修改</button><button class="btn-delete" data-id="${value.id}">删除</button></td></tr>` ).join('');document.querySelector('tbody').innerHTML = html;});});//渲染模块function render() {axios({method: 'GET',url: '/api/getbooks',params: {appkey: "luxingniao",}}).then((result) => {const list = result.data.data;const html = list.map((value) => `<tr><td>${value.id}</td><td>${value.bookname}</td><td>${value.author}</td><td>${value.publisher}</td><td><button class="btn-update" data-id="${value.id}">修改</button><button class="btn-delete" data-id="${value.id}">删除</button></td></tr>`).join('');// console.log(html);// 插入 tbody 中document.querySelector('tbody').innerHTML = html;})};//异步,不阻塞render()//通过事件委托的方式来绑定事件//删除模块,,获取编辑表单const tbody = document.querySelector('tbody')const form3 = document.querySelector('.modal-update form')tbody.addEventListener('click', function (e) {if (e.target.className === 'btn-delete') {const id = e.target.dataset.idaxios({method: 'delete',url: '/api/delbook',params: {appkey: "luxingniao",id: id,}}).then((result) => {console.log(result)render()})}//else if (e.target.className === 'btn-update') {// console.log('编辑');// document.querySelector()document.querySelector('.modal-update').style.display = 'flex';const id = e.target.dataset.id;axios({method: 'get',url: '/api/getbooks',params: {appkey: 'luxingniao',id,},}).then((result) => {console.log(result);const obj = result.data.data[0];for(const key in obj){document.querySelector(`.modal-update [name=${key}]`).value = obj[`${key}`];}});}})//绑定修改表单的提交事件form3.addEventListener('submit', function (e) {e.preventDefault();const datastr = $('.modal-update form').serialize();axios({method: 'put',url: '/api/updatebook',data: datastr,}).then(result => {document.querySelector('.modal-update').style.display = 'none';render();})})// 表单绑定重置事件(点击的type=reset button就触发)form3.addEventListener('reset',function(){document.querySelector('.modal-update').style.display = 'none';})//1.7.1封装一个原生js快速获取表单的方法function serialize(){// 1.7.2FormData 快速获取form标签的数据 (不是可以直接使用)// FormData 是特殊的对象 直接 打印 看不出东西的const fd =new FormData(form)// 1.8.URLSearchParams 把formdata数据 转成 a=1&b=2...const usp =new URLSearchParams(fd)//1.9. usp.toString,toString可以把对象转成字符串const datastr =usp.toString()//1.10.返回数据:datastrreturn datastr}
XMLHttpRequest
是浏览器内置的一个构造函数
作用:基于 new 出来的 XMLHttpRequest 实例对象,可以发起 Ajax 的请求。
axios 中的 axios.get()、axios.post()、axios() 方法,都是基于
XMLHttpRequest(简称:XHR) 封装出来的!
主要的 4 个关键实现步骤:
//创建 网络请求 对象let xhr = new XMLHttpRequest();//调用 xhr.open() 函数xhr.open('GET', 'http://xxx.com/api/xx');//调用 xhr.send() 函数xhr.send();//监听 load 事件 load事件+this.response即可拿到服务器响应数据xhr.addEventListener('load', function () {//返回的数据类型是json字符串类型,需要自行反序列化console.log(this.response);//response=响应的意思})
URL参数,只能拼接在 URL 地址 后面
请求体,放到 send() 里面
let xhr = new XMLHttpRequest();xhr.addEventListener('load', function () {console.log(this.response);})// 将请求参数拼接到url后面xhr.open('请求方式', 'http://www.itcbc.com/api/xx?id=1&username=zhangsan');xhr.send( 请求体 );
原生ajax实现方式
post 请求
传递的参数
1.只能放在send方法中
2.传递的参数的3种格式
2.1字符串的形式a=1&b=2&c=3
2.1.1声明对象再转成字符串进行传输
const data = {bookname: 'if的魔法',author: '小黄',publisher: '黑马前端',// 改为只有自己知道的key 长度 6-20appkey: 'luxingniao',};// 将对象 转成 字符串// URLSearchParams 不能用在ie上 全局覆没const purple = new URLSearchParams(data);const white = new XMLHttpRequest();white.open('post', 'http://www.itcbc.com:3006/api/addbook')white.send(purple);//post请求的参数 只能放在send方法内white.addEventListener('load', function () {console.log(this.response);})
2.1.2 直接传输字符串
const white = new XMLHttpRequest();white.open('post', 'http://www.itcbc.com:3006/api/addbook')//设置请求头white.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')const data = `bookname=钵仔糕的魔法&author=小白&publisher=黑马前端&appkey=luxingniao`white.send(data);//post请求的参数 只能放在send方法内white.addEventListener('load', function () {console.log(this.response);})
2.2 json的格式{ a:1,b:2}
const data = {bookname: 'if的魔法',author: '小白',publisher: '黑马前端',appkey: 'luxingniao',};const purple = new XMLHttpRequest();purple.open('POST', 'http://www.itcbc.com:3006/api/addbook');purple.setRequestHeader('content-type', 'application/json');//使用了json格式// 把对象转成 json字符串的格式 再去传递 ! - axios 没有让我转格式 - 它内部做了封装处理 !!purple.send(JSON.stringify(data));purple.addEventListener('load', function () {console.log(this.response);})
2.3 FormData的格式
<body><img src="" alt=""><input type="file" name="" id="" /><script>const input = document.querySelector('input');input.addEventListener('change', function () {// 获取 要上传的文件const file = this.files[0];// 文件上传 必须用到 FormDataconst fd = new FormData();// 把文件包起来fd.append('avatar', file);// ........使用 原生的ajax 把 formData 发送给后端 完成文件上传// formdata 做文件上传 不需要设置 content-typeconst purple = new XMLHttpRequest();purple.open('post', 'http://www.itcbc.com:3006/api/formdata');purple.send(fd);purple.addEventListener('load', function () {console.log(this.response);//因为返回的数据是json字符串,所以需要进行转换const result = JSON.parse(this.response)//修改src属性document.querySelector("img").src = result.data.filename;})})</script></body>
form补充
form标签的id属性= button的form属性做到 button放在form外面一样可以form的提交事件
1 form添加一个 id =”aa”
2 button添加一个 form = “aa”
3 解决 按钮必须放在form标签中的问题
<form action="" id="aa"></form><button form="aa">提交</button><script>document.querySelector('form').addEventListener('submit', function (e) {e.preventDefault()console.log(1);})</script>
<form action=""><input type="text" name="a" /><input type="text" name="b" /><input type="text" name="c" /><button type="reset">重置</button><div>点击我 也能重置 表单</div></form><script>const form = document.querySelector('form');const div = document.querySelector('div');div.addEventListener('click', function () {// form dom元素具有一个方法 重置form.reset();});</script>
原生ajax封装函数小案例
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport"content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no" /><title>08-封装ajax.html</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}</style></head><body><img src="" alt=""><input type="file" /><script>/*1 ajax 是一个什么样的数据 函数2 ajax 接收了一个参数-对象格式3 函数封装的心得1 固定不变的部分 放在函数中2 会变化的部分 通过形参 或者 条件判断来代替*///封装本体function ajax(purple) {const white = new XMLHttpRequest();const yellow = purple.data ? '?' + purple.data : ''if (purple.type === 'get') {white.open(purple.type, purple.url + yellow);white.send();} else if (purple.type === 'post') {white.open(purple.type, purple.url);if (typeof purple.data === 'string') {white.setRequestHeader('content-type','application/x-www-form-urlencoded');white.send(purple.data);} else if (typeof purple.data === 'object') {if (purple.data instanceof FormData) {white.send(purple.data);} else {white.setRequestHeader('content-type', 'application/json');white.send(JSON.stringify(purple.data))}}}white.addEventListener('load', function () {purple.success(JSON.parse(this.response));});}//版本1ajax({type: 'get',url: 'http://www.itcbc.com:3006/api/getbooks',success(result) {console.log('第一次获取成功,准备页面渲染');console.log(result);},});//版本2ajax({type: 'get',url: 'http://www.itcbc.com:3006/api/getbooks',data: 'bookname=钵仔糕的魔法&appkey=luxingniao',success(result) {console.log('第二次获取成功,准备页面渲染');console.log(result);},});//版本3ajax({type: 'post',url: 'http://www.itcbc.com:3006/api/addbook',data: 'bookname=钵仔糕的魔法&&author=小白&publisher=魔仙堡&appkey=luxingniao',success(result) {console.log('第三次获取成功,准备页面渲染');console.log(result);},});//版本4ajax({url: 'http://www.itcbc.com:3006/api/addbook',type: 'post',data: {bookname: '小龙虾的魔法',author: '小白',publisher: '魔仙堡',appkey: 'luxingniao',},success(result) {console.log('第四次获取成功,准备页面渲染');console.log(result);},})//版本5const input = document.querySelector('input');input.addEventListener('change', function () {const file = this.files[0];const purple = new FormData();purple.append('avatar', file);const white = new XMLHttpRequest();ajax({send: purple,url: 'http://www.itcbc.com:3006/api/formdata',type: 'post',data: purple,// 函数的声明 函数的定义!!success(result) {console.log(result);console.log('第五次获取成功,准备渲染页面图片');document.querySelector("img").src = result.data.filename;},});});</script></body></html>
同源策略 & 跨域
什么是同源(面试会出)
同源指的是两个URL地址具有相同的协议、主机名、端口号。
注意:一般情况下,端口默认是80 只有80可以省略
例如,下表给出了相对于 http://www.test.com/index.html 页面的 5 个同源检测结果:
| URL | 是否同源 | 原因说明 |
|---|---|---|
| http://www.test.com/other.html | 是 | 同源(协议、域名、端口相同) |
| https://www.test.com/about.html | 否 | 协议不同(http 与 https) |
| http://blog.test.com/movie.html | 否 | 域名不同(www.test.com 与 blog.test.com) |
| http://www.test.com:7001/home.html | 否 | 端口不同(默认的 80 端口与 7001 端口) |
| http://www.test.com:80/main.html | 是 | 同源(协议、域名、端口相同) |
什么是同源策略
同源策略(英文全称Same origin policy)是浏览器提供的一个安全功能。
浏览器的同源策略规定:不允许非同源的URL之间进行资源的交互。

注意:可以发请求,但是会报错!
什么是跨域
同源指的是两个 URL 的协议、主机名、端口号完全一致,反之,则是跨域。
出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。例如:
网页:http://www.test.com/index.html
接口:http://www.api.com/userlist
受到同源策略的限制,上面的网页请求下面的接口会失败!
浏览器对跨域请求的拦截过程
浏览器允许发起跨域请求。但跨域请求回来的数据,会被浏览器拦截,无法被页面获取到!示意图如下:

突破浏览器跨域限制的两种方案
JSONP 和 CORS 是实现跨域数据请求的两种技术方案。
| 方案 | 诞生的时间 | 方案来源 | 优点 | 缺点 |
|---|---|---|---|---|
| JSONP | 出现较早 | 民间(非官方) | 兼容性好(兼容低版本 IE) | 仅支持 GET 请求 |
| CORS | 出现较晚 | W3C 官方标准 | 支持 GET、POST、PUT、DELETE、PATCH等常见的请求方式 | 不兼容某些低版本浏览器 |
注意:目前 JSONP 在实际开发中很少会用到,CORS 是跨域的主流技术解决方案。
CORS 的概念
CORS 是解决跨域数据请求的终极解决方案,全称是 Cross-origin resource sharing。(跨域资源共享)
CORS 技术需要浏览器和服务器同时支持,二者缺一不可:
浏览器要支持 CORS 功能(主流的浏览器全部支持,IE 不能低于 IE10)
服务器要开启 CORS 功能(需要后端开发者为接口开启 CORS 功能)
CORS 是真正的 Ajax 请求,支持 GET、POST、DELETE、PUT、PATCH 等这些常见的 Ajax 请求方式
只需要后端开启 CORS 功能即可,前端的代码无须做任何改动
服务器端通过 Access-Control-Allow-Origin 响应头,来告诉浏览器当前的 API 接口是否允许跨域请求。

什么是 JSONP
JSONP 是实现跨域数据请求的一种技术解决方案。它只支持 GET 请求,不支持 POST、DELETE 等其它请求。
在实际开发中很少被使用
在解决跨域问题时:
CORS 方案用到了 XMLHttpRequest 对象,发起的是纯正的 Ajax 请求
JSONP 方案没有用到 XMLHttpRequest 对象,因此,JSONP 不是真正的 Ajax 技术
防抖 & 节流
防抖
防抖(debounce)指的是:频繁触发某个操作时,只执行最后一次
防抖的应用场景
场景:搜索框只在输入完后,才执行查询的请求。
好处:这样可以有效减少请求的次数,节省网络资源。
核心代码:
<script>// 一、延时器的妙用// 二、 过程 华为手机// 1按下按键华开启延时器 1.5s后再执行// 2.在1.5s内按下为// (1)先清空旧延时器(搜索华)// (2)同时又开启了一个新的延时器(搜索华为) 1.5s// 3. 在1.5s内 按下手// (1)先清空旧的延时器(搜索 华为)// (2)同时 又开启了一个新的延时器// (3)当超过1.5s未输入数据即把当前输入的内容发送请求,可以减小服务器压力// 三、发现// 代码 用新的输入来阻止旧的输入let timeid;const input = document.querySelector('input');input.addEventListener(' input', function(){clearTimeout(timeid);timeid = setTimeout(()=>{//修改这个业务console.log('获取关键字去发送网络请求',this.value);//修改这个业务},1500);});</script>
节流
节流(throttle)指的是:单位时间内,频繁触发同一个操作,只会触发 1 次。
核心代码:
<button>发送小白的魔法</button><script>/*1点击发送验证码按钮2开启倒计时6s3在6s内重复点击无效!! button.disabled=true(1)禁用按钮4在6s过后允许重新点击(1)开启按钮*/const button = document.querySelector('button')button.addEventListener('click', function () {button.disabled = true// console.log('3 开始发送小白的魔法');//2.开启倒计时let num = 6//开启定时器let timeId = setInterval(() => {num--;console.log(num);if (num === 0) {//关闭定时器clearInterval(timeId);button.disabled = false}}, 1000)})</script>
