详解 ajax前后端交互

329 阅读5分钟

1. 什么是ajax?

  - `async javascript and xml`
    + async  异步
    + javascript
    + and  和
    + xml  是一种严格是html格式数据
  - ajax 就是 前后端交互的一种方式(异步交互)

2. 前后端交互

    - 前端 和 后端 之间的通讯沟通
    - 目的: 通过 ajax 向后端(服务端)送一个请求, 得到服务端返回的结果
      + 比如: 登录, 发送用户名 和 密码,接受返回 登录成功 或 登录失败

1)同步和异步的定义

  - js 是单线程的,单线程就是指在同一个时间不能执行多个任务
  
  - 同步: 同步执行那个就是按照顺序执行
  
  - 异步: 不按照顺序执行(跳过执行),异步代码会等所有的同步代码执行结束后,才执行异步代码

3. http传输协议(了解)

  根据传输协议规定, 必须是由前端向后端发送请求, 发送请求的时候如果要携带一些参数, 必须是字符串格式。
  
 三次握手的步骤
  1. 建立连接通道
      浏览器和服务端 建立一个连接

  2. 前端(客户端)发送请求
      要求前端必须以 '请求报文' 的形式发送;
      请求报文 由 浏览器进行组装, 我们只需要提供对应的信息即可;
      比如说: 请求的方式, 请求的地址, 请求需要的参数

  3. 服务器返回响应
      要求后端必须以 '响应报文' 的形式返回;
      响应报文内有一个东西叫做响应状态码;
      
      响应状态码
      100~199   表明连接还在继续
      200~299   表明连接各种成功  但现在只会返回一个 200
      300~399   表明请求重定向
      400~499   表明请求失败     但现在只会看到一些 403 404 401 400  一般4开头是前端的问题
      500~599   表明服务端出错   跟前端无关, 是后端的问题

  4. 断开连接
      浏览器和服务端的连接断开
      

4.标准格式

写法1:(推荐)
    + onload事件 只能在标准浏览器中使用  在低版本IE中不能使用
    <script>
        // 1. 创建一个 ajax 对象
        const xhr = new XMLHttpRequest()
        // 2. 配置 ajax 配置请求信息 (请求方式,请求地址,是否异步):默认值为true 表示ajax异步,false,表示ajax同步
        xhr.open('GET', 'http://localhost:8888/test/first', true)
        //3. 接收响应
        xhr.onload = function () {//给xhr绑定函数
            // console.log('现在后端已经给我们返回了 我们想要的数据了')
            console.log(xhr.responseText) //ajax对象的属性responseText存储了响应的数据
            // 已经成功使用 ajax 发送给我了一个请求, 这是我给你的回应
        }
        // 4. 发送请求
        xhr.send()
    </script>
写法2:(比较少用)
      ajax对象的 readystatechange 事件  可以兼容低版本IE
      - 触发时机:  当ajax状态码发生变化的时候触发
  <script>
    // 1. 创建一个 ajax 对象
    const xhr = new XMLHttpRequest()
    // 2. 配置 ajax对象 (请求方式,请求地址,是否异步):默认值为true 表示ajax异步,false,表示ajax同步
    xhr.open('GET', 'http://localhost:8888/test/first')

    // 4. 配置接收响应的函数
    xhr.onreadystatechange = function () {//给xhr绑定函数
        if (xhr.readyState === 4) {
            console.log('当前浏览器已经完全解析完毕 返回的数据', xhr.responseText)
            //ajax对象的属性responseText存储了响
        }
    }
    // 3. 发送请求
    xhr.send()
  </script>

1)为什么用上述格式

    一下是错误写法,xhr.onload应该放在xhr.send前
<script>
    const xhr = new XMLHttpRequest()
    xhr.open('GET', 'http://localhost:8888/test/first', false) //false,表示ajax同步
    xhr.send()//发送请求,等待返回值并赋给xhr。xhr.send()需要几秒完成,它包括组装报文等操作
    //open里如果是true,xhr.send就是异步代码,程序会继续向下运行xhr.onload将方法绑定给xhr。然后等待返回值会赋给xhr,send结束后再触发onload函数
    //open里如果是false就是同步步代码,xhr.send()发送一个请求, 等待请求完成后, 开始执行后边的xhr.onload代码,但是xhr.send()已经把这个请求完全的运行结束了, 所以此时不可能再触发这个函数了)
    xhr.onload = function () {//相当于给xhr绑定方法
        console.log(xhr.responseText)
    }
</script>

5. ajax 的状态码

通过一个数字, 表明 ajax 当前运行到那一步了

0    表示ajax对象创建完成    //例如:const xhr = new XMLHttpRequest()后 xhr.readyState=0

1    表示配置本次请求信息完成 //例如:xhr.open('GET', 'http://localhost:8888/test/first')后,xhr.readyState=1


//因为 xhr.send()整个过程中做了以下3个动作:所以状态码从1变为2,从2变为3,从3变为4
2    表示发送请求,并且响应已经回到浏览器的瞬间(响应报文来到浏览器,但是浏览器还没有开始解析响应报文),//xhr.readyState=2

3    表示浏览器正在解析响应报文,并且逐条把响应报文的内容填充到 `xhr.responseText`中,//xhr.readyState=3

4    表示浏览器解析响应结束,并且已经把响应报文全部填充到 `xhr.responseText`中,本次请求结束。//xhr.readyState=4

6. 不同的请求方式

1) get请求方式:

  参数的位置:发送请求时,所需要携带的参数(key1=value1&key2=value2)通过?,&拼接(and符号),放在请求地址后
  参数的形式:字符串形式
  格式:xhr.open('GET', 'http://localhost:8888/test/third?name=zhangsan&age=18')

