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. 指定请求方式为GET
method: '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冲突,所以改成获取id
const 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书名内容如果为空,就提示书名不能为空并且return
if (bookname.value === '') {
alert('书名不能为空')
return
}
//1.6.2作者内容如果为空,就提示作者不能为空并且return
else if (author.value === '') {
alert('作者不能为空')
return
}
//1.6.3出版社内容如果为空,就提示出版社不能为空并且return
else 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.id
axios({
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.返回数据:datastr
return 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-20
appkey: '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];
// 文件上传 必须用到 FormData
const fd = new FormData();
// 把文件包起来
fd.append('avatar', file);
// ........使用 原生的ajax 把 formData 发送给后端 完成文件上传
// formdata 做文件上传 不需要设置 content-type
const 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));
});
}
//版本1
ajax({
type: 'get',
url: 'http://www.itcbc.com:3006/api/getbooks',
success(result) {
console.log('第一次获取成功,准备页面渲染');
console.log(result);
},
});
//版本2
ajax({
type: 'get',
url: 'http://www.itcbc.com:3006/api/getbooks',
data: 'bookname=钵仔糕的魔法&appkey=luxingniao',
success(result) {
console.log('第二次获取成功,准备页面渲染');
console.log(result);
},
});
//版本3
ajax({
type: 'post',
url: 'http://www.itcbc.com:3006/api/addbook',
data: 'bookname=钵仔糕的魔法&&author=小白&publisher=魔仙堡&appkey=luxingniao',
success(result) {
console.log('第三次获取成功,准备页面渲染');
console.log(result);
},
});
//版本4
ajax({
url: 'http://www.itcbc.com:3006/api/addbook',
type: 'post',
data: {
bookname: '小龙虾的魔法',
author: '小白',
publisher: '魔仙堡',
appkey: 'luxingniao',
},
success(result) {
console.log('第四次获取成功,准备页面渲染');
console.log(result);
},
})
//版本5
const 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开启倒计时6s
3在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>