学Ajax看这一篇就够了

699 阅读14分钟

1 服务器相关的基础概念

1.1 服务器

服务器的本质也是一台电脑。

服务器的作用:

  • 存储一个网站的文件(html,css,js,图片,音乐... )
  • 提供网站的文件给用户

1.2 资源

资源代指服务器上存储的内容,通俗的讲,我们浏览网页时,从网络中看到的内容都叫资源

服务器上的每个资源,都对应着独一无二的URL地址

注意:通过在浏览器地址栏输入URL地址,去向服务器发起的请求本质上是一个get请求

image.png

2 network面板介绍

image.png

2.1 调接口时的一个时间轴

image.png

2.2 显示请求方式

image.png

2.3 查看请求状态

image.png

image.png

3 客户端与服务器通信的过程

客户端一般指浏览器

客户端与服务器的通信过程,分为请求-响应两个步骤,其中:

  • 请求的概念:客户端通过网络去找服务器要资源的过程,叫做“请求”

  • 响应的概念:服务器把资源通过网络发送给客户端的过程,叫做“响应”

4 同步和异步

1 同步:阻塞类型的代码执行方式

2 异步 非阻塞类型的代码执行方式

常见的异步有:

  • 定时器 setInterval
  • 延时器 setTimeout
  • Ajax
  • 事件绑定 addEventListner
  • promise

5 什么是Ajax

Asynchronous JavaScript And XML(异步的js和xml)

5.1 生活中的Ajax

image.png

image.png

5.2 Ajax的三大关键

1 请求地址(URL)

告诉浏览器去哪个服务器地址和服务器交互数据

2 请求方式

让浏览器选择合适的方式和服务器交互

发起Ajax的5种请求方式

请求方式描述
post向服务器新增数据
get从服务器获取数据
delete删除服务器上的数据
put更新服务器上的数据--侧重于完整更新(例如更新用户的完整信息)
patch更新服务器上的数据--侧重于部分更新(例如只更新用户的手机号)

3 请求参数

不是必须,具体看接口文档说明

6 工作中使用Ajax的方式

1 原生底层的Ajax (比较少)

2 基于Ajax封装的js库(axios,jQuery)主流

3 面向现代浏览器的fetch

7 第一个axios实例

image.png

8 如何区分不同的请求方式携带参数写在data还是params

1 看文档

  • Query 参数 写在url上或者params上

image.png

  • body 参数 写在data 上

image.png

2 默认

  • get请求 -- url或者params

  • post请求 -- data

  • delete、put、patch 结合文档来看

  • delete 和 get 类似

  • put patch post 类似

3 试试就知道了!!!

9 接口数据的crud

9.1 获取接口数据的实现方式

这里先引入一个概念appkey

image.png

因为后面我们做项目不单单是调接口就完事了,更多是以下的业务情况

image.png

  • 除了页面显示数据要调接口

  • 在输入框输入值,点击搜索时,也要调接口,所以一般要封装一个render函数,可接收参数

  • 页面一开始加载时,就调用render函数

  • 点击搜索时也要调render函数,并且把输入框的值传过去。

代码如下

axios.defaults.baseURL ="http://www.itcbc.com:3006"

// 封装表格数据渲染函数
function render(bn) {

  //每次调用时先清空表格的内容
    tbody.innerHTML=""   
    
    let params = {}
    
    //当调用函数不传参时,bn为undefined,执行这段代码,只给接口传appkey
    if(!bn){    
          params = { appkey: "132000088"}   
    }else{
    
    //当点击搜索时,调用这个函数,给这个函数传参为输入框的值,连同appkey传给接口
         params = { bookname:bn, appkey: "132000088"}
    }
   
    axios.get("/api/getbooks",{params}).then((result) =>{
        const arr = result.data.data
        
        //根据返回的数据动态生成表格
        arr.forEach(value => {
            let tr = document.createElement("tr")
            tr.innerHTML=   `
            <td>${value.id}</td>
            <td>${value.bookname}</td>
            <td>${value.author}</td>
            <td>${value.publisher}</td>
            <td>
              <div class="operate">
                <button type="button" class="btn btn-primary"  data-toggle="modal" data-target="#myModal"  data-id=${value.id}>编辑</button>
                <button type="button" class="btn btn-danger"   data-id=${value.id}>删除</button>
              </div>
            </td>
         `
          tbody.appendChild(tr)
        });
        
    })
}

// 一开始调用函数进行渲染
render()

9.2 新增接口数据的实现方式

一般的业务需求为:

点击新增按钮,有个弹窗,填好表单的值,点击确定时,会调用新增接口新增数据

需求分析:

1 因为弹窗的输入框个数不会少,所以要快速获取表单的值,而不能一个一个去获取,通过以下代码实现:

