技术思考 【Ajax】

176 阅读7分钟

技术思考

Ajax发送请求的异同

使用异步对象发起 get 请求

  1. 创建异步对象。

  2. 通过 open 方法设置请求方式和请求地址,如果有参数需要在 url 后面拼接参数,格式为:

    地址?参数=值&参数=值

  3. get 方式不用设置请求头,因为 get 方式的参数只能是字符串参数,编码默认为 utf-8

  4. 发起请求,传递请求体,get方式没有请求体,但是要调用 send 方法。

使用异步对象发起 post 请求

  1. 创建异步对象。
  2. 通过 open 方法设置请求方式和请求地址,如果有参数不能够在 url 中拼接参数,否则后台获取不到,从而报错。
  3. post 方式如果有参数,需要设置请求头(主要用于参数的编码格式)。
    • 传递字符串值
      Content-Type:application/x-www-form-urlencoded
      
    • 传递对象,要转为 json·
      Content-Type:application/json
      
  4. post 方式如果有参数,则需要在请求体中设置 xhr.send('key=value&key=value')

getpost 在原生方式中的异同

  1. 相同点

    都要创建异步对象,都要设置请求报文和接收响应。

  2. 区别

    • get 方式如果有参数,必须在请求行 url 中拼接参数, post 方式不能。
    • get 方式没有请求体, post 方式的请求体需要在send中传递。
    • get 方式的参数需要在url中拼接, post方式的参数在请求体中传递。
    • get 方式不用设置请求头, post 方式需要设置,且还要根据用户所传递的参数类型进行设置。
      1. key=value&key=value :

        Content-Type:application/x-www-form-urlencoded
        
      2. {key:value} :

        Content-Type:application/json ,  xhr.send(JSON.stringify())
        
      3. formdata : ''

服务器返回状态值

状态码定义
200成功
301、302重定向
304数据来源于缓存
400参数有问题
404页面资源未找到,一般是url写错了
401,403没有权限,用户信息验证失败
500服务器有问题

Ajax报错处理方式

当请求出现问题的时候

  1. 打开 network 面板
  2. 查看当前请求是否成功的发起,如果请求链接是红色,一定是请求出错
  3. 查看请求的 url 是否正确,method 是否正确
  4. 查看是否按接口的要求传递了参数,同时参数的值是否合理
  5. 查看后台接口的返回数据

axios收集与传递数据

根据需要找到对应的接口文档的说明

  1. 确定当前接口的 url
  2. 确定当前接口的 method
  3. 确定当前接口的参数需求。
  4. 在发起请求的时候,设置正确的 urlmethod ,参数传递方式。
    • get : { params :{参数}}
    • post :{参数对象}

表单元素的name属性

表单元素的 name 属性的值就是参数的键,且 name 属性的值一定要参照后台接口的说明,不能错。

收集数据的几种方式

收集数据之后做为参数传递时的格式, key 的名称要参照后台接口。

  1. key=value&key=value 使用 jq.serialize() 方法:获取指定表单中拥有 name 属性的表单元素的 value
  2. {key:value,key:value} 自己获取元素,获取数据,生成对象或拼接为字符串
  3. new FormData() formdata ,传文件时才会用到。需要后台接口的支持。

注意:

所有场景下,传递给服务器的参数都是键值对的形式

跨域

jsonp的原理

通过 script 标签的 src 请求的资源,默认会按 js 语法来解析。

jsonp 面试时的回答

  1. 本质是利用了 script 标签的 src 属性的天然跨域特性。
  2. 它向后台接口发起请求,传递一个前台页面拥有的函数名称,后台返回函数的调用形式,并拼接数据,前台页面拼接后台返回的内容,会以 js 语法来解析。

缺点:

  1. 它与异步对象没有任何的关系
  2. 它只能发起 get 请求,并且它严重的依赖服务器的配置。

防抖与节流

放抖

  1. 防抖:指的是频繁触发某个操作时,只执行最后一次。

  2. 应用场景:输入框为 input 事件时,在输入完成后才执行查询请求。

  3. 好处:减少请求次数,节省网络资源。

  4. 图示。

    防抖.png

节流

  1. 节流:单位时间内,频繁触发同一个操作,只会触发一次。

  2. 应用场景:点击按钮显示下一页,在加载的时候点击也不会触发请求。

  3. 图示。

    节流.png

案例小练

自己封装一个Ajax函数

英雄管理

整体效果如下所示。

英雄案例.png

页面渲染

最开始的第一步是先拿到数据,用数据和静态结构做页面渲染。而获取数据的方式要先看接口文档,知道请求方式和请求地址。

查看接口文档我们得知,方式为 get ,由于 method 不设置时默认是 get ,因此可以省略;地址 urlhttp://127.0.0.1:3001/getHeroList 。现在可以写代码了。

由于后续很多操作都需要渲染页面,所以直接用函数封装即可。引入 axios 库,用 axios 发起 get 请求,回调函数内用字符串拼接的方式动态渲染,请求返回后调用回调函数。

