关于ajax的那些事

  • 白小霁
  • 16 Minutes
  • March 23, 2017

自己在简书上写的一篇关于Ajax基础篇,主要内容是基础概念、核心事件以及自己封装了一个基础的ajax方法。

Demo

这次主要是一个项目来回顾Ajax
代码
预览

知其所以然(基础概念)

AjaxAsynchronous JavaScript and XML缩写。Ajax的核心是JavaScript中的一个对象XMLHttpRequest,该对象帮我们将数据发往服务器和解析服务器响应。当然,这个对象有一定的兼容问题,只要是IE7+才能使用我以下的原生方法,现在你也不用考虑这么多了,你可以使用jQuery封装好的ajax方法。

kick off

任务描述

模拟刷朋友圈的加载效果。
步骤如下:

  1. 点击加载更多按钮
  2. 请求后台返回数据
  3. 渲染页面

后端

server-mock基于express开发的,是一个方便使用的前后端分离的工具。server-mock介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 服务端 router.js
app.get('/loadMore', function(req, res) {
var curIdx = req.query.index
var len = req.query.length
var data = []
for(var i = 0; i < len; i++) {
data.push('新闻' + (parseInt(curIdx) + i))
}
//res.send(data);
//模拟慢网速
setTimeout(function(){
res.send(data);
}, 2000)
});

使用了server-mock来给自己mock数据,根据文档自己写了一个后端接口。

前端

1
2
3
4
5
6
7
8
// 主要结构
<body>
<ul id="ct">
</ul>
<a id="load-more" class="btn" href="#">
加载更多
</a>
</body>

现在写JavaScript会有意识的先把大概的步骤写下。

  1. 按钮点击
  2. 触发回调函数(HandleFu)
  3. HandleFu -> 发ajax请求
  4. 返回结果 -> 渲染页面
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    btn.addEventListener("click",function(){
    var xhr = new XMLHttpRequest() // 创建XHR对象
    xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
    if(xhr.status === 200 || xhr.status === 304){ // 成功并收到响应
    var results = JSON.parse(xhr.responseText)
    console.log(results)
    var fragment = document.createDocumentFragment();
    for(var i = 0; i<news.length; i++){
    var node = document.createElement("li")
    node.innerText = news[i]
    fragment.appendChild(node)
    }
    ct.appendChild(fragment)
    }
    }
    }
    xhr.open("GET","/loadMore?index=0&length=5",true)
    xhr.send()
    })
    /*
    渲染数据
    防止频繁操作DOM 创建 fragment 对象保存代码片段
    使用innerText,比innerHTML安全
    */
    function renderData(news){
    var fragment = document.createDocumentFragment();
    for(var i = 0; i<news.length; i++){
    var node = document.createElement("li")
    node.innerText = news[i]
    fragment.appendChild(node)
    }
    ct.appendChild(fragment)
    }

根据思路步骤,书写出面向过程的代码。开始重构代码,观察可以重写的点:

  1. ajax的封装
  2. 加载数据
  3. 渲染数据的函数

首先封装ajax(这次相对简单只是封装了关于get的,全的请看基础篇代码)。其实很多的时候我们不知道怎么封装一个方法,所以我们逆向思考:「我想怎样的调用ajax」

1
2
3
4
5
6
7
8
9
10
11
12
13
// 我想这样的调用
ajax({
type:"get",
url:url,
data:{
index:0,
length:5
},
onSuccess:fn,
onError:function(){
console.log("出错了")
}
})

当然你有你想要的调用方法,我这是想说的是:「像ajax这样的每次冗杂的代码,我们可以根据我想怎样的调用从而指导我怎样的封装这个函数,前提是顺向思维想不到时」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function ajax(options){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState ===4 ){
if(xhr.status === 200 || xhr.status === 304){
var results = JSON.parse( xhr.responseText)
options.onSuccess(results)
}
}
}
var query = "?"
for(key in options.data){
query += key+"="+options.data[key]+"&"
}
query = query.substr(0,query.length-1)
xhr.open(options.type,options.url+query, true);
xhr.send()
}

其余两个函数其实可以直接分离出来。

存在的BUG

考虑这样的一个情景:网速超慢,用户点击一次迟迟没有数据返回,便会重复点击按钮,这样一来便会加载更多的数据。为了防止这样的现象,我们需要加一个状态锁(status lock)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var isDataArrive = false;
btn.addEventListener("click",function(){
if(isDataArrive){
return;
}
loadData(function(news){
renderData(news)
isDataArrive = false;
})
isDataArrive = true
})

总结

  1. 主要通过一个项目来复习了ajax的使用以及封装
  2. 核心代码的解释
  3. 封装的技巧
  4. 引进状态锁解决BUG