Node初学者入门,一本全面的NodeJS教程

JavaScript与Node.js

JavaScript与你

抛开技术,我们先来聊聊你以及你和JavaScript的关系。本章的主要目的是想让你看看,对你而言是否有必要继续阅读后续章节的内容。

如果你和我一样,那么你很早就开始利用HTML进行“开发”,正因如此,你接触到了这个叫JavaScript有趣的东西,而对于JavaScript,你只会基本的操作——为web页面添加交互。

而你真正想要的是“干货”,你想要知道如何构建复杂的web站点 —— 于是,你学习了一种诸如PHP、Ruby、Java这样的编程语言,并开始书写“后端”代码。

与此同时,你还始终关注着JavaScript,随着通过一些对jQuery,Prototype之类技术的介绍,你慢慢了解到了很多JavaScript中的进阶技能,同时也感受到了JavaScript绝非仅仅是window.open()那么简单。 .

不过,这些毕竟都是前端技术,尽管当想要增强页面的时候,使用jQuery总让你觉得很爽,但到最后,你顶多是个JavaScript用户,而非JavaScript开发者

然后,出现了Node.js,服务端的JavaScript,这有多酷啊?

于是,你觉得是时候该重新拾起既熟悉又陌生的JavaScript了。但是别急,写Node.js应用是一件事情;理解为什么它们要以它们书写的这种方式来书写则意味着——你要懂JavaScript。这次是玩真的了。

问题来了: 由于JavaScript真正意义上以两种,甚至可以说是三种形态存在(从中世纪90年代的作为对DHTML进行增强的小玩具,到像jQuery那样严格 意义上的前端技术,一直到现在的服务端技术),因此,很难找到一个“正确”的方式来学习JavaScript,使得让你书写Node.js应用的时候感觉 自己是在真正开发它而不仅仅是使用它。

因为这就是关键: 你本身已经是个有经验的开发者,你不想通过到处寻找各种解决方案(其中可能还有不正确的)来学习新的技术,你要确保自己是通过正确的方式来学习这项技术。

当然了,外面不乏很优秀的学习JavaScript的文章。但是,有的时候光靠那些文章是远远不够的。你需要的是指导。

本书的目标就是给你提供指导。更多学习资料请点击此处

简短申明

业界有非常优秀的JavaScript程序员。而我并非其中一员。

我就是上一节中描述的那个我。我熟悉如何开发后端web应用,但是对“真正”的JavaScript以及Node.js,我都只是新手。我也只是最近学习了一些JavaScript的高级概念,并没有实践经验。

因此,本书并不是一本“从入门到精通”的书,更像是一本“从初级入门到高级入门”的书。

如果成功的话,那么本书就是我当初开始学习Node.js最希望拥有的教程。

服务端JavaScript

JavaScript最早是运行在浏览器中,然而浏览器只是提供了一个上下文,它定义了使用JavaScript可以做什么,但并没有“说”太多关于 JavaScript语言本身可以做什么。事实上,JavaScript是一门“完整”的语言: 它可以使用在不同的上下文中,其能力与其他同类语言相比有过之而无不及。

Node.js事实上就是另外一种上下文,它允许在后端(脱离浏览器环境)运行JavaScript代码。

要实现在后台运行JavaScript代码,代码需要先被解释然后正确的执行。Node.js的原理正是如此,它使用了Google的V8虚拟机 (Google的Chrome浏览器使用的JavaScript执行环境),来解释和执行JavaScript代码。

除此之外,伴随着Node.js的还有许多有用的模块,它们可以简化很多重复的劳作,比如向终端输出字符串。

因此,Node.js事实上既是一个运行时环境,同时又是一个库。

要使用Node.js,首先需要进行安装。关于如何安装Node.js,这里就不赘述了,可以直接参考官方的安装指南。安装完成后,继续回来阅读本书下面的内容。

“Hello World”

好了,“废话”不多说了,马上开始我们第一个Node.js应用:“Hello World”。

打开你最喜欢的编辑器,创建一个helloworld.js文件。我们要做就是向STDOUT输出“Hello World”,如下是实现该功能的代码:

console.log("Hello World");

保存该文件,并通过Node.js来执行:

node helloworld.js

正常的话,就会在终端输出Hello World

好吧,我承认这个应用是有点无趣,那么下面我们就来点“干货”。

一个完整的基于Node.js的web应用

用例

我们来把目标设定得简单点,不过也要够实际才行:

用户可以通过浏览器使用我们的应用。

当用户请求http://domain/start时,可以看到一个欢迎页面,页面上有一个文件上传的表单。

用户可以选择一个图片并提交表单,随后文件将被上传到http://domain/upload,该页面完成上传后会把图片显示在页面上。

差不多了,你现在也可以去Google一下,找点东西乱搞一下来完成功能。但是我们现在先不做这个。

更进一步地说,在完成这一目标的过程中,我们不仅仅需要基础的代码而不管代码是否优雅。我们还要对此进行抽象,来寻找一种适合构建更为复杂的Node.js应用的方式。