function init() {
    axios({
        url: 'http://127.0.0.1:3001/getHeroList',
    }).then(res => {
        let data = res.data.data
        let htmlStr = ''
        data.forEach((value, index) => {
            htmlStr += `<tr>
            <td>${index+1}</td>
            <td>${value.name}</td>
            <td>${value.gender}</td>
            <td>
              <img
                src="${value.img}"
                alt=""
              />
            </td>
            <td>
              <button type="button" class="btn btn-info upload" data-id="${value.id}">
                上传头像
              </button>
              <button type="button" class="btn btn-warning del" data-id="${value.id}">
                删除
              </button>
            </td>
          </tr>`
        });
        tbody.innerHTML = htmlStr
    })
}
init()

搜索英雄

这里的英雄搜索用到的是模糊查询,输入一个“白”字可获得“李白”、“白起”等数据。这里先介绍一下模糊查询的原理。

我们知道,字符串有一个内置对象为 indexOf() ,可获取括号内的字符在字符串中第一次出现的位置,如下所示。

indexof.png

因此我们可以把用户在输入框内输入的值作为数据发送给后台,后台对英雄名字进行查询,如果有数据(即 indexOf 的值不为-1)则显示相应的数据。

indexOf 还有一个小妙处,任何字符串中都包含空字串,如下图所示。

空字符串.png

所以我们可以这么想,搜索功能实际上就是获取英雄数据,渲染页面也是获取英雄数据,代码都是一样的。区别就在于一个传参,一个不传参。如果我们传递了参数,则后台进行模糊查询,如果我们不传递参数,则默认为空字符串,所有数据都匹配,因此渲染全部数据。

因此渲染函数代码可改为以下形式。

function init(key = '') {
        axios({
            url: 'http://127.0.0.1:3001/getHeroList',
            params: {
                heroName: key
            }
        }).then(res => {
            // 省略
        })

设置一个形参 keyget 方法传递参数用 paramskey 的默认值为空字符串,默认情况下渲染全部数据。查询操作如下所示。

let btn_search = this.document.querySelector('#btn_search')
let hname = this.document.querySelector('#hname')
btn_search.addEventListener('click', function() {
    let hnameV = hname.value
    init(hnameV)
})

把输入框里的值作为实参传递给函数,此时函数的形参 key 的值为输入框的值。

新增英雄

点击添加英雄按钮,弹出模态框,输入姓名选择男女后点击确定则发送数据给后台,渲染页面。

查看文档,确定请求方式为 post 和地址,需要的数据有英雄名字和性别。先获取到名字输入框、性别下拉框和点击按钮,点击按钮后获取到表单数据,用 axios 传递过去。

let heroname = this.document.querySelector('#heroname')
let herogender = this.document.querySelector('#gender')
let btnAdd = this.document.querySelector('.btnadd')
btnAdd.addEventListener('click', function() {
    let name = heroname.value
    let gender = herogender.value
    axios.post('http://127.0.0.1:3001/addHero', `name=${name}&gender=${gender}`).then(res => {
        init()
    })
})

删除英雄

删除按钮是动态创建的未来元素,需要用到事件委托来绑定。查看文档,删除功能的请求方式是 get ,需要传参 id 。需要我们在渲染页面的操作那里设置一个自定义属性 data-id ,点击删除按钮后获取 e.target.dataset.id

tbody.addEventListener('click', function(e) {
if (e.target.classList.contains('del')) {
    let id = e.target.dataset.id
    axios({
        url: 'http://127.0.0.1:3001/delHeroById',
        params: {
            id: id
        }
    }).then(res => {
        console.log(res);
    })
}

上传头像

点击上传头像弹出模态框,同删除按钮一样也需要用到事件委托。查看文档,该功能是 post 请求,需要先用一个接口获取到图片,传递的参数为 formdata 获取到的文件,再做数据添加,参数为 id 和第一个接口返回的 src 路径。

先为上传文件按钮绑定事件打开模态框,想要获取 id 只能在这里获取,为了在另一个函数中也能使用,故在全局作用域中声明。

let editId
tbody.addEventListener('click', function(e) {
    if (e.target.classList.contains('del')) {
        // 省略
    } else if (e.target.classList.contains('upload')) {
        $('#uploadImgModal').modal('show')
        editId = e.target.dataset.id
    }
})

formdata 获取文件列表有两种方法,我们用 append 的方法。

let myfile = heroImg.files[0]
let fd = new FormData()
fd.append('file_data', myfile)

传递的参数为fd。返回数据中有一条是图片的存储路径,获取到该路径,和前面获取到的id一同传递,即可。

btnHeroEdit.addEventListener('click', function() {
    let myfile = heroImg.files[0]
    let fd = new FormData()
    fd.append('file_data', myfile)
    axios.post('http://127.0.0.1:3001/uploadFile', fd).then(res => {
        let imgsrc = res.data.src
        if (res.data.code == 200) {
            axios.post('http://127.0.0.1:3001/updateHero', `id=${editId}&img=${imgsrc}`).then(res => {
                $('#uploadImgModal').modal('hide')
            })
        }
    })
})