为了账号安全,请及时绑定邮箱和手机立即绑定
3. 调用后端服务 static/script.js

文件 script.js 定义了调用后端服务的 Ajax 函数,由如下部分构成:

5.总结

市场上有很多 Ajax 类库,它们更加成熟、设计更加合理,在实际工作中可以首选使用。jQuery 提供了非常简单易用的 Ajax 工具方法,包括简单的 $.load 方法,还有更为灵活也更为底层的 $.ajax 方法。deferred 能够很好的应用于一些耗时操作,并且提供统一的编程接口。deffered 改变了以往 $.ajax 的处理方式,使得我们可以通过链式调用的方式来进行结果的操作,既改变了回调函数的老式做法,也让我们对结果操作更有掌控权。

5. 小结

本节我们通过真实的业务场景触发,对原生的 ajax 请求做了 promise 封装,最后我们对真实的业务场景使用 axios 对业务二次封装,这样更好地复用业务逻辑,统一增加不同返回结果的提示。应用 Promise 封装 Ajax 实践

2. 改造上节案例

上一节 我们通过一个案例来讲解 Promise + Generator 在实际应用中的使用,通过 Generator 函数 和 yield 让异步代码看起来像同步代码一样执行。但是这样里面存在的一个问题就是生成器函数直接执行,需要手动处理。为了解决深层回调的问题我们借助了 co 库来帮助我们去执行生成器函数,从而解决了回调地狱的问题。下面是上一节的代码。const ajax = function(api) { return new Promise((resolve, reject) => { setTimeout(() => { if (api === 'api_1') { resolve('api_2'); } if (api === 'api_2') { resolve(100); } }, 0) })}function * getValue() { const api = yield ajax('api_1'); const value = yield ajax(api); return value;}co(getValue()).then(res => { console.log(res);})上面的代码中 getValue 是生成器函数,不能直接调用,这里用 co 库来进行执行,然后通过 Promise 的链式调用获取执行后的结果。但是这里借助了 co 的库,我们其实最希望的是能像执行普通函数一样直接调用 getValue 就能执行并得到结果。 async/await 的出现就是为了抹平在调用时所做的额外步骤。那让我们看看 async/await 是怎么用的:async function getValue() { const api = await ajax('api_1'); const value = await ajax(api); console.log(value) return value;}getValue() // 控制台打印 value的值是:100上面的代码中我们可以看出使用 async/await 定义的 getValue 函数和生成器函数 */yield 定义的基本相同,但是在执行时 async/await 定义的函数直接调用即可。从这里我们就能看到 async/await 的优点,无需过多的操作非常优雅和简洁。

3.1 API 介绍

jQuery.ajax(url,[settings])如上所示,$.ajax 接受两个参数,分别为 url 和 配置对象。其中:url : 一个用来包含发送请求的URL字符串。[settings] : 一个以 { key : value } 组成的 Ajax 请求设置对象。所有选项都是可选的。在配置对象中,我们通常会传入我们希望指定的配置项,来指定我们的 Ajax 的行为和属性。当然有些参数我们并不一定要去指定,因为有的配置项本身会有默认配置,只要该默认配置符合你的使用需求即可。有兴趣的同学也可以到 jQuery Ajax 详细查看每个配置项的默认值以及含义。

3.4 删除待做事项

function onDeleteTodo(todoId){ var data = JSON.stringify({'todoId': todoId}); $.ajax({ 'url': '/todos/delete', 'type': 'POST', 'contentType': 'application/json', 'data': data, 'dataType': 'json', 'error': ajaxError, 'success': ajaxSuccess });}在第 5 行,通过 JQuery 的 ajax 函数调用后端服务,设置 url 为 ‘/todos/delete’、type 为 ‘POST’ ,删除待做事项。

3.3 更新待做事项

function onUpdateTodo(todoId) { var data = JSON.stringify({'todoId': todoId}); $.ajax({ 'url': '/todos/update', 'type': 'POST', 'contentType': 'application/json', 'data': data, 'dataType': 'json', 'error': ajaxError, 'success': ajaxSuccess });}当用户完成一个待做事项后,将待做事项的 status 从 ‘todo’ 更改为 ‘done’。在第 5 行,通过 JQuery 的 ajax 函数调用后端服务,设置 url 为 ‘/todos/update’、type 为 ‘POST’ ,更新待做事项的 status。

