AJAX day3

96 阅读5分钟

1、XMLHttpRequest的使用

1.1 发起get无参请求

  • 创建一个异步对象
let xhr = new XMLHttpRequest()
  • 设置请求行:请求方式的请求url
xhr.open('get', 'http://www.itcbc.com:3006/api/getbooks')
  • get方式不用设置请求头
  • get方式没有请求体,但是也要发起请求
xhr.send()
  • 监听异步对象的load事件
    • 当数据响应成功且可以使用了就会触发
    • 数据在xhr.response属性中
    • 返回数据的默认类型是字符串,使用之前进行转换
    • 数据格式是json格式
xhr.addEventListener('load', function() {
      console.log(JSON.parse(xhr.response), typeof JSON.parse(xhr.response))
})
  • 完整代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <button>点我啊</button>
    <script>
      let btn = document.querySelector('button')
      btn.addEventListener('click', function() {
        // 1.创建一个异步对象
        let xhr = new XMLHttpRequest()
        // 2.发起请求:设置正确的请求报文
        // 2.1 报文行:设置请求方式和请求url,调用open方法可以设置请求行
        xhr.open('get', 'http://www.itcbc.com:3006/api/getbooks')
        // 2.2 报文头:get方式不用设置报文头,因为它的参数是进行url拼接,所以不用设置编码格式
        // 2.3 报文体:设置本次请求传递给服务器的参数,同时发起请求,get方式的参数在url中拼接,所以不用在这个位置传递参数,调用send可以发起请求
        xhr.send()

        // 3.接收响应
        // 由于是一个异步请求,所以我们不知道什么时候响应,那么当数据响应回客户端可以使用的时候,就会触发load事件,返回值就在xhr的response属性中
        xhr.addEventListener('load', function() {
          console.log(xhr.response, typeof xhr.response)
          console.log(JSON.parse(xhr.response), typeof JSON.parse(xhr.response))
        })
      })
    </script>
  </body>
</html>

1.2 发起get带参请求

  • get方式的参数需要在url后面拼接
  • 拼接的格式为: url?参数=值&参数=值(不要在参数中写空格)
// 如果有参数,在原生代码中,需要在url后面拼接
xhr.open(
  'get',
  'http://www.itcbc.com:3006/api/getbooks?bookname=' + value
)
  • 完整代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <input type="text" />
    <button>点我啊</button>
    <script>
      let btn = document.querySelector('button')
      let input = document.querySelector('input')
      btn.addEventListener('click', function() {
        let value = input.value
        // 1.创建一个异步对象
        let xhr = new XMLHttpRequest()
        // 2.发起请求:设置正确的请求报文
        // 2.1 报文行:设置请求方式和请求url,调用open方法可以设置请求行
        // 如果有参数,在原生代码中,需要在url后面拼接
        xhr.open(
          'get',
          'http://www.itcbc.com:3006/api/getbooks?bookname=' + value
        )
        // 2.2 报文头:get方式不用设置报文头,因为它的参数是进行url拼接,所以不用设置编码格式
        // 2.3 报文体:设置本次请求传递给服务器的参数,同时发起请求,get方式的参数在url中拼接,所以不用在这个位置传递参数,调用send可以发起请求
        xhr.send()

        // 3.接收响应
        // 由于是一个异步请求,所以我们不知道什么时候响应,那么当数据响应回客户端可以使用的时候,就会触发load事件,返回值就在xhr的response属性中
        xhr.addEventListener('load', function() {
          console.log(xhr.response, typeof xhr.response)
          console.log(JSON.parse(xhr.response), typeof JSON.parse(xhr.response))
        })
      })
    </script>
  </body>
</html>

1.3 发起post请求

  • 创建一个异步对象
let xhr = new XMLHttpRequest()
  • 调用open方法设置请求方式和请求url
xhr.open('post', 'http://www.itcbc.com:3006/api/addbook')
  • 调用setRequestHeader方法设置请求头
    • 属性名称:Content-Type
    • 值:application/x-www-form-urlencoded(默认值)
