本篇文档的整理源自 www.jianshu.com/p/bf187fed8… 本书中的代码案例都在Node.js 0.6.11版本中测试过,能够正确工做。 读完本书以后,你将完成一个完整的web应用,该应用容许用户浏览页面以及上传文件。html
本书先从介绍在Node.js环境中进行JavaScript开发和在浏览器环境中进行JavaScript开发的差别开始。node
紧接着,会带领你们完成一个最传统的“Hello World”应用,这也是最基础的Node.js应用。git
最后,会和你们讨论如何设计一个“真正”完整的应用,剖析要完成该应用须要实现的不一样模块,并一步一步介绍如何来实现这些模块。web
能够确保的是,在这过程当中,你们会学到JavaScript中一些高级的概念、如何使用它们以及为何使用这些概念就能够实现而其余编程语言中同类的概念就没法实现。 该应用全部的源代码均可以经过 Github代码仓库查看,你也能够选择先将代码clone以后再继续进行下面的阅读。编程
1、本文档结构
2、JavaScript与Node.js
- JavaScript与你
- 简短申明
- 服务器端JavaScript
- “Hello World”
3、一个完整的基于Node.js的web应用
- 用例
- 应用不一样模块分析
4、构建应用的模块
- 一个基础的HTTP服务器
- 分析HTTP服务器
- 进行函数传递
- 函数传递是如何让HTTP服务器工做的
- 基于事件驱动的回调
- 服务器是如何处理请求的
- 服务端的模块放在哪里
- 如何来进行请求的“路由”
- 行为驱动执行
- 路由给真正的请求处理程序
- 让请求处理程序做出响应
①.很差的实现方式 ②.阻塞与非阻塞 ③.以非阻塞操做进行请求响应后端
5、更有用的场景
- 处理POST请求
- 处理文件上传
6、总结与展望
Node.js,服务端的JavaScript。----从新认识JavaScript。浏览器
本书并非一本“从入门到精通”的书,更像是一本“从初级入门到高级入门”的书。 若是成功的话,那么本书就是咱们开始学习Node.js最但愿拥有的教程。服务器
要实如今后台运行JavaScript代码,代码须要先被解释而后正确的执行。Node.js的原理正是如此,它使用了Google的V8虚拟机 (Google的Chrome浏览器使用的JavaScript执行环境),来解释和执行JavaScript代码。 Node.js事实上既是一个运行时环境,同时又是一个库 ####4."Hello World" 打开你最喜欢的编辑器,建立一个helloworld.js文件。输出“Hello World”,以下是实现该功能的代码:网络
console.log("Hello World");
复制代码
保存该文件,并经过Node.js来执行:app
node helloworld.js
复制代码
正常的话,就会在终端输出Hello World 。 第二部分结束。
咱们来把目标设定得简单点,不过也要够实际才行:
- 用户能够经过浏览器使用咱们的应用。
- 当用户请求*http://domain/start*时,能够看到一个欢迎页面,页面上有一个文件上传的表单。
- 用户能够选择一个图片并提交表单,随后文件将被上传到*http://domain/upload*,该页面完成上传后会把图片显示在页面上。
差很少了,你如今也能够去Google一下,找点东西乱搞一下来完成功能。可是咱们如今先不作这个。
更进一步地说,在完成这一目标的过程当中,咱们不只仅须要基础的代码而无论代码是否优雅。咱们还要对此进行抽象,来寻找一种适合构建更为复杂的Node.js应用的方式。
咱们来分解一下这个应用,为了实现上文的用例,咱们须要实现哪些部分呢?
- 咱们须要提供Web页面,所以须要一个HTTP服务器
- 对于不一样的请求,根据请求的URL,咱们的服务器须要给予不一样的响应,所以咱们须要一个路由,用于把请求对应到请求处理程序(request handler)
- 当请求被服务器接收并经过路由传递以后,须要能够对其进行处理,所以咱们须要最终的请求处理程序
- 路由还应该能处理POST数据,而且把数据封装成更友好的格式传递给请求处理入程序,所以须要请求数据处理功能
- 咱们不只仅要处理URL对应的请求,还要把内容显示出来,这意味着咱们须要一些视图逻辑供请求处理程序使用,以便将内容发送给用户的浏览器
- 最后,用户须要上传图片,因此咱们须要上传处理功能来处理这方面的细节 ######如今咱们就来开始实现之路,先从第一个部分--HTTP服务器着手。 ###4、构建应用的模块 ####1.一个基础的HTTP服务器 如今咱们来建立一个用于启动咱们的应用的主文件,和一个保存着咱们的HTTP服务器代码的模块。
为便于理解,把主文件叫作index.js或多或少是个标准格式。把服务器模块放进叫server.js的文件里则很好理解。
让咱们先从服务器模块开始。若是你clone了项目会发现咱们将要写的server.js文件是不一样的,从头开始,在你的项目的根目录下建立一个叫server.js的文件,通常状况下,咱们会写入如下代码:
var http = require("http");
http.createServer(function(request, response){
response.writeHead(200,{"Content-Type":"text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
复制代码
这个时候,在终端中输入
node server
,浏览器访问http://localhost:8888,你会看到一个写着“Hello World”的网页。到这里相信你们都很是很是的熟悉,可是关于这几行代码的原理与分析你是否考虑过,或者本身明白了?到这里,咱们就先来谈谈HTTP服务器的问题,而把如何组织项目的事情先放一边,我相信对于理解不深的人来讲,会让你对这几行代码有一个更清楚的理解。 ####2.分析HTTP服务器 第一行请求(require
)Node.js自带的 http 模块,而且把它赋值给 http 变量。
接下来咱们调用http模块提供的函数: createServer 。这个函数会返回一个对象,这个对象有一个叫作 listen 的方法,这个方法有一个数值参数,指定这个HTTP服务器监听的端口号。
我们暂时先无论 http.createServer 的括号里的那个函数定义。
咱们原本能够用这样的代码来启动服务器并侦听8888端口:
var server = http.createServer();
server.listen(8888);
复制代码
至此你确定名了http是Node.js自带的模块,也知道了createServer是http模块提供的函数,而且知道了这个函数返回的对象有一个listen侦听端口的方法。接下来咱们要开始对createServer函数中的内容进行一个剖析。 ####3.进行函数传递 咱们先来看几行代码,并仔细阅读它:
function say(word){
console.log(word);
}
function execute(someFunction,value){
someFunction(value);
}
execute(say,"Hello")
复制代码
代码分析(慢慢阅读,不急不急):在这里,咱们把 say 函数做为execute函数的第一个变量进行了传递。这里返回的不是 say 的返回值,而是 say 自己! 这样一来, say 就变成了execute 中的本地变量 someFunction ,execute能够经过调用 someFunction() (带括号的形式)来使用 say 函数。 固然,由于 say 有一个变量, execute 在调用 someFunction 时能够传递这样一个变量。
咱们能够,就像刚才那样,用它的名字把一个函数做为变量传递。可是咱们不必定要绕这个“先定义,再传递”的圈子,咱们能够直接在另外一个函数的括号中定义和传递这个函数:
function execute(someFunction,value){
someFunction(value)
}
execute(function(word){console.log(word)},"Hello")
复制代码
代码的啰嗦分析:
- 咱们在 execute 接受第一个参数的地方直接定义了咱们准备传递给 execute 的函数。
- 用这种方式,咱们甚至不用给这个函数起名字,这也是为何它被叫作 匿名函数 。
- 咱们如今能够接受这一点:在JavaScript中,一个 函数能够做为另外一个函数接收一个参数。咱们能够先定义一个函数,而后传递,也能够在传递参数的地方直接定义函数。
本章总共11节,本章第三节结束。 ####4.函数传递是如何让HTTP服务器工做的
带着刚才咱们刚学习的一点小东西(若是有丁点疑惑,返回去继续瞅一瞅,确保彻底明白每一个字节的意思),咱们来看看咱们简约而不简单的HTPP服务器:
var http = require("http");
http.createServer(function(request, response){
response.writeHead(200,{"Content-Type":"text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);</pre>
复制代码
是的,这个时候你已经知道,咱们向createServer函数传递了一个匿名函数。咱们能够将上面代码进行改观与前面的知识进行碰撞(一样的目的与做用):
var http = require("http");
function onRequest(request, response){
response.writeHead(200,{"Content-Type":"text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
复制代码
这个时候你有没有疑问?咱们为何要用这样的方式呢?对于刚刚开始学习看到这里的我来讲,我确定不会去想这个问题的,开玩笑,我甚至都没去想http这个东西是node自带的模块。
那么做者提出的这个为何,究竟是想要说什么?咱们继续进行下一节--【基于事件驱动的回调】,什么是【基于事件驱动的回调】?这是什么东西? 这个【基于事件驱动的回调】是个什么东西?重要的文字写三遍。
本章总共十一节,下面咱们开始第五节的内容。 ####5.基于事件驱动的回调 因而我带着【基于事件驱动的回调】这个名词来到本节的时候,做者首先说了一句“这个问题很差回答(至少对于我来讲)”
而后关于这个事件驱动回调的背景知识,做者给了一篇文章的连接,感兴趣的能够去瞅瞅:debuggable.com/posts/under…
这一切都归结于“Node.js是事件驱动的”这一事实。好吧,其实我也不是特别确切的了解这句话的意思。不过我会试着解释,为何它对咱们用Node.js写网络应用(Web based application)是有意义的。
- 当咱们使用 http.createServer 方法的时候,咱们固然不仅是想要一个侦听某个端口的服务器,咱们还想要它在服务器收到一个HTTP请求的时候作点什么。
问题是,这是异步的:请求任什么时候候均可能到达,可是咱们的服务器却跑在一个单进程中. 在咱们的Node.js程序中,当一个新的请求到达8888端口的时候,咱们怎么控制流程呢?
- 咱们建立了服务器,而且向建立它的方法传递了一个函数。不管什么时候咱们的服务器收到一个请求,这个函数就会被调用。
- 咱们不知道这件事情何时会发生,可是咱们如今有了一个处理请求的地方:它就是咱们传递过去的那个函数。至于它是被预先定义的函数仍是匿名函数,就可有可无了
- 这个就是传说中的 回调 。咱们给某个方法传递了一个函数,这个方法在有相应事件发生时调用这个函数来进行 回调 。
让咱们再来琢磨琢磨这个新概念。咱们怎么证实,在建立完服务器以后,即便没有HTTP请求进来、咱们的回调函数也没有被调用的状况下,咱们的代码还继续有效呢?咱们试试这个:
var http = require("http");
function onRequest(request, response){
console.log("Request received.");
response.writeHead(200,{"Content-Type":"text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
复制代码
代码与解析分析: 当咱们与往常同样,运行它
node server.js
时,它会立刻在命令行上输出“Server has started.”。当咱们向服务器发出请求(在浏览器访问http://localhost:8888/ ),“Request received.”这条消息就会在命令行中出现。
这就是事件驱动的异步服务器端JavaScript和它的回调啦!
(请注意,当咱们在服务器访问网页时,咱们的服务器可能会输出两次“Request received.”。那是由于大部分服务器都会在你访问 http://localhost:8888 /时尝试读取 http://localhost:8888/favicon.ico )
######本节总结:说了一大堆,经过代码咱们能够简单理解,服务器启动执行后端console
方法,客户端向服务器发出请求,再执行另外一个console
方法,这就是事件驱动的异步服务端JavaScript和它的回调。关于这个问题可自行多多研究。 本章总共11节,本章第五节结束。 ####6.服务器是如何处理请求的
好的,接下来咱们简单分析一下咱们服务器代码中剩下的部分,也就是咱们的回调函数 onRequest() 的主体部分。
当回调启动,咱们的 onRequest() 函数被触发的时候,有两个参数被传入: request 和 response 。
它们是对象,你可使用它们的方法来处理HTTP请求的细节,而且响应请求(好比向发出请求的浏览器发回一些东西)。
因此咱们的代码就是:当收到请求时,使用 response.writeHead() 函数发送一个HTTP状态200和HTTP头的内容类型(content-type),使用 response.write() 函数在HTTP相应主体中发送文本“Hello World"。
最后,咱们调用 response.end() 完成响应。
目前来讲,咱们对请求的细节并不在乎,因此咱们没有使用 request 对象。 ###7.服务端的模块放在哪里 到这里咱们开始说
如何组织应用
这个问题上。 把服务器脚本放到一个叫作start的函数里,而后导出这个函数,在主文件中使用:
#server.js
var http = require("http");
function start(){
function onRequest(request, response){
console.log("Request received.");
response.writeHead(200,{"Content-Type":"text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
#index.js
var server = require("./server");
server.start();
复制代码
咱们仍然只拥有整个应用的最初部分:咱们能够接收HTTP请求。可是咱们得作点什么——对于不一样的URL请求,服务器应该有不一样的反应。对于一个很是简单的应用来讲,你能够直接在回调函数 onRequest() 中作这件事情,可是,固然应用不会这么简单了。 处理不一样的HTTP请求在咱们的代码中是一个不一样的部分,叫作“路由选择”——那么,咱们接下来就创造一个叫作 路由 的模块吧。
本章总共11节,本章第⑦节结束。 ####8.如何来进行请求的“路由”
咱们要为路由提供请求的URL和其余须要的GET及POST参数,随后路由须要根据这些数据来执行相应的代码.
所以,咱们须要查看HTTP请求,从中提取出请求的URL以及GET/POST参数。
咱们须要的全部数据都会包含在request对象中,该对象做为onRequest()回调函数的第一个参数传递。可是为了解析这些数据,咱们须要额外的Node.JS模块,它们分别是
ur
l和querystring
模块。
如今咱们来给onRequest()函数加上一些逻辑,用来找出浏览器请求的URL路径:
var http = require("http");
var url = require("url");
function start(){
function onRequest(request, response){
var pathname = url.parse(request.url).pathname;
console.log("Request for "+ pathname +" received.");
response.writeHead(200,{"Content-Type":"text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
复制代码
将上面代码进行运行后,输入不一样的url能够看到会输出不一样的
console
,咱们能够经过请求的URL不一样来区别不一样的请求了。
如今咱们能够来编写路由了,创建一个名为
router.js
的文件,添加如下内容:
function route(pathname){
console.log("About to route a request for "+ pathname);
}
exports.route = route;
复制代码
这个时候,
index.js
、server.js
、rounter.js
全都有了,如何互相关联就不赘述了。直接进入下一节。 ###9.行为驱动执行 谈一谈函数编程: 将函数做为参数传递并不只仅出于技术上的考量。对软件设计来讲,这实际上是个哲学问题。想一想这样的场景:在index文件中,咱们能够将router对象传递进去,服务器随后能够调用这个对象的route函数。
就像这样,咱们传递一个东西,而后服务器利用这个东西来完成一些事。嗨那个叫路由的东西,能帮我把这个路由一下吗?
可是服务器其实不须要这样的东西。它只须要把事情作完就行,其实为了把事情作完,你根本不须要东西,你须要的是动做。也就是说,你不须要名词,你须要动词。
理解了这个概念里最核心、最基本的思想转换后,我天然而然地理解了函数编程。
听说这篇文章会让你对函数编程有一个更好的理解:steve-yegge.blogspot.com/2006/03/exe…