转眼辞职自学前端也有半年了,一路看过“JavaScript DOM编程艺术”、“高程三”,也在慕课网的前端职业路径里学了一些课,一直没有掌握ajax的相关知识,看了一下慕课网的“Ajax全接触”这门课,但是后端部分要用PHP,就没有继续。直到上个月用node.js和express框架初步学习了一下后端的知识,这几天又想结合掌握的后端知识好好学一学ajax,就用“JavaScript DOM编程艺术”里讲解ajax的章节的例子做了一个应用了ajax的小demo。
这里借这篇手记总结一下基本的概念,以及这个小的demo。
非初学者和已经掌握ajax的小伙伴应该不需要看了。如果你也在入门前端,正好刚学了一点node.js和express框架,想实践一下ajax,这个demo是很简单也不费时的。
基本概念小结
概念:
- Asynchronous JavaScript + XML
- 本质还是Http请求
优点: - 只更新页面的一小部分
- 异步性:对页面的请求以异步方式发送到服务器,服务器不会用整个页面来响应请求,它会在后台处理请求,等待响应的同时用户还能继续浏览页面并与页面交互。
缺点: - 缺少状态记录,用户无法使用后退按钮或者无法为特定状态下的页面添加书签
- 需要开发人员在向服务器发出请求和服务器返回响应时,给用户明确提示
- 需要浏览器支持JavaScript(现在应该大部分都支持了)
核心:
XMLHttpRequest对象(XHR,):浏览器中的脚本(客户端)与服务器之间的中间人的角色,负责传递请求和响应。 - 普通请求:浏览器发出
- Ajax请求:通过XMLHttpRequest对象发送请求,同时也自己处理响应
发送ajax请求和接收响应的demo
我把项目代码放在github上了:项目文件与代码.
demo简单效果是点击页面中的p元素发起ajax请求,请求指向example.txt文件,读取文件中的文本,然后显示在下面的div元素中。非常简单吧,因为我也是初学者_
我的基本开发环境:Windows10,node 5.11.0,express 4.13.4。
假设你已经装好了node和express(安装方法参考node.js和express的官网),在命令行中执行:express -e getFile
其中 “-e” 代表选择ejs模板引擎,getFile是你项目文件夹的名字,接着cd进入项目文件夹,安装依赖:
cd node-express-ajax-craigslist
npm install
项目文件夹内文档构成如下图:
-
app.js是服务端启动文件,或者说入口文件,启动服务器的服务端js代码放在这个文件中
-
public文件夹中还有styles,javascripts和images三个文件夹,其中styles文件夹存放样式表文件,javascripts文件夹存放客户端js文件,这些对前端来说应该比较熟悉了
-
views文件夹里放的是ejs格式的页面模板文件
-
routes文件夹里放的是路由处理的js代码
打开app.js,在
var app = express();
后面加上app.set('port', process.env.PORT || 3000);
设置监听端口;将app.use(function(req, res, next) {
var err = new Error(‘Not Found’);
err.status = 404;
next(err);
});
删掉,在module.exports = app;
前面加上
app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
让应用绑定并监听前面设置的3000端口。
安装supervisor模块方便调试npm install -g supervisor
这样每次修改代码后再到浏览器中刷新就能看到变化是否生效。接下来,使用命令:supervisor app.js
启动app.js,在浏览器中打开 http://localhost:3000/ ,应该能看到下面的页面
接着我们来完成路由部分的代码,这个简单的demo只有一个路由,可以把实现路由功能的代码放在app.js里,但是随着网站规模扩大和路由的复杂化,app.js文件会变得臃肿和难以维护,这不符合代码模块化的思想,所以为了养成好习惯,我还是把这个简单的路由放在 routes/index.js 里。express官方给出的写法是在 app.js 中实现简单的路由分配,然后再去 index.js 中找到对应的路由函数,最终实现路由功能。我把路由控制器和实现路由功能的函数都放到 index.js 里,app.js 中只有一个总的路由接口。最终把app.js修改如下:
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var routes = require('./routes/index');
var app = express();
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
routes(app);
app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
module.exports = app;
修改 index.js 如下:
module.exports = function (app) {
app.get('/',function(req,res) {
res.render('index', {
title: "Ajax"
});
});
};
当用户发送HTTP GET请求到"/",index.ejs被渲染,传入title变量,然后发送给浏览器。接着我们修改index.ejs文件如下:
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p id="trigger">A demo of <%= title %>, click this paragraph to request a text file using ajax</p>
<div id="new"></div>
<script src="/javascripts/script.js"></script>
</body>
</html>
这时在浏览器中刷新页面,应该是这样的:
然后我们在public文件夹下新建files文件夹,在files文件夹里新建example.txt文件,在里面写上“This was loaded asynchronously!”,做为服务器的响应。
只学过前端的小伙伴看了上面这些可能一下子不太好接受,没关系,后面要说的才是ajax的内容。
Hi,你还在看嘛_,看来你是和我一样爱学习的,哈哈,程序员就是要爱学习~
接着在javascripts文件夹内新建script.js,也就是我们浏览器端的js代码,发送ajax请求并处理响应就在这部分代码里完成:
window.onload = function () {
var trigger = document.getElementById('trigger');
trigger.onclick = getNewContent;
}
function getHTTPObject () {
// IE7之前的版本中不支持原生的XHR对象
if (typeof XMLHttpRequest === "undefined") {
XMLHttpRequest = function() {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
catch (e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
catch (e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP"); }
catch (e) {}
return false;
}
}
return new XMLHttpRequest();
}
function getNewContent () {
var request = getHTTPObject();
if (request) {
// open方法的三个参数分别是,请求类型,请求文件的路径(URL 相对于执行代码的当前页面或者是绝对路径)和是否异步(布尔值)
request.open('GET', "/files/example.txt", true);
// 服务器给XMLHttpRequest对象送回响应的时候被触发执行
request.onreadystatechange = function () {
if (request.readyState === 4) {
var para = document.createElement("p");
var txt = document.createTextNode(request.responseText);
para.appendChild(txt);
document.getElementById('new').appendChild(para);
}
};
// 这里的send()方法接收一个参数,即要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入null,因为这个参数对有些浏览器来说是必需的。调用send()之后,请求就会被分派到服务器。
request.send(null);
} else{
alert('Sorry, your browser doesn\'t support XMLHttpRequest');
}
}
以上代码主要用到了两个函数:
-
getHTTPObject返回一个新的XHR对象,由于IE7之前的版本并不支持原生的XHR对象,所以要先用对象检测技术检测浏览器是否支持XHR对象,如果不支持则返回IE所支持的ActiveX对象,如果都不支持则
return false
。 -
getNewContent发出ajax请求和处理响应,request是一个XHR对象的实例,这里我调用XHR对象的open方法对文件发出一个GET请求,open方法有三个参数,请求的方法(GET,POST……),请求文件的路径和是否异步发送请求的布尔值(true-异步,false-同步),可见ajax虽然名字里叫异步,但也可以发送同步请求;onreadystatechange是一个事件,给它绑定的处理函数会在服务器给XHR对象发回响应时执行;XHR对象的readyState表示请求/响应过程的当前活动阶段。这个属性可取的值如下:
- 0:未初始化。尚未调用open()方法。
- 1:启动。已经调用open()方法,但尚未调用send()方法。
- 2:发送。已经调用send()方法,但尚未接收到响应。
- 3:接收。已经接收到部分响应数据。
- 4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。
只要readyState属性的值变成4,服务器发送来的数据准备就绪,就可以访问了。
在指定了请求目标,也明确了如何处理响应,就可以用send方法发送请求了,这里的send()方法接收一个参数,即要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入null,因为这个参数对有些浏览器来说是必需的。
给页面中原本的p元素绑定onclick事件,点击p元素就异步发起ajax请求,过程演示如下:
在浏览器中打开firebug,选择“网络”面板,然后打开 http://localhost:3000/
会显示和之前相同的页面
这个时候看firebug的网络面板,应该可以看到:
此时只有三个HTTP请求,然后点击“A demo of Ajax, click this paragraph to request a text file using ajax”这一行,可以看到
页面中加入了服务器响应txt文件中的文字!此时再看firebug的网络面板,会发现多了一个GET请求,这正是ajax所发送的:你还在吗_,没想到你会一路跟到这里,那么到这里,我们就完成了一个最最最最基本的ajax的小小小demo,不是很难吧,作为一个初学者,我弱弱的认为ajax最最最最基本的内容就是这些了,不过还有最后一个小问题,也是很重要的,既然你都到这里了,就看完吧~
前面提到这个请求时异步发送的,那么异步和同步有什么区别呢,简单的说就是js脚本在发送ajax请求后,如果不用等待服务器响应就继续执行Ajax之后的代码,就是异步的,如果要等待服务器响应并执行处理响应的函数才能继续执行后面的代码就是同步的,利用这个小demo怎么来验证异步性呢?
很简单,在script.js文件中,在if (request.readyState === 4) {
这一行后面加上:alert('Response Received!');
在getNewContent函数的最后加上alert('Function Done!');
看看哪个会先弹出来呢?答案太简单,我就不说啦,有兴趣可以自己试试啦~
参考资料:
JavaScript DOM 编程艺术第二版7.4节
JavaScript 高级程序设计第三版第21章
Node.js开发指南
handling ajaxcalls with node.js and express .
一个基于express框架的多人博客系统
慕课网firebug课程
本作品采用知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。要查看该许可协议,可访问 http://creativecommons.org/licenses/by-nc-sa/4.0/ 或者写信到 Creative Commons, PO Box 1866, Mountain View, CA 94042, USA。
热门评论
文章写的不错,排版有待加强。
app.set('port', process.env.PORT || 3000);
自学半年就到这程度,你是个天才