5. 小结

Ajax 主要通过 XMLHttpRequest 来发送请求,必要的时候为了兼容低版本 IE 浏览器, 我们可以启用 ActiveXObject。XMLHttpRequest 提供 open 方法,我们可以借助它来实现 GET、POST、PUT、DELETE 等请求。Ajax 在向服务端发送数据时,比如 POST 、PUT , 需要通过 XMLHttpRequest.setRequestHeader() 设置 Content-type 的类型,服务端根据指定 Content-type进行请求主体的解析。

前言

相信很多有一定前端经验的同学肯定听说过 jQuery,并且稍有研究的人肯定也知道 jQuery 提供了 Ajax 系列的工具方法。事实上,市面上有各种各样的 Ajax 类库可以供我们使用,它们更加健壮、更加成熟、并且设计也更为合理。在我们在实际开发的过程中,我们并不直接使用 XMLHttpRequest,而是会使用这些封装好的经得起考验的类库,来为我们的程序提供服务,让我们的开发者更加专注在本身的业务上。那么,本章节挑选了曾经盛极一时的 jQuery 的工具方法 Ajax 来做展开介绍。首先,我先引用 jQuery 的设计宗旨:Write less, do more.稍有研究的同学都知道 jQuery 非常好用,这些都得益于 jQuery 优秀的设计思想。它不但能够提供便捷的元素选择及操作方式,还提供了一系列的工具方法、链式操作等等,使得我们在开发过程中变得足够简易。Ajax 同样继承了 jQuery 的优秀设计思想,提供了更为人性化的接口和配置。接下来我们来聊聊 jQuery Ajax :

2.正文须知

本章节不考虑不同域文档之间的跨域交互,主要讲 Ajax 造成的跨域的解决方法。开始讲解 Ajax 造成的跨域问题如何解决之前,我们思考一下:假如我们要从山的一边 A 到山的另一边 B,这座山无疑就是个障碍,那么我们有几种解决办法?我想,要么我们就直接穿过去,要么我们就“曲线救国”,绕个道也未尝不可。没错,接下来我们要讲的 Ajax 跨域也是从这两方面来讲,既然跨域有这样那样的一些限制,那我们要么就直面去解决,要么就耍个机灵,同样能够解决。

1.引入 Jquery

因为我们使用的 Ajax 方法是 jQuery 提供的,因此我们需要在页面中引入 jQuery 脚本。<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>Tips: 注意 jQuery 脚本要放在使用到 jQuery 的脚本之前,这样才可以在我们的页面中愉快的玩耍~

6.4 删除联系人

function deleteUser(button, userId){ var children = $(button).parent().children(); var data = JSON.stringify({}); $.ajax({ 'url': '/users/' + userId, 'type': 'DELETE', 'contentType': 'application/json', 'data': data, 'dataType': 'json', 'error': ajaxError, 'success': ajaxSuccess });}点击 “删除” 按钮后,执行函数 deleteUser(button, userId),button 指向的是 “新增” 按钮,userId 是需要更新的联系人 id。在第 3 行到第 5 行,获取需要联系人的姓名和电话,使用了和 6.2 小节相同的方法,请参考 6.2 小节。在第 8 行,通过 jquery 的 ajax 函数调用后端服务,设置 url 为 ‘/users/userId’、type 为 ‘DELETE’ ,表示 RESTful 架构下的删除联系人。

前言

近年来,前端技术呈现一派迅猛发展的态势,随之而来的是,前后端通信方式也发生了翻天覆地的变化,从一开始的重载页面的老旧方式,逐步发展到如今 XMLHttpRequest 和 fetch。相应的,各种各样的 Ajax 类库推陈出新,不断的进步。这个章节,我们打算来聊一聊 jQuery Ajax、fetch和 axios,相信同学们将会有一个更加全面的了解。

2. 引入 jQuery

jQuery 可以直接从官网下载,也可以用 npm 安装,也可以使用 bower 等这些包管理工具,本篇幅采用 CDN 的形式引入,本身 jQuery 就是一个 .js 文件,只需引入就能使用。<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>引入之后就可以在全局下通过 jQuery 或者 $ 调用 jQuery 了。<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script><script> console.log($); console.log(jQuery); console.log($ === jQuery); // 输出:true</script>

jQuery

jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript.(jQuery 官方介绍)