image.png

2 然后调用这个函数,给函数传表单DOM元素,把返回的值如果要拼接上其他参数就进行拼接,如果不用,就把这个返回值直接传给接口就行,记得调完接口成功新增数据之后,要再调用render函数,让页面显示最新的数据

image.png

9.3 删除接口的数据的实现方式

需求:(点击页面上的删除按钮,删除接口中当前行的数据,并刷新页面)

image.png

分析需求的一个实现方式:

1 首先要给删除按钮绑定事件,因为后面可能会添加新的数据,页面中的删除按钮会动态增加,不能直接去获取删除的dom元素,直接绑定事件,而是要通过事件委托的方式,给它的一个父元素table绑定事件,利用事件冒泡,实现动态的给删除按钮绑定事件

2 点击的时候,怎么获取当前行的一个id属性,把这个属性传给接口?就给删除按钮添加一个自定义属性data-id,给它赋值为接口的id值value.id,后面就可以通过e.target.dataset.id获取到当前行的一个id值

代码实现:

image.png

image.png

9.4 编辑接口的数据的实现方式

需求:(点击页面上的编辑按钮,把当前行的数据赋值给右边的表单,右边的表单修改完数据之后,点击确认编辑按钮,会修改左边的数据)

image.png

分析需求的一个实现方式:

1 点击编辑按钮 ,获取到被点击的按钮(事件委托的方式),把当前行的数据的id传给获取数据的接口----这里id拿的方式跟删除数据时拿id一个道理,把对应的数据显示到表单中,顺带给确认编辑按钮加一个自定义属性赋值为id,方便待会提交编辑时,能拿到id

2 修改完数据之后,点击确认编辑,把表单的数据传给接口,刷新页面

代码实现:

image.png

image.png

image.png

10 调接口出问题时,快速的调试方法

  • 这些是后端对你请求接口的回应 image.png

image.png

  • 这个标头是我们请求时的一些信息

image.png

  • 这个载荷是我们请求接口时给接口传的参数

image.png

一般如果接口报200能调通,预览这里报不存在的接口,那么问题一般出现在你写的代码单词出错了,可能是请求的三要素:请求的地址,请求的方式,请求的参数 的代码单词写错了(注意请求方式是method不是methods,请求的URL地址 / 看有没有加多了,或者params单词有没有写错)

image.png

image.png

11 post-data-字符串格式数据

image.png

12 form表单和按钮-默认刷新

一般页面在进行提交表单数据时,会进行跳转,用户无法停留在当前的页面,导致体验很差,这是表单的默认提交行为导致的,所以以后凡是在表单提交事件中的代码中一定要写上event.preventDefault()阻止表单的默认事件

image.png

13 快速获取form表单input标签的值

13.1 jq的写法

步骤:

  • 要具备form标签
  • 然后每一个input标签要有一个name属性--否则用户填写的信息无法被采集到
  • 然后要引入jq包
  • 最后在表单提交时,写上序列化参数的代码就行了--这里的按钮应该是提交按钮,也就是要给普通的按钮加上type="submit",表示提交按钮,这样该按钮点击时,才会触发表单的默认事件-submit提交事件---而且该按钮标签要放在form标签里面,关联对应的表单,表示提交的是对应的表单的数据(如果不想把按钮放在form标签里,怕破坏原有的html结构,可以按以下这样做,给按钮加一个form="属性值",给form标签加一个id="属性值",这两个属性值要对应,就行了)

image.png

image.png

但是该方法也有缺点:

image.png

13.2 原生js的写法

一定要注意后台接口是否支持formdata

步骤:要具备form标签,然后每一个input标签要有一个name属性

image.png

实现的效果跟上述一样

formdata不像一般的对象,它比较害羞,不能通过打印直接查看它的值

image.png

13.3 formdata()和serialize()的区别

image.png

14 FormData快速获取表单中的值

image.png

image.png

image.png

15 axios发送请求的简写方式

15.1 get请求简写方式

image.png

15.2 post请求简写方式

image.png

image.png

  • 其他一些请求类型比如 put delete patch 具体参考文档,如果文档请求参数是query参数,那参数就是写在params里,跟get一样,那么请求的简写方式跟get一样

  • 如果文档请求参数是body参数,那参数就是写在data里,跟post一样,那么请求的简写方式跟post一样

16 axios-基地址

image.png

17 文件上传

17.1 把图片文件加载到浏览器 显示出来

image.png

17.2 将多个图片文件加载到浏览器 显示出来

image.png

17.3 把文件发送给服务器

用fd.append()来添加数据

formdata.append(参数键,任意参数值)

这个fd.append()的第一个参数怎么写,具体看接口文档

image.png

image.png

image.png

18 请求报文和响应报文

