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 - 图1

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地址?后面是参数!!! 数据量多的时候也可用&分隔

  1. axios({
  2. method: 'get',
  3. url: 'http://www.itcbc.com:3006/api/getbooks?id=43&a=1&b=2',
  4. }).then((result) => {
  5. console.log('数据请求成功');
  6. })

URL地址改为 ‘ /api/getbooks’:

  1. axios({
  2. method: 'GET',
  3. url: 'http://www.itcbc.com:3006/api/getbooks'
  4. }).then((result) => {
  5. console.log(result)
  6. })

GET 请求的查询参数

只要写get请求,参数一定会放在params

刚才查询回来的是所有图书的列表数据,如果想指定查询的条件,可以通过 params 选项来指定查询的参数:

  1. axios({
  2. // 1. 指定请求方式为GET
  3. method: 'GET',
  4. // 2. 指定请求的 URL 地址
  5. url: 'http://www.itcbc.com:3006/api/getbooks',
  6. // 3. 查询参数
  7. params: {
  8. id: 1
  9. }//表示获取id=1的图书
  10. }).then(result => {
  11. console.log(result)
  12. })

如果要携带多个参数,只需要在 params 对象中指定多个查询参数项即可。示例代码如下:

  1. axios({
  2. method: 'GET',
  3. url: 'http://www.itcbc.com:3006/api/getbooks',
  4. // 查询参数
  5. params: {
  6. id: 1,
  7. bookname: 'love'
  8. }//表示获取 id=1 并且 bookname=love 的图书
  9. }).then(result => {
  10. console.log(result)
  11. })

POST 请求

POST 请求用于向服务器新增数据:

一定包含data

携带的参数不是放在url上直接看看不见!!!
请求体-后面会讲到

使用 axios 发起 POST 请求时,只需要将 method 属

性的值设置为 ‘POST’ ,URL地址改为

‘/api/delbook’:

只要写post请求,参数就会在data里面

  1. <div>
  2. <button>新增图书</button>
  3. </div>
  4. <table>
  5. <thead>
  6. <tr>
  7. <th>id</th>
  8. <th>书名</th>
  9. <th>作者</th>
  10. <th>出版社</th>
  11. </tr>
  12. </thead>
  13. <tbody>
  14. </tbody>
  15. </table>
  16. <script src="./lib/axios.js"></script>
  17. <script>
  18. const button = document.querySelector('button')
  19. button.addEventListener('click', function () {
  20. axios({
  21. method: 'post',
  22. url: 'http://www.itcbc.com:3006/api/addbook',
  23. data: {
  24. bookname: "防脱发指南",
  25. author: "某地中海程序员",
  26. publisher: "地中海出版社",
  27. },
  28. }).then((result) => {
  29. console.log('数据请求成功');
  30. })
  31. })

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删除成功刷新一下页面(重新获取最新的数据-少一条刚才删除过的数据)

  1. <script>
  2. function render() {
  3. axios({
  4. url: 'http://www.itcbc.com:3006/api/getbooks',
  5. method: 'GET',
  6. params: {
  7. appkey: "luxingniao",
  8. }
  9. }).then((result) => {
  10. const list = result.data.data;
  11. const html = list.map((value) => `
  12. <tr>
  13. <td>${value.id}</td>
  14. <td>${value.bookname}</td>
  15. <td>${value.author}</td>
  16. <td>${value.publisher}</td>
  17. <td><button data-id="${value.id}">删除</button></td>
  18. </tr>
  19. `).join('');
  20. // console.log(html);
  21. // 插入 tbody 中
  22. document.querySelector('tbody').innerHTML = html;
  23. });
  24. };//异步,不阻塞
  25. render()
  26. //通过事件委托的方式来绑定事件
  27. const tbody = document.querySelector('tbody')
  28. tbody.addEventListener('click', function (e) {
  29. if (e.target.tagName === 'BUTTON') {
  30. //id等于当前点击的删除按钮上自定义id,获取后通过它来进行删除
  31. const id = e.target.dataset.id
  32. // console.log('你点击的是删除按钮');
  33. axios({
  34. method: 'delete',
  35. url: 'http://www.itcbc.com:3006/api/delbook',
  36. params: {
  37. appkey: "luxingniao",
  38. id: id,
  39. }
  40. }).then(result => {
  41. console.log(result);
  42. render()
  43. })
  44. }
  45. })
  46. </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