6. 结语

讲到这里,本章节也就到了尾声。本章节旨在通过一个课程录入的例子,并使用 GET 和 POST 两种请求方式来诠释一个前后端交互的完整过程。希望同学们在这个过程中,能够学习到 Ajax 在实际交互中的一个简单的应用,包括:了解如何响应事件,比如点击事件;了解如何发送一个请求;了解如何简单使用 MySQL;了解后端如何返回响应内容;如何前端如何处理响应内容并更新页面。在实际的开发工作中, Ajax 好比前后端之间的一个桥梁,我们可以通过这个桥梁进行通信。然而,如何通信,何时通信,更需要我们有一个良好的程序控制。总而言之,希望同学们能够多用、善用 Ajax,并且让它在你们的程序中发光发亮。

1.跨域请求

简单来说,跨域请求就是一个域下的资源请求另外一个域下的资源。同一个域,指的是,协议名、域名、端口号都一致。 举个例子来说,假如 “http://www.a.com” 下的 JavaScript 脚本发起 Ajax 请求 “http://www.a.com/ajax” ,由于 协议名 http 、域名 www.a.com 和 端口号(默认都是 80)三者都是一致的,因此都属于同一个域,不造成跨域请求。而假如其中任一元素不相同,则造成跨域请求。与此同时,浏览器出于安全考虑,基于同源策略则会做一定的限制:比方说:无法获取不同域的 Cookie、LocalStorage 等等。无法获取不同域的 DOM 对象。无法向不同域发送 Ajax 请求。

3.3 阶段处理

$.ajax 提供了非常人性化的函数配置项,分别有:beforeSend、error、dataFilter、success、complete。我把它们分别归为请求前和请求后两个阶段。3.3.1 请求前对于一个 Ajax 的请求来说,我们在正式发送请求之前可能会对我们的 Ajax 做一些提前的配置或者预先要进行的操作。那么,我们可以非常愉快的使用 beforeSend 。3.3.1.1 beforeSendbeforeSend(xhr)beforeSend 指定的是发送请求之前要先执行的方法。 并且,jQuery 会在 beforeSend 中传入当前的 XMLHttpRequest 对象,方便我们在该函数中做预先的操作。打个比方,我们可以通过这个函数,给当前的 Ajax 请求添加自定义的 headers 项。3.3.2 请求后当我们成功的发送了请求,那么我们接下来就是要解决返回结果的问题了。3.3.2.1 dataFilter 和 success在服务器成功响应之后,我们需要对结果进行获取和处理,这个时候 dataFilter 和 success 就派上用场了。其中:dataFilter :dataFilter (Object data, String type)dataFilter 接受一个服务端返回的原始数据和一个 dataType 值作为参数,会在请求成功之后调用,目的是为了我们可以在请求成功之后对数据进行进一步的处理、过滤。 对比另外一个函数 —— success。success:success(( Object data, String textStatus, jqXHR jqXHR ))success 是成功响应后执行的函数,优先级在 dataFilter 之后。 它接受三个参数,分别是:数据对象、状态描述字符串以及一个 XHR 对象。我们通常会在这个函数里面进行对结果的操作。3.3.2.2 错误处理当然也会有请求失败的情况,那么我们该如何来处理呢?$.ajax 同样提供了人性化的一个回调函数 error 。error(jqXHR jqXHR, String textStatus, String errorThrown)error 接受三个参数,分别为 一个 XHR 对象、一个状态描述字符串以及一个捕获异常的字符串。假如我在程序中定义了这样一个 $.ajax :$.ajax({ url: '/jquery_ajax/get/2', method: 'GET', error (xhr, status, err) { console.log(xhr, status, err) }})其中,我们的 url 指定的是一个不存在的接口,那么我们可以在控制台看到这样的打印信息:显而易见,我们可以很轻易在这个函数中获取到请求的错误信息。事实上,我们常常会在 error 中对异常做进一步的操作。举个栗子,在我们开发的应用中,我们不可能把请求失败的这样一个结果自己在程序中抹干净不上报给用户知道,如果我们内部自己消化了,用户不知道自己到底成不成功,这样的体验就非常糟糕了!这个时候,我们或许会选择弹一个框,告知我们失败的结果以及下一步的操作。3.3.2.3 最后处理好了,来继续思考这样一个场景:假如我们发起一个 $.ajax(),为了让用户感知,我们一般会弹起一个 loading 的界面,让用户稍微等待一下。那问题来了,我们的 loading 界面到底什么时候应该关闭呢?是在请求成功之后关闭吗?那如果请求发生错误怎么办?总不能错误了就不关闭 loading 了吧?那或者在成功和失败的回调中都执行关闭 loading 的方法,这貌似是一个不错的做法,但是这样的做法我们要在两个地方都进行改动,未免太过麻烦,并且后期代码也不好维护呢。别忘了,我们还有一个 complete。complete(jqXHR jqXHR, String textStatus)complete 会在请求完成之后执行,无论请求成功或者失败。 我们可以很好地将一个请求的善后工作交给它来进行。