书写格式:

      // 1. 创建一个 ajax 对象
      const xhr = new XMLHttpRequest()
      // 2. 配置 ajax 对象 xhr.open('请求方式', '请求地址')
      xhr.open('GET', 'http://localhost:8888/test/third?name=zhangsan&age=18')//字符串的形式发送请求
      // 3. 配置接收响应的函数
      xhr.onload = function () {
          const res = JSON.parse(xhr.responseText)//将json字符串转化为对象形式
          console.log(res)
      }
      // 4. 发送请求
      xhr.send()

2) post请求方式:

   参数的位置: 不能拼接在请求地址后,而是放在请求方式 send()的()里
   参数的形式: 字符串的形式
   格式:xhr.send('name=张三&age=18')
   在传参的时候还需要配置一个请求头中的属性 content-type
   content-type 赋值的时候, 还要区分我们传递要的参数是普通字符串, 还是 json 格式的字符串。
   1) 普通字符串: xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
   2) json字符串: xhr.setRequestHeader('content-type', 'application/json')    

书写格式:

    // 1. 创建一个 ajax 对象
    const xhr = new XMLHttpRequest()
    // 2. 配置 ajax 对象 xhr.open('请求方式', '请求地址')
    xhr.open('POST', 'http://localhost:8888/test/fourth')
    // 4. 配置接收响应的函数
    xhr.onload = function () {
        const res = JSON.parse(xhr.responseText)//将json字符串转化为对象形式
        console.log(res)
    }

    // 3.1 post 请求传参时需要配置 content-type(请求头内的属性)
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')

    // 3.2 发送请求
    xhr.send('name=张三&age=18')//字符串的形式发送请求

7.封装异步(ajax请求)代码

为什么用promise去封装ajax:发送请求包含异步代码,我们不知道onload什么时候触发。ajax是异步代码,封装异步代码的方法
有两个:一个是回调函数,一个是promise. 但是回调函数会遇到回调地狱问题。所以用promise去封装ajax的异步代码

     *  需求:
     *      1. 请求的方式: 选填, 默认值为 GET;  形参名: type
     *      2. 请求的地址: 必填;    形参名: url
     *      3. 请求为同步还是异步: 选填, 默认值为 true; 形参名: async
     *      4. 请求需要携带参数: 选填, 默认为 '';   形参名: data
     * 
     *  返回值:
     *      需要返回一个 promise 对象, 后续可以通过 .then 或者 async/await 去使用
    <script>
        function myAjax(options) {//把promise封装到myAjax函数里
            // 1. 验证 参数中的 url 必传
            if (options.url === undefined) throw new Error('参数中缺少必传项 url')  // 返回一个错误
            // 1.1 参数格式验证, 拿请求方式举例: type 只接受 undefined 或者 string 两种类型
            if (!(options.type === undefined || typeof (options.type) === 'string')) throw new Error('参数中 type 的类型必须为 string')
            // 2. 封装默认值
            const _options = {
                url: options.url,             //必填;    形参名: url
                type: options.type || 'GET',  //选填, 默认值为 GET;  形参名: type
                data: options.data || '',     //选填, 默认为 '';   形参名: data
                async: options.async ?? true  //选填, 默认值为 true; 形参名: async
                //空值检测符, 该符号的特点 只会在 左侧的值为空值的时候返回右侧, 比如左侧为: null, undefined。
            }
            // 3. 如果当前 _options.data 有值, 并且 _options.type 是 GET 方式, 我们直接将 data 拼接到 url 后边
            if (_options.data && /^(GET)$/i.test(_options.type)) {
                _options.url += '?' + _options.data 
                //例如:_options.url=http://localhost:8888/test/third?name=zhangsan&age=18
            }
            // 用promise去封装异步(ajax请求)代码
            const p = new Promise((res, rej) => { //直接运行箭头函数里的代码,p用来存储promise对象
                const xhr = new XMLHttpRequest() // 创建一个 ajax 对象 xhr
                xhr.open(_options.type, _options.url, _options.async)   //配置 ajax对象
                xhr.onload = function () { //ajax 对象 xhr绑定方法
                    res(xhr.responseText)//将promise对象p 的状态改为成功,触发then函数并运行里面的代码
                }
                if (/^(POST)$/i.test(_options.type)) { //如果请求方式是POST
                    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
                    xhr.send(_options.data)//发送请求后,如果成功收到后端返回值就会触发onload事件
                } else {//如果请求方式不是POST
                    xhr.send() //发送请求后,如果成功收到后端返回值就会触发onload事件
                }
            })
            return p //返回一个Promise对象 p
        }
    </script>

使用上述的封装函数

        //1.  'POST'方式 使用.then
        myAjax({
            url: 'http://localhost:8888/test/fourth',
            data: 'name=zhangsan&age=18',
            type: 'POST'
        }).then((res) => {
            console.log('我是 .then 内输出的 参数: ', JSON.parse(res))
        })


        //2. 不传请求方式type  使用.then方法
        myAjax({
            url: 'http://localhost:8888/test/third',
            data: 'name=zhangsan&age=18',
        }).then((res) => {
            console.log('我是 .then 内输出的 参数: ', JSON.parse(res))
        })

        //3. 不传请求方式type  使用async-await方法
        async function mfAjaxReturn() {
            const res = await myAjax({
                url: 'http://localhost:8888/test/third',
                data: 'name=zhangsan&age=18',
            })
            console.log(res)
        }
        mfAjaxReturn()