基本语法:

  1. axios({
  2. method: '请求的类型',
  3. url: '请求的URL地址'
  4. }).then((result) => {
  5. // then 用来指定请求成功之后的回调函数
  6. // 形参中的 result 是请求成功之后的结果
  7. })

appkey身份认证

服务器存储的图书,分为通用数据个人数据
默认获取、添加、删除、修改的都是通用数据。
在获取、添加、删除、修改时,如果带appkey参数,则表示使用个人数据。

appkey密钥使用方法

第一步,在添加书籍内增加属性:

data: {

  1. bookname: "魔法少女屋",
  2. author: "小魔仙",
  3. publisher: "花园城堡",
  4. **appkey: "luxingniao",**
  5. },

第二步,在搜索的时候增加搜索条件:

axios({

  1. url: '[http://www.itcbc.com:3006/api/getbooks](http://www.itcbc.com:3006/api/getbooks)',
  2. method: 'GET',
  3. params: {
  4. **appkey: "luxingniao",**
  5. }
  6. })

动态渲染表格小案例

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport"
  6. content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no">
  7. <title>动态渲染页面.html</title>
  8. <style>
  9. * {
  10. margin: 0;
  11. padding: 0;
  12. box-sizing: border-box;
  13. }
  14. table {
  15. margin: 50px 0 0 50px;
  16. border-collapse: collapse;
  17. }
  18. table,
  19. th,
  20. td {
  21. border: 1px solid black;
  22. text-align: center;
  23. }
  24. </style>
  25. </head>
  26. <body>
  27. <table>
  28. <thead>
  29. <tr>
  30. <th>id</th>
  31. <th>书名</th>
  32. <th>作者</th>
  33. <th>出版社</th>
  34. </tr>
  35. </thead>
  36. <tbody>
  37. </tbody>
  38. </table>
  39. <script src="./lib/axios.js"></script>
  40. <script>
  41. axios({
  42. method: 'get',
  43. url: 'http://www.itcbc.com:3006/api/getbooks',
  44. }).then((result) => {
  45. console.log('数据请求成功');
  46. const list = result.data.data;
  47. // console.log(list);
  48. const html = list.map(item => `<tr>
  49. <th>${item.id}</th>
  50. <th>${item.bookname}</th>
  51. <th>${item.author}</th>
  52. <th>${item.publisher}</th>
  53. </tr>`).join('');
  54. document.querySelector('tbody').innerHTML = html;
  55. })
  56. </script>
  57. </body>
  58. </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 属性

  1. const form = document.querySelector('form')
  2. /* 如果想要快速获取表单中的数据
  3. 1 必须提供form标签
  4. 2 form标签中的 input标签必须要有name属性
  5. 3 利用jq的方法,快速获取 */
  6. form.addEventListener('submit', function (e) {
  7. e.preventDefault();
  8. // 使用jq的方法,快速获取表单的数据
  9. // 序列化 => 快速把数据 转成 字符串的能力
  10. // 反序列化 => 字符串 => 解析成原本的样子(对象 数组)
  11. const a = $('form').serialize();
  12. // console.log(a);
  13. // JSON.stringify({})
  14. // JSON.parse()// 反序列化
  15. const data = $('form').serialize() + "&appkey=luxingniao"
  16. axios({
  17. method: 'post',
  18. url: 'http://www.itcbc.com:3006/api/addbook',
  19. data: data
  20. }).then(result => {
  21. console.log(result);
  22. })
  23. })

axios 全局配置 和 拦截器

全局配置

在 url 地址中,协议://域名:端口 对应的部分叫做“请求根路径”。

例如:http://www.itcbc.com//是一个根路径

全局配置请求根路径的好处

