技术思考
Ajax发送请求的异同
使用异步对象发起 get 请求
-
创建异步对象。
-
通过
open方法设置请求方式和请求地址,如果有参数需要在url后面拼接参数,格式为:地址?参数=值&参数=值
-
get方式不用设置请求头,因为get方式的参数只能是字符串参数,编码默认为utf-8。 -
发起请求,传递请求体,get方式没有请求体,但是要调用
send方法。
使用异步对象发起 post 请求
- 创建异步对象。
- 通过
open方法设置请求方式和请求地址,如果有参数不能够在url中拼接参数,否则后台获取不到,从而报错。 post方式如果有参数,需要设置请求头(主要用于参数的编码格式)。- 传递字符串值
Content-Type:application/x-www-form-urlencoded - 传递对象,要转为
json·Content-Type:application/json
- 传递字符串值
post方式如果有参数,则需要在请求体中设置xhr.send('key=value&key=value')。
get 和 post 在原生方式中的异同
-
相同点
都要创建异步对象,都要设置请求报文和接收响应。
-
区别
get方式如果有参数,必须在请求行url中拼接参数,post方式不能。get方式没有请求体,post方式的请求体需要在send中传递。get方式的参数需要在url中拼接,post方式的参数在请求体中传递。get方式不用设置请求头,post方式需要设置,且还要根据用户所传递的参数类型进行设置。-
key=value&key=value:Content-Type:application/x-www-form-urlencoded -
{key:value}:Content-Type:application/json , xhr.send(JSON.stringify()) -
formdata: ''
-
服务器返回状态值
| 状态码 | 定义 |
|---|---|
| 200 | 成功 |
| 301、302 | 重定向 |
| 304 | 数据来源于缓存 |
| 400 | 参数有问题 |
| 404 | 页面资源未找到,一般是url写错了 |
| 401,403 | 没有权限,用户信息验证失败 |
| 500 | 服务器有问题 |
Ajax报错处理方式
当请求出现问题的时候
- 打开
network面板 - 查看当前请求是否成功的发起,如果请求链接是红色,一定是请求出错
- 查看请求的
url是否正确,method是否正确 - 查看是否按接口的要求传递了参数,同时参数的值是否合理
- 查看后台接口的返回数据
axios收集与传递数据
根据需要找到对应的接口文档的说明
- 确定当前接口的
url。 - 确定当前接口的
method。 - 确定当前接口的参数需求。
- 在发起请求的时候,设置正确的
url,method,参数传递方式。get: {params:{参数}}post:{参数对象}
表单元素的name属性
表单元素的 name 属性的值就是参数的键,且 name 属性的值一定要参照后台接口的说明,不能错。
收集数据的几种方式
收集数据之后做为参数传递时的格式, key 的名称要参照后台接口。
key=value&key=value使用jq.serialize()方法:获取指定表单中拥有name属性的表单元素的value。{key:value,key:value}自己获取元素,获取数据,生成对象或拼接为字符串new FormData()formdata,传文件时才会用到。需要后台接口的支持。
注意:
所有场景下,传递给服务器的参数都是键值对的形式
跨域
jsonp的原理
通过 script 标签的 src 请求的资源,默认会按 js 语法来解析。
jsonp 面试时的回答
- 本质是利用了
script标签的src属性的天然跨域特性。 - 它向后台接口发起请求,传递一个前台页面拥有的函数名称,后台返回函数的调用形式,并拼接数据,前台页面拼接后台返回的内容,会以
js语法来解析。
缺点:
- 它与异步对象没有任何的关系
- 它只能发起
get请求,并且它严重的依赖服务器的配置。
防抖与节流
放抖
-
防抖:指的是频繁触发某个操作时,只执行最后一次。
-
应用场景:输入框为
input事件时,在输入完成后才执行查询请求。 -
好处:减少请求次数,节省网络资源。
-
图示。
节流
-
节流:单位时间内,频繁触发同一个操作,只会触发一次。
-
应用场景:点击按钮显示下一页,在加载的时候点击也不会触发请求。
-
图示。
案例小练
自己封装一个Ajax函数
英雄管理
整体效果如下所示。
页面渲染
最开始的第一步是先拿到数据,用数据和静态结构做页面渲染。而获取数据的方式要先看接口文档,知道请求方式和请求地址。
查看接口文档我们得知,方式为 get ,由于 method 不设置时默认是 get ,因此可以省略;地址 url 是 http://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 的值不为-1)则显示相应的数据。
而 indexOf 还有一个小妙处,任何字符串中都包含空字串,如下图所示。
所以我们可以这么想,搜索功能实际上就是获取英雄数据,渲染页面也是获取英雄数据,代码都是一样的。区别就在于一个传参,一个不传参。如果我们传递了参数,则后台进行模糊查询,如果我们不传递参数,则默认为空字符串,所有数据都匹配,因此渲染全部数据。
因此渲染函数代码可改为以下形式。
function init(key = '') {
axios({
url: 'http://127.0.0.1:3001/getHeroList',
params: {
heroName: key
}
}).then(res => {
// 省略
})
设置一个形参 key , get 方法传递参数用 params 。 key 的默认值为空字符串,默认情况下渲染全部数据。查询操作如下所示。
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')
})
}
})
})