4.结尾

一路走来,前端发展的过程中诞生了很多优秀的前后端交互的技术。本章从 jQuery Ajax 、fetch 和 axios 三者展开讨论和比较,尽管 jQuery 对于现在来讲比较老旧了,但这丝毫不妨碍它是一个优秀的类库,它提供的 Ajax 功能也确实是非常好。而 fetch 后来的替代方案,本身的设计也是可圈可点的,只是现在来讲还过于稚嫩,不适用于我们业务中推广使用。相比之下,axios 可算是当打之年,从设计上、从体积上,都很适用于我们现在的各种前端技术体系中,因此,我也推荐同学们可以进一步的学习和使用这个技术。

4. POST 请求

事实上我们除了要查询数据,提交数据也是非常重要。在 Ajax 中,我们通常使用 POST 方法来进行数据创建工作。

1. XMLHttpRequest 对象

XMLHttpRequest 对象可以提供给前端开发人员使用 JavaScript 发起 HTTP 请求的能力。该对象会被简称为 XHR 对象。var xhr = new XMLHttpRequest();这样就获得到了一个 XHR 对象的实例。接下来可以使用他发起请求。var xhr = new XMLHttpRequest();xhr.onreadystatechange = function() { // 当 readyState 改变的时候 if (xhr.readyState === 4 && xhr.status === 200) { // 判断当前请求的状态 与 请求的状态码 console.log(xhr.responseText); // 输出服务端返回的内容 }}xhr.open('GET', '/', true); // 设定 GET 请求,请求的路径是 /,并且请求是异步的xhr.send(); // 发送!onreadystatechange 是一个事件处理器属性,每次 readyState 改变的时候都会触发。如果 readyState 为 4,即请求已经完成,并且状态码是 200,表示请求结束并且服务端成功响应。响应成功,通过 responseText 获取到服务端响应的内容。通过 open 方法,设置请求的方法、路径等,例子中设置了 / 路径,如果当前站点的域名是 imooc.com,则请求地址就是 imooc.com/,拿到的数据应该会是网站首页的 HTML。然后通过 send 方法发送请求,发送后 readyState 会在各个阶段发送改变,然后调用 onreadystatechange。这是一个 AJAX 请求较为基本的流程。更多与 AJAX 相关的内容可以参阅 AJAX Wiki,相信阅读完会有许多收获。

2. web 交互的前期

在 Ajax 出现之前,我们也有网页,我们的前后端信息也需要交互,那么我们是怎么做的呢?当时的做法是:刷新(重载)或跳转页面。举两个例子:当我们填写完 form 表单的时候,submit 提交表单,这个时候浏览器会进行页面刷新或跳转,反馈给用户表单提交是否成功。当我们在页面上点击跳转慕课网链接(一般是 a 标签,或者导航栏输入),那么页面会刷新或者跳转到慕课网上去。也许这个时候你会觉得奇怪,交互一次就需要刷新一次?这未免也太过笨拙!是的,如你所见,在 Ajax 之前,HTTP 请求对应着页面,一次 HTTP 请求也就意味着需要请求一个页面。当然,人往高处走,总会有更加先进的办法来解决当前的问题。因此,逐渐的,我们有了 Ajax。

2. 国际化

上述例子发现输出的结果是英文的,显然是不适合在国内环境使用,moment.js 提供了国际化支持,在现有的库中,moment 支持的语言可以说是相对完备了。通过引入对应的国际化资源(语言文件),来切换语言。<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/moment.min.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/locale/zh-cn.min.js"></script><script> var now = moment().calendar(); console.log(now);// 输出当前日历时间 moment().startOf('hour').fromNow(); // 相对这个小时过去了多少分钟 var timestamp = 1593933593236; // 2020年7曰5日下午15点20分38秒 moment(timestamp).fromNow(); // 相对时间戳多久前</script>有关国际化的更多内容可以参考文档。

3. 小结

处理 Ajax 请求,我们应该在适当的时机进行处理。我们应该在 xhr.readyState == 4 ,并且 xhr.status === 200 || xhr.status === 304 的时候正确获取响应的内容;XMLHttpRequest.readyState 体现着当前请求以及服务端响应的状态;XMLHttpRequest.status 即 XMLHttpRequest 响应中的数字状态码。这个数字状态码是一个无符号短整型状态码,代表着我们的 Ajax 请求的状态成功与否;HTTP 状态码有很多,包括 404、 500 等,每一个包含着不一样的含义;获取服务器响应内容,我们可以使用 responseText 、 responseXML 和 response 。其中,responseText 返回一个纯文本的值,responseXML 返回一个包含请求检索的 HTML 和 XML 的 Document,而 response 返回响应正文。返回类型可以有 DOMString、 Blob 、ArrayBuffer 、Document 或 JavaScript Object ,这取决于 responseType。

Lodash

是一个一致性、模块化、高性能的 JavaScript 实用工具库。Lodash 实现了大量实用的工具方法。(官方文档)<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script><script> console.log(window._); var arr = [1, 2, 3, 4, 5]; var arrChunk = _.chunk(arr, 2); console.log(arrChunk); // 输出:[[1, 2], [3, 4], [5]]</script>

5. 使用 AOP 统一后端返回值格式

前后端分离的项目结构中,前端通过 Ajax 请求后端接口,此时最好使用统一的返回值格式供前端处理。此处就可以借助 AOP 来实现正常情况、异常情况返回值的格式统一。

3.2 新增待做事项

function onAddTodo(button){ var children = $(button).parent().children(); var title = children.eq(0).val(); var data = JSON.stringify({'title': title}); $.ajax({ 'url': '/todos/add', 'type': 'POST', 'contentType': 'application/json', 'data': data, 'dataType': 'json', 'error': ajaxError, 'success': ajaxSuccess });}点击 “新增” 按钮后,执行函数 onAddTodo(button),button 指向的是 “新增” 按钮。在 templates/index.html 中,按钮、待做事项位于相同的 DIV 中,如下所示: <div> <input type="text" class="row" placeholder="输入待办事项"> <i class="fa fa-fw fa-plus-square" onclick='onAddTodo(this);'></i> </div>在第 3 行到第 4 行,表达式的含义如下所示:表达式含义$(button).parent()指向按钮的父节点$(button).parent().children()表示 div 的 2 个子节点children.eq(0)指向待做事项的文本框children.eq(0).val()待做事项的文本框的值在第 7 行,通过 JQuery 的 ajax 函数调用后端服务,设置 url 为 ‘/todos/add’、type 为 ‘POST’ ,表示新增一条待做事项。

4. 程序的结构

程序的核心结构如上图所示,程序由前端与后端构成。前端的核心操作如下:在 login.html 中,通过 POST 方法向页面 /users/login 提交表单请求完成登录;在 register.html 中,通过 POST 方法向页面 /users/register 提交表单请求完成登录;访问页面 /users/logout 退出系统;在 script.js 中,函数 addTodo () 通过 Ajax 向页面 /todos/add 请求增加待做事项;在 script.js 中,函数 updateTodo () 通过 Ajax 向页面 /todos/update 请求更新待做事项;在 script.js 中,函数 deleteTodo () 通过 Ajax 向页面 /todos/delete 请求删除待做事项。后端的核心操作如下:Flask 程序使用了 2 个蓝图:users 和 todos;蓝图 users 定义了页面 /users/login 、/users/register、/users/logout,登录和注册的页面处理函数是 login 和 register,最终调用 db.js 中的数据库访问函数 login 和 register,实现登录和注册的功能;蓝图 todos 定义了页面 /todos/add、/todos/update、/todos/delete,它们的页面处理函数是 addTodo、updateTodo、deleteTodo,最终调用 db.js 中的数据库访问函数 addTodo、updateTodo、deleteTodo,实现增加、更新、删除待做事项的功能。

直播
查看课程详情
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号