提高项目的可维护性。全局配置根路径后,后续所有的请求都可以使用全局配置的根路径

若端口号更改,没有全局配置根路径,则每个请求的URL中的端口号都需要被修改!
有全局配置根路径,则只需要修改全局配置即可

基于 axios 提供的固定配置,即可轻松配置请求的根路径。语法格式如下:

  1. //axios.defaults.baseURL = '请求根路径'
  2. // 全局配置根路径
  3. axios.defaults.baseURL = 'http://www.itcbc.com:3006';
  4. // 请求接口时,不用写根路径
  5. axios.get('/api/get').then(result => {
  6. console.log(result);
  7. })
  8. // 请求接口时,不用写根路径
  9. axios.post('/api/post', { username: 'zhangsan', password: '123456'}).then(result => {
  10. console.log(result);
  11. })

拦截器

拦截器(interceptors)用来全局拦截 axios 的每一次请求响应
好处:可以把每个请求中,某些重复性的业务代码封装到拦截器中,提高代码的复用性。

使用拦截器实现 loading 效果:

在请求拦截器中展示:$(‘.loading-box’).show()

在响应拦截器中隐藏:$(‘.loading-box’).hide()

FormData和文件上传

FormData

FormData 是一个浏览器对象。用于管理表单数据。
IE10+支持
可以这样理解,FormData的作用和 jQuery中的 serialize() 作用一样,用于快速收集表单数据
并且可以将创建的FormData对象直接提交给接口。
典型应用场景:FormData + Ajax 技术实现文件上传的功能。

  1. <body>
  2. //准备空图片,用来接收用户上传的src信息
  3. <img src="" alt="">
  4. //file类型可以进行上传图片
  5. <input type="file" name="" id="">
  6. //引入 axios库
  7. <script src="./axios.js"></script>
  8. <script>
  9. //获取按钮
  10. const input = document.querySelector('input')
  11. //绑定改变事件
  12. input.addEventListener('change', function () {
  13. // 获取加载到浏览器内存中的文件数组
  14. const file = input.files[0];// 获取到具体的一个文件
  15. // 创建一个formdata对象
  16. const fd = new FormData();
  17. // 使用fd来包装 文件 !!
  18. // key 写什么 需要看文档!!! 以接口文档为准!!
  19. fd.append('avatar', file)// append 是fd中添加数据固定写法!!
  20. // 根据接口文档要求 完成文件的上传!!
  21. axios({
  22. method: 'post',
  23. url: 'http://www.itcbc.com:3006/api/formdata',
  24. data: fd,// 把参数传递给data
  25. }).then((result) => {
  26. const filename = result.data.data.filename;
  27. document.querySelector('img').src = filename
  28. })
  29. })
  30. </script>
  31. </body>

请求报文 & 响应报文

最主要的作用:容易了解和调试代码

客户端与服务器通信的过程是基于请求响应的。

其中:

请求报文规定了客户端以什么格式把数据发送给服务器
响应报文规定了服务器以什么格式把数据响应给客户端

请求报文

请求行(request line)、请求头部( header )、空行请求体 4 个部分组成。

ajax - 图2

ajax - 图3

URL参数

常用的5种请求方式,都可以在URL后面携带请求参数。
由于URL的长度有限制,所以请求参数一般都比较小,比如不能做文件上传
常用的请求参数有两种写法
第一种格式的参数:/api/xxx?参数=值&参数=值