xhr.setRequestHeader(
  'Content-Type',
  'application/x-www-form-urlencoded'
)
  • 调用send方法设置请求体
    • post方式的参数需要在请求体中传递
    • 默认格式为key=value&key=value
 xhr.send(
  `bookname=${bookname.value}&author=${author.value}&publisher=${publisher.value}`
)
  • 监听异步对象的load事件,通过response属性获取返回的内容
  • 完整代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <form class="card-body bg-light" id="addForm">
      <!-- 书名 -->
      <div class="input-group mb-3">
        <div class="input-group-prepend">
          <span class="input-group-text">书名</span>
        </div>
        <input
          type="text"
          class="form-control"
          placeholder="请输入图书名称"
          name="bookname"
        />
      </div>
      <!-- 作者 -->
      <div class="input-group mb-3">
        <div class="input-group-prepend">
          <span class="input-group-text">作者</span>
        </div>
        <input
          type="text"
          class="form-control"
          placeholder="请输入作者名字"
          name="author"
        />
      </div>
      <!-- 出版社 -->
      <div class="input-group mb-3">
        <div class="input-group-prepend">
          <span class="input-group-text">出版社</span>
        </div>
        <input
          type="text"
          class="form-control"
          placeholder="请输入出版社名称"
          name="publisher"
        />
      </div>
      <input type="button" value="添加" class="btn btn-dark btnadd" />
    </form>
    <script>
      let btnadd = document.querySelector('.btnadd')
      // 书名
      let bookname = document.querySelector('[name="bookname"]')
      // 作者
      let author = document.querySelector('[name="author"]')
      // 出版商
      let publisher = document.querySelector('[name="publisher"]')

      btnadd.addEventListener('click', function() {
        // 1.创建一个异步对象
        let xhr = new XMLHttpRequest()
        // 2.发起请求
        // 2.1 设置请求行:open(方式,地址)
        xhr.open('post', 'http://www.itcbc.com:3006/api/addbook')
        // 2.2 设置请求头:post方式需要设置请求头,默认情况下编码格式应该设置为:application/x-www-form-urlencoded
        // 通过 setRequestHeader 方法可以设置请求头
        // Content-Type:用来设置参数的编码格式的
        xhr.setRequestHeader(
          'Content-Type',
          'application/x-www-form-urlencoded'
        )
        // 2.3 设置请求体,通过send方法可以设置
        // post方式的参数需要在请求体中传递,默认的格式为:key=value&key=value
        xhr.send(
          `bookname=${bookname.value}&author=${author.value}&publisher=${publisher.value}`
        )

        // 3.接收响应
        xhr.addEventListener('load', function() {
          console.log(xhr.response)
        })
      })
    </script>
  </body>
</html>
  • 细节说明
    • post,put, patch必须设置正确的请求头,否则后台无法正确的获取到所传递的参数
      • 如果没有正确的设置,则相当于没有传递参数
    • post, put, patch的参数不能在url中拼接,后台不支持,同样的,get参数也不能在send中传递
    • post支持三种参数,但是编码格式不一样
      • key=value&key=value
       xhr.setRequestHeader(
        'Content-Type',
        'application/x-www-form-urlencoded'
      )
      
      • {key:value,key1:value1}
        • 将对象转换为json字符串
        xhr.setRequestHeader('Content-Type', 'application/json')
        
      • formdata
        • 浏览器自动设置

2、封闭自己的ajax工具函数

2.1 简化参数及默认值处理

// 参数简化和处理
  let method = option.method || 'get'
  let url = option.url
  if (!url) {
    alert('一定要设置url')
    return
  }
  let params = option.params
  let data = option.data

2.2 创建一个异步对象

let xhr = new XMLHttpRequest()

2.3 处理get方式的params参数

  • 判断如果是get请求,同时传递了params,就将params对象转换为key=value的格式,拼接在url后面
    if (params && method.toLowerCase() == 'get') {
        url = url + '?' + exchange(params)
    }
    
  • 以&符号分隔成每个字符串的代码
    function exchange(obj) {
    let arr = [];
    for(let key in obj) {
        arr.push(`${key}=${obj[key]}`)
    }
    return arr.join('&')
    }
    

2.4 设置请求行

xhr.open(method, url)

2.5 设置请求头和请求体

  • 如果是post方式
    • 要根据参数的类型进行请求头的设置和请求体的传递:
    • 1.如果参数是key=value
      if (typeof data == 'string') {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        xhr.send(data)
      }
      
    • 2.如果参数是formdata
      else if (data instanceof FormData) {
        // 传递了formdata,浏览器进行设置好请求头
        xhr.send(data)
      }
      
    • 3.如果参数是对象
      else {
        xhr.setRequestHeader('Content-Type', 'application/json')
        // 将对象转换为字符串
        xhr.send(JSON.stringify(data))
      }
      
  • 如果是get方式
    xhr.send()
    