应用不同模块分析

我们来分解一下这个应用,为了实现上文的用例,我们需要实现哪些部分呢?

我们需要提供Web页面,因此需要一个HTTP服务器

对于不同的请求,根据请求的URL,我们的服务器需要给予不同的响应,因此我们需要一个路由,用于把请求对应到请求处理程序(request handler)

当请求被服务器接收并通过路由传递之后,需要可以对其进行处理,因此我们需要最终的请求处理程序

路由还应该能处理POST数据,并且把数据封装成更友好的格式传递给请求处理入程序,因此需要请求数据处理功能

我们不仅仅要处理URL对应的请求,还要把内容显示出来,这意味着我们需要一些视图逻辑供请求处理程序使用,以便将内容发送给用户的浏览器

最后,用户需要上传图片,所以我们需要上传处理功能来处理这方面的细节

我们先来想想,使用PHP的话我们会怎么构建这个结构。一般来说我们会用一个Apache HTTP服务器并配上mod_php5模块。

从这个角度看,整个“接收HTTP请求并提供Web页面”的需求根本不需要PHP来处理。

不过对Node.js来说,概念完全不一样了。使用Node.js时,我们不仅仅在实现一个应用,同时还实现了整个HTTP服务器。事实上,我们的Web应用以及对应的Web服务器基本上是一样的。

听起来好像有一大堆活要做,但随后我们会逐渐意识到,对Node.js来说这并不是什么麻烦的事。

现在我们就来开始实现之路,先从第一个部分--HTTP服务器着手。

构建应用的模块

一个基础的HTTP服务器

当我准备开始写我的第一个“真正的”Node.js应用的时候,我不但不知道怎么写Node.js代码,也不知道怎么组织这些代码。

我应该把所有东西都放进一个文件里吗?网上有很多教程都会教你把所有的逻辑都放进一个用Node.js写的基础HTTP服务器里。但是如果我想加入更多的内容,同时还想保持代码的可读性呢?

实际上,只要把不同功能的代码放入不同的模块中,保持代码分离还是相当简单的。

这种方法允许你拥有一个干净的主文件(main file),你可以用Node.js执行它;同时你可以拥有干净的模块,它们可以被主文件和其他的模块调用。

那么,现在我们来创建一个用于启动我们的应用的主文件,和一个保存着我们的HTTP服务器代码的模块。

在我的印象里,把主文件叫做index.js或多或少是个标准格式。把服务器模块放进叫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);

搞定!你刚刚完成了一个可以工作的HTTP服务器。为了证明这一点,我们来运行并且测试这段代码。首先,用Node.js执行你的脚本:

node server.js

接下来,打开浏览器访问http://localhost:8888/,你会看到一个写着“Hello World”的网页。

这很有趣,不是吗?让我们先来谈谈HTTP服务器的问题,把如何组织项目的事情先放一边吧,你觉得如何?我保证之后我们会解决那个问题的。

分析HTTP服务器

那么接下来,让我们分析一下这个HTTP服务器的构成。

第一行请求(require)Node.js自带的http模块,并且把它赋值给http变量。

接下来我们调用http模块提供的函数:createServer。这个函数会返回一个对象,这个对象有一个叫做listen的方法,这个方法有一个数值参数,指定这个HTTP服务器监听的端口号。

咱们暂时先不管http.createServer的括号里的那个函数定义。

我们本来可以用这样的代码来启动服务器并侦听8888端口:

var http = require("http");

var server = http.createServer();

server.listen(8888);

这段代码只会启动一个侦听8888端口的服务器,它不做任何别的事情,甚至连请求都不会应答。

最有趣(而且,如果你之前习惯使用一个更加保守的语言,比如PHP,它还很奇怪)的部分是createSever()的第一个参数,一个函数定义。

实际上,这个函数定义是createServer()的第一个也是唯一一个参数。因为在JavaScript中,函数和其他变量一样都是可以被传递的。

进行函数传递

举例来说,你可以这样做:

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的第一次亲密接触,不过我们还是得循序渐进。现在,我们先接受这一点:在JavaScript中,一个 函数可以作为另一个函数接收一个参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。

函数传递是如何让HTTP服务器工作的

带着这些知识,我们再来看看我们简约而不简单的HTTP服务器:

var http = require("http");

http.createServer(function(request, response){

response.writeHead(200,{"Content-Type":"text/plain"});

response.write("Hello World");

response.end();

}).listen(8888);

现在它看上去应该清晰了很多:我们向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);

也许现在我们该问这个问题了:我们为什么要用这种方式呢?

基于事件驱动的回调 实在太多,有兴趣的朋友可点击阅读全文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    w_zhuan阅读 3,607评论 2 41
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    Myselfyan阅读 4,064评论 2 58
  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,305评论 0 6
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 日总结20170811:玫瑰原义玫:在玉石上刻绘美丽的花纹瑰:难以描述的美丽的玉石写法玫️斜玉旁, 以竖1线为中心...
    月诺兮阅读 177评论 0 0