18.1 请求报文

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

请求报文由4 个部分组成

  • 请求行(request line)
  • 请求头部( header )
  • 空行
  • 请求体

image.png

image.png

image.png

18.2 响应报文

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

响应报文由4 个部分组成

  • 状态行
  • 响应头部
  • 空行
  • 响应体

image.png

image.png

19 状态码

19.1 http 响应状态码

概念:http 响应状态码(Status Code)由三位数字组成,用来标识响应成功与否的状态。

作用:客户端浏览器根据响应状态码,即可判断出这次 http 请求是成功还是失败了。

image.png

常见的 http 响应状态码

image.png

19.2 业务状态码

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

19.3 区分响应状态码和业务状态码的不同

  • 所处的位置不同
  • 表示的结果不同
  • 通用性不同

所处的位置

  • http状态码可以直接在状态行看见
  • 业务状态码只可以在响应体中看见 image.png

表示的结果

  • http状态码表示这次请求发送给了服务端,不能说明这次的业务操作就是成功!
  • 业务状态码表示这次请求业务是否成功!! image.png

通用性

  • http状态码全行业通用
  • 业务状态码全行业通用(换公司和换项目都不一样) image.png

20 原生的Ajax代码

20.1 原生方式发起get请求不带参数

原生的Ajax请求成功之后返回的是response,axios库请求成功之后返回的是result

image.png

20.2 原生方式发起get请求带参数

image.png

20.3 原生方式发起post请求

请求体格式Content-Type
参数=值&参数=值application/x-www-form-urlencoded
'{ "id": 1, "name": "zs" }'application/json
new FormData()multipart/form-data; xxxxxxxxx随机字符

传递的参数为字符串的形式时

image.png

传递的参数为JSON字符串的形式时

image.png

传递的参数为formData的形式时,不需要设置content-type

image.png

21 案例练习-封装原生Ajax功能

实现如下功能:

9256c91fc00829e307a5a2a27f16276.png

代码实现功能一:

image.png

代码实现功能二:

image.png

22 跨域

22.1 跨域和同源的概念

跨域和同源其实是浏览器的一种安全机制,不同源的或者跨域了两个URL之间,默认不让你们做数据交互(不让你发Ajax请求)

跨域的出现是因为浏览器有个同源策略

那么什么叫同源策略呢?

就是两个URL之间要满足三个相同,只要有一个不相同,就会造成跨域

  • 协议的名称
  • 主机/域名/IP地址
  • 端口号

image.png

22.2 跨域的解决方案

22.2.1 cors

目前用的比较多的是cors,这是需要后端来解决的,不需要前端解决,如果后端没空,那就前端自己加上以下代码就可以了

const express = require('express');
// const cors = require('cors');
const app = express();
// app.use(cors())

app.get('/books', (req, res) => {
  res.send({
    code: 0,
    message: '查询成功',
    data: [{ name: '红楼梦' }, { name: '三国' }],
  });
});
app.listen(3456)

但是原理还是 res.header("Access-Control-Allow-Origin", "*"); 这一行 设置响应头。

如果接口的响应头有Access-Control-Allow-Origin,说明后端在写这个接口的时候,允许跨域。

image.png

22.2.2 jsonp

      前端的页面是 www.baidu.com

      后端接口     www.qq.com 

      1 访问 后端的一个接口 返回一些数据  前端拿到数据 正常做遍历 渲染 

        1 接口  http://www.itcbc.com:3006/api/getscript

      2 前端利用 script 标签不会跨域的特点 

      3 只能用get请求

    <script>

      //  想方法 获取数据

      // let result = ......



      // 希望 show方法 你自己在前端定义

      // 服务器帮你把数据 装到 show(数据)



      // 什么时候请求完成 后端返回的代码中 自动帮我们调用 show方法 

      // 在show方法中编写业务即可  

      function show(option) {

        // 就是我们前端自己的业务

        // console.log(option);

        // const result=option.data.data;

        // result.map....

      }

    </script>

    <script src="http://www.itcbc.com:3006/api/getscript2"></script>

23 NProgress

使用NProgress之前的准备工作

下载包,然后引包

image.png

注意引入的顺序 image.png

在每一次发送Ajax请求的时候使用NProgress

image.png

但是如果一个页面的请求接口有很多个的话,每一个接口都要加这两行代码,工作量很大,所以为了方便,我们引进拦截器这个概念

24 拦截器

拦截器有两种:请求拦截器和响应拦截器

image.png

拦截器的使用步骤

1 新建一个js文件,在index.html文件引进--注意引进顺序,要在我们写请求接口代码的文件index.js前引进,因为要在发送请求之前就先设置好拦截器

image.png