2.6 接收响应

  • 以回调函数的参数将后台响应数据返回
    // 接收响应
    xhr.addEventListener('load', function() {
        // 通过回调函数的参数将后台响应数据返回
        success(JSON.parse(xhr.response))
    })
    

2.7 完整代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <script src="./lib/axios.js"></script>
    <script src="./js/myajax.js"></script>
  </head>
  <body>
    <input type="text">
    <button class="search">查询指定名称的数据</button>
    <script>
      let input = document.querySelector('input')
      let search = document.querySelector('.search');
      search.addEventListener('click', function() {
        axios({
          method: 'post',
          url: 'http://www.itcbc.com:3006/api/addbook',
          data: `bookname=11&author=22&publisher=33`,
          success: function(res) {
            console.log(res);
          }
        })
      })
    </script>
  </body>
</html>
  • 工具函数的代码:
function exchange(obj) {
    let arr = []
    for (let key in obj) {
      arr.push(`${key}=${obj[key]}`)
    }
    return arr.join('&')
}

function axios(option) {
    let method = option.method || 'get';
    let url = option.url;
    if(!url) {
        alert('一定要设置url');
        return
    }
    let params = option.params;
    let data = option.data;
    let success = option.success

    let xhr = new XMLHttpRequest();
    if(params && method.toLowerCase() == 'get') {
        url = url + '?' + exchange(params);
    }
    xhr.open(method, url)
    if(method.toLowerCase() == 'post') {
        if(typeof data == 'string') {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
            xhr.send(data)
        }else if(data instanceof FormData) {
            xhr.send(data)
        } else {
            xhr.setRequestHeader('Content-Type', 'application/json')
            xhr.send(JSON.stringify(data))
        }
    }else{
        xhr.send()
    }
    xhr.addEventListener('load', function() {
        success(JSON.parse(xhr.response))
    })
}

3、同源跨域-面试服务

  • 3.1 同源跨域是浏览器的安全策略
    • 可以正常的向服务器发起数据请求
    • 服务器也会正常的响应
    • 但是浏览器会阻止数据的返回
  • 3.2 同源:协议,域名(IP),端口都一样,则同源,但凡有一个不一样则跨域
    • 浏览器认为与不同源的url交互不安全
  • 3.3 解决跨域的三种常见方案
    • cors跨域:服务器端跨域
      • 在服务器端进行设置的,前端不用任何的改变
      app.use('/', (req, res, next) => {
        // * 允许所有源来发起跨域请求
        res.setHeader('Access-Control-Allow-Origin', '*')
        res.setHeader('Access-Control-Allow-Credential', 'true')
        res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS')
        next()
      })
      
    • jsonp跨域:浏览器跨域
      • 本质是利用了Scirpt标签的src天然跨域特性
      • 通过Script获取的内容会默认以js语法来解析
      • 在发起请求的时候,传递一个函数名称,服务器返回函数调用形式并拼接相应的数据
      • jsonp与异步对象没有任何的关系,它不是一个异步请求,只能发get请求

4、防抖

  • 4.1 是指多次触发,只执行最后一次
  • 4.2 将业务处理代码放到延迟器中
  • 4.3 每次触发事件之后,先清除之前的延迟器,再重新添加延迟器,实现重新计时
  • 4.4 代码演示:
let tid = null
 // input:内容改变就会触发
 input.addEventListener('input', function() {
    // 先清除之前所添加的延迟器
    clearTimeout(tid)
    tid = setTimeout(() => {
      axios({
        url: 'http://www.itcbc.com:3006/api/getbooks'
      }).then(res => {
        console.log(res)
      })
    }, 2000)
 })

5、节流

  • 5.1 是指多次触发,只执行第一次
  • 5.2 定义一个标识,如果满足条件才进行处理
  • 5.3 在第一次操作之后,将标识进行重置,例如重置为false,再进行操作不再处理
  • 5.4 本次操作完成了,将标识再重置为true
  • 5.5 代码演示:
let flag = true // true表示 本次单击应该进行处理
button.addEventListener('click', function() {
    if(!flag){ return}
    flag = false
    setTimeout(() => {
      console.log(123)
      flag = true
    }, 2000)
})