使用 Ajax
之前看到的例子都是整页地提交,如果提交失败就重新刷新页面,显示错误信息;如果提交正确,就返回完整的新页面或者跳转到其他页面。重新刷新页面和返回新页面意味着整个页面的内容都要重新加载,但事实上其中大部分 HTML 是相同的。这种传统方法浪费了很多带宽,加载得更慢,用户体验不好。另外一个问题是,刷新的页面里应该包含之前未提交成功的数据,不应该让用户重复输入,这也需要大量的逻辑来保证。
大型网站的首页内容都很丰富,比如豆瓣的匿名(未登录状态)首页,除了登录表单,还有顶部导航栏、热点内容和各产品线的推荐内容等。登录时,可不可以在不刷新页面的前提下,只向服务器发送和取回登录必要的数据呢?
Ajax 是 Asynchronous JavaScript and XML 的简称,通过 Ajax 向服务器发送 HTTP 请求,接收服务器返回的 JSON 数据,然后使用 JavaScript 修改网页的局部来实现更新和提交。现在绝大多数 Web 应用都在使用 Ajax。
jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库,它提供的 API 易于使用且兼容全部主流浏览器,如果不是专业的前端工程师,使用它也可以很方便地操作文档对象、选择文档对象模型(DOM)元素、处理事件、创建动画效果、开发 Ajax 程序等。
首先下载 jQuery:
> wget https://code.jquery.com/jquery-2.2.4.min.js-O static/javascripts/jquery.min.js
jQuery 2.x 与其 1.x 的 API 一样,但是前者只支持 IE 9 及以上的浏览器版本。
登录页面(signin.html)如下:
<!DOCTYPE html>
<html>
<head>
<title>Signin</title>
<script src="{{url_for('static', filename='javascripts/jquery.min.js')}}"></
script>
<style>
#result{
margin-top:20px;
color:red;
}
</style>
</head>
<body>
<div class="container">
<form action="/signin"method="post"role="form">
<h2>Please Sign In</h2>
<input type="name"name="username"placeholder="Username"required
autofocus>
<input type="password"name="password"placeholder="Password"required>
<button class="btn"type="button">SignIn</button>
</form>
<div id="result"></div>
</div>
<script type='text/javascript'>
$(function(){
$('.btn').click(function(){
var$result=$('#result');
var$username=$('input[name="username"]').val();
var$password=$('input[name="password"]').val();
$.ajax({
url:'/signin',
data:$('form').serialize(),
type:'POST',
dataType:'json'
}).done(function(data){
if (!data.r){
$result.html(data.rs);
}else{
$result.html(data.error);
}
});
});
});
</script>
</body>
</html>
这个页面主要包含如下部分。
1.head:head 标签中可以引用 JavaScript 脚本和样式表(CSS)、提供元信息等。jquery.min.js 这种全局的工具库应该放在最上面,在渲染页面时 jQuery 可以更早加载。除此之外,style 标签还给 id 为 result 的 div 元素添加了样式。
2.form:就是登录表单,其中 action 是提交时请求的地址,method 表示提交方法。表单中有两个 input 标签,分别为用户名和密码,还有一个用来提交的按钮。
3.script:最下面的 script 标签中使用 jQuery 监控类名为 btn 的元素的点击事件。单击“提交”按钮则获取表单中的用户名和密码,通过$.ajax 函数发送 POST 请求。其中 done 是执行成功的回调函数,返回的数据叫作 data,如果 data.r 为 0,表示登录成功,给 id 为 result 的 div 添加 data.rs 中的内容,否则添加 data.error 中的内容。由于 script 会获取页面元素,应该在页面元素的之后出现,所以一般都在 body 的最下面。
上述代码中有一些使用 jQuery 的细节。
- “$(function(){...})”将会在浏览器加载完页面的基础内容之后立即执行。
- jQuery 可以使用标签和 CSS 选择器来选取 HTML 元素。举几个例子来看一看:
$("p"):选取<p>元素。
$("p.intro"):选取所有 class="intro"的<p>元素。
$("p#demo"):选取所有 id="demo"的<p>元素。
$('input[name="password"]'):选取所有 name="password"的<input>y 元素。
- 上例使用了“$result.html”设置内容,html() 可以设置或返回所选元素的内容(包括 HTML 标记)。除此之外,还有 text()(设置或返回所选元素的文本内容)和 val()(设置或返回表单字段的值)。使用 html() 更灵活,因为可以在返回的结果中添加标签和样式。
看一看页面视图:
@app.route('/')
def index():
return render_template('chapter5/section3/signin.html')
@app.route('/signin', methods=['POST'])
def signin():
username=request.form['username']
password=request.form['password']
error=None
if len(username)<5:
error='Password must be at least 5 characters'
if len(password)<6:
error='Password must be at least 8 characters'
elif not any(c.isupper() for c in password):
error='Your password needs at least 1 capital'
if error is not None:
return jsonify({'r':1, 'error':error})
return jsonify({'r':0, 'rs':'Ok'})
视图分为两个:
- 首页接受 GET 请求,渲染 signin.html。
- /signin 页面即上面 Ajax 设置的请求地址,接收用户名和密码,处理之后用 JSON 格式返回结果。需要强调的是,不仅后端需要验证用户名和密码,前端也应该通过 JavaScript 验证用户名和密码的格式是否符合,如果不符合,则直接提示错误而不用产生一次请求,减少后端压力。
使用$.ajax 的时候,提交的数据使用了“$('form').serialize()”,它通过序列化表单值,创建 URL 编码的文本字符串。序列化的值可在生成 Ajax 请求时用在 URL 查询字符串中;否则需要挨个字段拼出来:
data:{'username':$username, 'password':$password}
使用 fetch 实现 Ajax
传统 Ajax 指的是 XMLHttpRequest(XHR),JavaScript 通过它来执行异步请求,jQuery 帮我们把 XMLHttpRequest 封装起来,成为$.ajax、$.get 和$.post 等函数。XHR 在设计上不符合职责分离原则,输入、输出以及状态都放在同一对象中,而且受 XML 影响,其命名也很复杂。
在 ECMAScript 2015(ES6)规范中有一个新的特性:Promise。Promise 对象用于延迟(de-ferred)计算和异步(asynchronous)计算,一个 Promise 对象代表着一个还未完成,但预期将来会完成的操作。Fetch(https://github.com/github/fetch )API 就是基于 Promise 设计的。举个 Ajax 返回 JSON 数据的例子,如果是用原生 XHR 写,会是这样:
var data=new FormData();
data.append('username',$username);
data.append('password',$password);
var xhr=new XMLHttpRequest();
xhr.open('POST', '/signin');
xhr.onreadystatechange=function(){
if (xhr.readyState==4&&xhr.status==200){
var data=JSON.parse(xhr.responseText);
if (!data.r){
$result.html(data.rs);
}else{
$result.html(data.error);
}
};
};
xhr.send(data);
如果使用 fetch 则可以写成这样:
fetch('/signin',{
method:'POST',
body:data
}).then(function(response){
return response.json();
}).then(function(data){
if (!data.r){
$result.html(data.rs);
}else{
$result.html(data.error);
}
});
从 Firefox 39 以及 Chrome 42 开始,它们已经支持了 Fetch API。为了保证其他浏览器或者低版本的 Firefox 以及 Chrome 也能使用,需要使用 GitHub 开源的 polyfill 脚本:
> wget https://github.com/github/fetch/raw/master/fetch.js-O static/javascripts/fetch.js
在模板中加载这个脚本:
<script src="{{url_for('static', filename='javascripts/fetch.js')}}"></script>
如果再使用 ES 7 提供的 async/awaitAPI,就可以像写同步代码一样写异步代码了:
try{
let response=await fetch('/signin',{
method:'POST',
body:data
})
let data=await response.json();
if (!data.r){
$result.html(data.rs);
}else{
$result.html(data.error);
}
}catch(e){
console.log("Oops, error", e);
}
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论