2 像上面一样引进NProgress的两个包,这样才可以使用NProgress.start() , NProgress.done()两段代码

3 在新建的js文件里写上以下这段代码(以后工作要用到拦截器直接拷贝这段代码到项目中)

// 添加一个请求拦截器
axios.interceptors.request.use(function (config) {
    

    //开始请求时开启进度条
    NProgress.start()   //注意是大写的
    
    return config;
  }, function (error) {
    

    //如果一开始请求失败关闭进度条
    NProgress.done()
    console.log('请求失败');
    
    return Promise.reject(error);
  });

// 添加一个响应拦截器
axios.interceptors.response.use(function (response) {
  
    
    //响应成功关闭进度条
    NProgress.done()
    console.log("响应成功");
    
    return response;
  }, function (error) {
    

     //响应失败关闭进度条
    NProgress.done()
    console.log('响应失败');
    
    return Promise.reject(error);
  });

实现的效果如下:

调接口没完成时,顶部显示进度条和右侧显示loading效果,调完接口返回数据时,就会隐藏顶部进度条和右侧的loading效果

image.png

25 fetch()

这是一种新的请求接口数据的方法,类似原生的Ajax,但跟它又有一定的区别

<script>

      /* 

      fetch 类似 XMLHttpRequst  请求和响应 

      1 新 技术 

      2 功能 和 XMLHttpRequst 一样

      3 用法 简单 fetch    
      
      4 axios(用得最多) fetch(其次) 
      
      5 axios可以使用拦截器-基地址 ,而fetch不可以使用拦截器和基地址 

       */



      //  1 get请求

      fetch('http://www.itcbc.com:3006/api/getbooks')

      .then((result) => {

        return result.json(); // 固定搭配 表示 把响应结果转成json  来使用

      })

      .then(result=>{

        console.log(result);

      })



      // 2 post 请求

      // fetch('url', {

      //   method: 'POST', // 请求类型

      //   headers: {

      //     'Content-Type': 'application/json', // 设置内容

      //   },

      //   body: JSON.stringify(data), // post请求的参数部分

      // });

    </script>

26 防抖和节流

26.1 防抖--关键技术原理--延时器

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。本质上是通过设置一个延时器

防抖一般用在搜索框业务上,拿之前做的那个图书管理案例来分析:

在搜索框输入我要搜索的值时,这个过程有一个要注意的点:

 <script>

      /* 

      1 当我输入完毕了, 你再去发送请求!! 

      2 如何判断输入完毕

        1 如果你输入了文字 过了 2s后,都没有新的输入 我就认为 你输入完毕了 

      3 延时器 

      4 过程

        1 每一次按下按键

          1 清除上一个延时器

          2 开启新的延时器 

        2 例如

          1 按下 按键  "A"

          2 开启一个延时器  => "A" 发送给后端

          3 按下 按键  "AB"

          4  先清空上一个 延时器 “A”

          5  开启一个延时器  => "AB" 发送给后端

          6 按下 按键  "ABC"

          7 先清空上一个 延时器 “AB”

          8 开启一个延时器  => "ABC" 发送给后端

       */

      const input = document.querySelector('input');

      let timeId;
      

      input.addEventListener('input', function () {

        clearTimeout(timeId);

        timeId = setTimeout(function () {

          // 正常业务

          console.log('发送请求出去');

          // 正常业务

        }, 2000);

      });

    </script>

26.2 节流

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

<button>发送验证码</button>

    <script>

      /* 

      1 点击按钮的时候

        判断 是否允许执行业务 条件

      2 满足条件  允许执行

      3 不满足条件  不允许执行 

      4 延时器时间到了, 重新允许执行 

      

       */

      const button = document.querySelector('button');

      button.addEventListener('click', function () {

        button.disabled = true; // 禁用按钮

        // console.log('发送一次验证码');

        let times = 3;

        let timeId = setInterval(() => {

          times--;

          console.log(times);

          if (times === 0) {

            clearInterval(timeId);

            button.disabled = false; // 重新启用按钮

          }

        }, 1000);

      });

    </script>

26.3 总结

  • 函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。

27 扩展

1 关闭表单之前填写的默认信息,就是说那些表单的输入框填过一次值之后,在点击输入框时,会有一个下拉菜单,显示之前填过的数据,有时候很烦,这时候只需要给表单加上一个autocomplete="off"就行了(autocomplete="off" 添加在 form标签上, 关闭 form标签中的input标签的历史记录)

2 提交表单后要清空表单所有input框的值--高级做法 获取表单DOM元素为form,然后form.reset()搞定,利用表单的一个reset()方法

3 JSON的语法要求

image.png

image.png

4 序列化和反序列化

image.png

5 restful api

以前发送Ajax请求只有两种类型: get 和 post

restful api : get post delete put patch