(这种格式的字符串叫做查询字符串,所以这样的参数叫做查询参数)

  1. // 直接写成查询字符串,并拼接到url后面
  2. axios.get('/api/xxx?key=value&key=value')
  3. // 按照axios的语法写,axios会帮我们转成查询字符串
  4. axios.get('api/xxx', { params: { key: value, key: value } }

第二种格式的参数:/api/xxx/值/值 (Restful 风格的接口用这种格式的参数)

  1. // 只能自己拼接
  2. 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 请求是成功还是失败了。

ajax - 图4

状态码 状态码描述 说明
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,叫做“响应状态码”

ajax - 图5

业务状态码位置

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

ajax - 图6

表示的结果不同

响应状态码只能表示这次请求的成功与否

http状态码这次请求发送给了服务端不能说明这次的业务操作就是成功!

例:小黄目的是去找小白,小黄到了小白家,说明该次请求成功,但是不意味着能找到小白(业务是否成功)

业务状态码用来表示这次业务处理的成功与否

说明当前业务已经经过了请求→响应报文两个阶段,成功处理完成该次业务

例:小黄成功找到了小白

通用性不同

响应状态码是由 http 协议规定的,具有通用性。每个不同的状态码都有其标准的含义,不能乱用。所有公司都是一样的

响应状态码

2xx 成功。
3xx 重定向。
4xx 客户端错误。 可以检查一下自己的参数是否错误
5xx 服务器错误。 需要问一下

业务状态码是后端程序员自定义的不具有通用性。

增删改查小案例

  1. //1.1.设置全局配置
  2. axios.defaults.baseURL = 'http://www.itcbc.com:3006';
  3. //一、新增模块
  4. //1.2.获取包含form标签,为避免与其他form冲突,所以改成获取id
  5. const form = document.querySelector('#form1')
  6. //1.3.获取书名,作者,出版社
  7. const bookname = document.querySelector('.bookname')
  8. const author = document.querySelector('.author')
  9. const publisher = document.querySelector('.publisher')
  10. //1.4.给form标签绑定提交事件
  11. form.addEventListener('submit', function (e) {
  12. //1.5.阻止按钮默认提交
  13. e.preventDefault();
  14. //1.6.进行非空判断
  15. //1.6.1书名内容如果为空,就提示书名不能为空并且return
  16. if (bookname.value === '') {
  17. alert('书名不能为空')
  18. return
  19. }
  20. //1.6.2作者内容如果为空,就提示作者不能为空并且return
  21. else if (author.value === '') {
  22. alert('作者不能为空')
  23. return
  24. }
  25. //1.6.3出版社内容如果为空,就提示出版社不能为空并且return
  26. else if (publisher.value === '') {
  27. alert('出版社不能为空')
  28. return
  29. }
  30. //1.11.声明常量存储原生js方法获取的表单数据,后缀加上陆行鸟密钥
  31. const data = serialize('form') + "&appkey=luxingniao"
  32. //1.12.使用axios方法,post类型,url使用简写(开头已设置全局配置)
  33. axios({
  34. method: 'post',
  35. url: '/api/addbook',
  36. //1.13.data:data 第二个data为通过原生js方法获取的表单数据
  37. data: data
  38. }).then(result => {
  39. //1.14.返回值为三个文本域清空,重新渲染页面
  40. bookname.value = ''
  41. author.value = ''
  42. publisher.value = ''
  43. render()
  44. })
  45. })
  46. // 查询模块
  47. const button = document.querySelector('#btn');
  48. const inp1 = document.querySelector('#inp1');
  49. const btn = document.querySelector('#btn1');
  50. btn.addEventListener('click', function () {
  51. render()
  52. inp1.value = ''
  53. bookname.value = ''
  54. author.value = ''
  55. publisher.value = ''
  56. })
  57. button.addEventListener('click', function () {
  58. // 获取输入框的值
  59. const value = inp1.value;
  60. axios({
  61. method: 'get',
  62. url: '/api/getbooks',
  63. // 参数
  64. params: {
  65. bookname: value,
  66. appkey: "luxingniao",
  67. },
  68. }).then((result) => {
  69. const list = result.data.data;
  70. const html = list
  71. .map(
  72. (item) => `
  73. <tr>
  74. <td>${item.id}</td>
  75. <td>${item.bookname}</td>
  76. <td>${item.author}</td>
  77. <td>${item.publisher}</td>
  78. <td>
  79. <button class="btn-update" data-id="${value.id}">修改</button>
  80. <button class="btn-delete" data-id="${value.id}">删除</button>
  81. </td>
  82. </tr>
  83. ` )
  84. .join('');
  85. document.querySelector('tbody').innerHTML = html;
  86. });
  87. });
  88. //渲染模块
  89. function render() {
  90. axios({
  91. method: 'GET',
  92. url: '/api/getbooks',
  93. params: {
  94. appkey: "luxingniao",
  95. }
  96. }).then((result) => {
  97. const list = result.data.data;
  98. const html = list.map((value) => `
  99. <tr>
  100. <td>${value.id}</td>
  101. <td>${value.bookname}</td>
  102. <td>${value.author}</td>
  103. <td>${value.publisher}</td>
  104. <td>
  105. <button class="btn-update" data-id="${value.id}">修改</button>
  106. <button class="btn-delete" data-id="${value.id}">删除</button>
  107. </td>
  108. </tr>
  109. `).join('');
  110. // console.log(html);
  111. // 插入 tbody 中
  112. document.querySelector('tbody').innerHTML = html;
  113. })
  114. };//异步,不阻塞
  115. render()
  116. //通过事件委托的方式来绑定事件
  117. //删除模块,,获取编辑表单
  118. const tbody = document.querySelector('tbody')
  119. const form3 = document.querySelector('.modal-update form')
  120. tbody.addEventListener('click', function (e) {
  121. if (e.target.className === 'btn-delete') {
  122. const id = e.target.dataset.id
  123. axios({
  124. method: 'delete',
  125. url: '/api/delbook',
  126. params: {
  127. appkey: "luxingniao",
  128. id: id,
  129. }
  130. }).then((result) => {
  131. console.log(result)
  132. render()
  133. })
  134. }
  135. //
  136. else if (e.target.className === 'btn-update') {
  137. // console.log('编辑');
  138. // document.querySelector()
  139. document.querySelector('.modal-update').style.display = 'flex';
  140. const id = e.target.dataset.id;
  141. axios({
  142. method: 'get',
  143. url: '/api/getbooks',
  144. params: {
  145. appkey: 'luxingniao',
  146. id,
  147. },
  148. }).then((result) => {
  149. console.log(result);
  150. const obj = result.data.data[0];
  151. for(const key in obj){
  152. document.querySelector(`.modal-update [name=${key}]`).value = obj[`${key}`];
  153. }
  154. });
  155. }
  156. })
  157. //绑定修改表单的提交事件
  158. form3.addEventListener('submit', function (e) {
  159. e.preventDefault();
  160. const datastr = $('.modal-update form').serialize();
  161. axios({
  162. method: 'put',
  163. url: '/api/updatebook',
  164. data: datastr,
  165. }).then(result => {
  166. document.querySelector('.modal-update').style.display = 'none';
  167. render();
  168. })
  169. })
  170. // 表单绑定重置事件(点击的type=reset button就触发)
  171. form3.addEventListener('reset',function(){
  172. document.querySelector('.modal-update').style.display = 'none';
  173. })
  174. //1.7.1封装一个原生js快速获取表单的方法
  175. function serialize(){
  176. // 1.7.2FormData 快速获取form标签的数据 (不是可以直接使用)
  177. // FormData 是特殊的对象 直接 打印 看不出东西的
  178. const fd =new FormData(form)
  179. // 1.8.URLSearchParams 把formdata数据 转成 a=1&b=2...
  180. const usp =new URLSearchParams(fd)
  181. //1.9. usp.toString,toString可以把对象转成字符串
  182. const datastr =usp.toString()
  183. //1.10.返回数据:datastr
  184. return datastr
  185. }

XMLHttpRequest

是浏览器内置的一个构造函数

作用:基于 new 出来的 XMLHttpRequest 实例对象,可以发起 Ajax 的请求
axios 中的 axios.get()、axios.post()、axios() 方法,都是基于

XMLHttpRequest(简称:XHR) 封装出来的!

主要的 4 个关键实现步骤:

  1. //创建 网络请求 对象
  2. let xhr = new XMLHttpRequest();
  3. //调用 xhr.open() 函数
  4. xhr.open('GET', 'http://xxx.com/api/xx');
  5. //调用 xhr.send() 函数
  6. xhr.send();
  7. //监听 load 事件 load事件+this.response即可拿到服务器响应数据
  8. xhr.addEventListener('load', function () {
  9. //返回的数据类型是json字符串类型,需要自行反序列化
  10. console.log(this.response);//response=响应的意思
  11. })

URL参数,只能拼接在 URL 地址 后面
请求体,放到 send() 里面

  1. let xhr = new XMLHttpRequest();
  2. xhr.addEventListener('load', function () {
  3. console.log(this.response);
  4. })
  5. // 将请求参数拼接到url后面
  6. xhr.open('请求方式', 'http://www.itcbc.com/api/xx?id=1&username=zhangsan');
  7. xhr.send( 请求体 );

原生ajax实现方式

post 请求

传递的参数

1.只能放在send方法

2.传递的参数的3种格式

2.1字符串的形式a=1&b=2&c=3

2.1.1声明对象再转成字符串进行传输

  1. const data = {
  2. bookname: 'if的魔法',
  3. author: '小黄',
  4. publisher: '黑马前端',
  5. // 改为只有自己知道的key 长度 6-20
  6. appkey: 'luxingniao',
  7. };
  8. // 将对象 转成 字符串
  9. // URLSearchParams 不能用在ie上 全局覆没
  10. const purple = new URLSearchParams(data);
  11. const white = new XMLHttpRequest();
  12. white.open('post', 'http://www.itcbc.com:3006/api/addbook')
  13. white.send(purple);//post请求的参数 只能放在send方法内
  14. white.addEventListener('load', function () {
  15. console.log(this.response);
  16. })

2.1.2 直接传输字符串

  1. const white = new XMLHttpRequest();
  2. white.open('post', 'http://www.itcbc.com:3006/api/addbook')
  3. //设置请求头
  4. white.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
  5. const data = `bookname=钵仔糕的魔法&author=小白&publisher=黑马前端&appkey=luxingniao`
  6. white.send(data);//post请求的参数 只能放在send方法内
  7. white.addEventListener('load', function () {
  8. console.log(this.response);
  9. })

2.2 json的格式{ a:1,b:2}
  1. const data = {
  2. bookname: 'if的魔法',
  3. author: '小白',
  4. publisher: '黑马前端',
  5. appkey: 'luxingniao',
  6. };
  7. const purple = new XMLHttpRequest();
  8. purple.open('POST', 'http://www.itcbc.com:3006/api/addbook');
  9. purple.setRequestHeader('content-type', 'application/json');//使用了json格式
  10. // 把对象转成 json字符串的格式 再去传递 ! - axios 没有让我转格式 - 它内部做了封装处理 !!
  11. purple.send(JSON.stringify(data));
  12. purple.addEventListener('load', function () {
  13. console.log(this.response);
  14. })

2.3 FormData的格式
  1. <body>
  2. <img src="" alt="">
  3. <input type="file" name="" id="" />
  4. <script>
  5. const input = document.querySelector('input');
  6. input.addEventListener('change', function () {
  7. // 获取 要上传的文件
  8. const file = this.files[0];
  9. // 文件上传 必须用到 FormData
  10. const fd = new FormData();
  11. // 把文件包起来
  12. fd.append('avatar', file);
  13. // ........使用 原生的ajax 把 formData 发送给后端 完成文件上传
  14. // formdata 做文件上传 不需要设置 content-type
  15. const purple = new XMLHttpRequest();
  16. purple.open('post', 'http://www.itcbc.com:3006/api/formdata');
  17. purple.send(fd);
  18. purple.addEventListener('load', function () {
  19. console.log(this.response);
  20. //因为返回的数据是json字符串,所以需要进行转换
  21. const result = JSON.parse(this.response)
  22. //修改src属性
  23. document.querySelector("img").src = result.data.filename;
  24. })
  25. })
  26. </script>
  27. </body>

form补充

form标签的id属性= button的form属性做到 button放在form外面一样可以form的提交事件

1 form添加一个 id =”aa”
2 button添加一个 form = “aa”
3 解决 按钮必须放在form标签中的问题

  1. <form action="" id="aa"></form>
  2. <button form="aa">提交</button>
  3. <script>
  4. document.querySelector('form').addEventListener('submit', function (e) {
  5. e.preventDefault()
  6. console.log(1);
  7. })
  8. </script>
  1. <form action="">
  2. <input type="text" name="a" />
  3. <input type="text" name="b" />
  4. <input type="text" name="c" />
  5. <button type="reset">重置</button>
  6. <div>点击我 也能重置 表单</div>
  7. </form>
  8. <script>
  9. const form = document.querySelector('form');
  10. const div = document.querySelector('div');
  11. div.addEventListener('click', function () {
  12. // form dom元素具有一个方法 重置
  13. form.reset();
  14. });
  15. </script>

原生ajax封装函数小案例

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport"
  6. content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no" />
  7. <title>08-封装ajax.html</title>
  8. <style>
  9. * {
  10. margin: 0;
  11. padding: 0;
  12. box-sizing: border-box;
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <img src="" alt="">
  18. <input type="file" />
  19. <script>
  20. /*
  21. 1 ajax 是一个什么样的数据 函数
  22. 2 ajax 接收了一个参数-对象格式
  23. 3 函数封装的心得
  24. 1 固定不变的部分 放在函数中
  25. 2 会变化的部分 通过形参 或者 条件判断来代替
  26. */
  27. //封装本体
  28. function ajax(purple) {
  29. const white = new XMLHttpRequest();
  30. const yellow = purple.data ? '?' + purple.data : ''
  31. if (purple.type === 'get') {
  32. white.open(purple.type, purple.url + yellow);
  33. white.send();
  34. } else if (purple.type === 'post') {
  35. white.open(purple.type, purple.url);
  36. if (typeof purple.data === 'string') {
  37. white.setRequestHeader(
  38. 'content-type',
  39. 'application/x-www-form-urlencoded'
  40. );
  41. white.send(purple.data);
  42. } else if (typeof purple.data === 'object') {
  43. if (purple.data instanceof FormData) {
  44. white.send(purple.data);
  45. } else {
  46. white.setRequestHeader('content-type', 'application/json');
  47. white.send(JSON.stringify(purple.data))
  48. }
  49. }
  50. }
  51. white.addEventListener('load', function () {
  52. purple.success(JSON.parse(this.response));
  53. });
  54. }
  55. //版本1
  56. ajax({
  57. type: 'get',
  58. url: 'http://www.itcbc.com:3006/api/getbooks',
  59. success(result) {
  60. console.log('第一次获取成功,准备页面渲染');
  61. console.log(result);
  62. },
  63. });
  64. //版本2
  65. ajax({
  66. type: 'get',
  67. url: 'http://www.itcbc.com:3006/api/getbooks',
  68. data: 'bookname=钵仔糕的魔法&appkey=luxingniao',
  69. success(result) {
  70. console.log('第二次获取成功,准备页面渲染');
  71. console.log(result);
  72. },
  73. });
  74. //版本3
  75. ajax({
  76. type: 'post',
  77. url: 'http://www.itcbc.com:3006/api/addbook',
  78. data: 'bookname=钵仔糕的魔法&&author=小白&publisher=魔仙堡&appkey=luxingniao',
  79. success(result) {
  80. console.log('第三次获取成功,准备页面渲染');
  81. console.log(result);
  82. },
  83. });
  84. //版本4
  85. ajax({
  86. url: 'http://www.itcbc.com:3006/api/addbook',
  87. type: 'post',
  88. data: {
  89. bookname: '小龙虾的魔法',
  90. author: '小白',
  91. publisher: '魔仙堡',
  92. appkey: 'luxingniao',
  93. },
  94. success(result) {
  95. console.log('第四次获取成功,准备页面渲染');
  96. console.log(result);
  97. },
  98. })
  99. //版本5
  100. const input = document.querySelector('input');
  101. input.addEventListener('change', function () {
  102. const file = this.files[0];
  103. const purple = new FormData();
  104. purple.append('avatar', file);
  105. const white = new XMLHttpRequest();
  106. ajax({
  107. send: purple,
  108. url: 'http://www.itcbc.com:3006/api/formdata',
  109. type: 'post',
  110. data: purple,
  111. // 函数的声明 函数的定义!!
  112. success(result) {
  113. console.log(result);
  114. console.log('第五次获取成功,准备渲染页面图片');
  115. document.querySelector("img").src = result.data.filename;
  116. },
  117. });
  118. });
  119. </script>
  120. </body>
  121. </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之间进行资源的交互。

ajax - 图7

注意:可以发请求,但是会报错!

什么是跨域

同源指的是两个 URL 的协议主机名端口号完全一致,反之,则是跨域。

出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。例如:
网页:http://www.test.com/index.html
接口:http://www.api.com/userlist
受到同源策略的限制,上面的网页请求下面的接口会失败!

浏览器对跨域请求的拦截过程

浏览器允许发起跨域请求。但跨域请求回来的数据,会被浏览器拦截,无法被页面获取到!示意图如下:

ajax - 图8

突破浏览器跨域限制的两种方案

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 接口是否允许跨域请求。

ajax - 图9

什么是 JSONP

JSONP 是实现跨域数据请求的一种技术解决方案。它只支持 GET 请求,不支持 POST、DELETE 等其它请求。
在实际开发中很少被使用

在解决跨域问题时:
CORS 方案用到了 XMLHttpRequest 对象,发起的是纯正的 Ajax 请求
JSONP 方案没有用到 XMLHttpRequest 对象,因此,JSONP 不是真正的 Ajax 技术

JSONP 在底层,用到了

防抖 & 节流

防抖

防抖(debounce)指的是:频繁触发某个操作时,只执行最后一次
image.png

防抖的应用场景

场景:搜索框只在输入完后,才执行查询的请求。
好处:这样可以有效减少请求的次数,节省网络资源。
image.png

核心代码:

  1. <script>
  2. // 一、延时器的妙用
  3. // 二、 过程 华为手机
  4. // 1按下按键华开启延时器 1.5s后再执行
  5. // 2.在1.5s内按下为
  6. // (1)先清空旧延时器(搜索华)
  7. // (2)同时又开启了一个新的延时器(搜索华为) 1.5s
  8. // 3. 在1.5s内 按下手
  9. // (1)先清空旧的延时器(搜索 华为)
  10. // (2)同时 又开启了一个新的延时器
  11. // (3)当超过1.5s未输入数据即把当前输入的内容发送请求,可以减小服务器压力
  12. // 三、发现
  13. // 代码 用新的输入来阻止旧的输入
  14. let timeid;
  15. const input = document.querySelector('input');
  16. input.addEventListener(' input', function(){
  17. clearTimeout(timeid);
  18. timeid = setTimeout(()=>{
  19. //修改这个业务
  20. console.log('获取关键字去发送网络请求',this.value);
  21. //修改这个业务
  22. },1500);
  23. });
  24. </script>

节流

节流(throttle)指的是:单位时间内,频繁触发同一个操作,只会触发 1 次
image.png

核心代码:

  1. <button>发送小白的魔法</button>
  2. <script>
  3. /*
  4. 1点击发送验证码按钮
  5. 2开启倒计时6s
  6. 3在6s内重复点击无效!! button.disabled=true
  7. (1)禁用按钮
  8. 4在6s过后允许重新点击
  9. (1)开启按钮
  10. */
  11. const button = document.querySelector('button')
  12. button.addEventListener('click', function () {
  13. button.disabled = true
  14. // console.log('3 开始发送小白的魔法');
  15. //2.开启倒计时
  16. let num = 6
  17. //开启定时器
  18. let timeId = setInterval(() => {
  19. num--;
  20. console.log(num);
  21. if (num === 0) {
  22. //关闭定时器
  23. clearInterval(timeId);
  24. button.disabled = false
  25. }
  26. }, 1000)
  27. })
  28. </script>