网上许多nodejs教程或书藉都是教你调用第三方模块来编写nodejs应用的,虽然这是非常便捷的,但是封装太厚,你基本一点东西还是没有学到。人家的模块,人家想怎么改就行,可以下一版本就改了接口,你的应用就完蛋了。比如说google,他就爱干这种事情。因此我们还得老老实实学习底层API吧。

本节首先教大家跑起一个页面吧。

我在以前就写一篇相关的, node.js 一个简单的页面输出,大家可以先预习一下。

一般来说,大家都是从这样一个例子入门

var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello node.js");
response.end();
}).listen(8888);

上面脚本写在一个app.js上面,然后打开控制台,定位于它所在目录,输入node app,再打开某一浏览器,输入http://localhost:8888/就看出效果。

分析上面脚本:

  1. 引入依赖,这是前端AMD规范未流行前, 非常神奇的东西。不过一个项目这么大,肯定要分成N个目录N个文件,各人负责一部分,减少合并冲突的风险
  2. require("http")吐出的http对象是一个原生对象,类似于前端的alert, setTimeout,非常常用。而这个http对象更是后端的中流砥柱,WEB应用离不开它
  3. http.createServer方法要接收一个函数,俗称回调。以后你会看到越来越多回调,nodejs的世界就是回调的世界,nodejs的发展史就是跟回调的抗争史
  4. 这个回调传给你两个重要对象,请求对象与响应对象。response要往前端输出东西,我们需要设置响应头(response.writeHead),告诉浏览器接着下来要怎么处理我们的内容。因为有的东西可能会当成script脚本,有的当成图片,有的当成CSS文件,有的要当成附件下载……response.write就是用来输出内容。输出结束了要调用end方法,这其实是方便response执行end回调。
  5. http.createServer 其实是返回一个Server实例,它有一个listen方法,它是用于监听某一端口。

ok,这样就完了。我们深入一点吧(这里比较难,可以跳过,直接看下一个★★★)。

我们打开这里,查看http模块的源码,它大抵调用了

_http_incoming
_http_outgoing
_http_server
_http_client

这四个重要的内部模块,它们是我们在nodejs环境中访问不到。http还引用其他内部模块,但现在可以不理会它们。_http_incoming,_http_outgoing是提供两个输入输出对象,流是以后我们重点学习的东西。在本模块中,除去哪些已经废弃的方法,主要是这三个方法:get, request , createServer

我将http源码删减一下,大家就明白什么回事了:


//------------------Server------------------
var server = require('_http_server');
exports.ServerResponse = server.ServerResponse;
var Server = exports.Server = server.Server; exports.createServer = function(requestListener) {
return new Server(requestListener);
}; //------------------Client------------------
var client = require('_http_client');
var ClientRequest = exports.ClientRequest = client.ClientRequest; exports.request = function(options, cb) {
return new ClientRequest(options, cb);
}; exports.get = function(options, cb) { //get是request方法的包装
var req = exports.request(options, cb);
req.end();
return req;
};

那么requestListener是怎么传进去的呢?我们到_http_server内部模块去,发现ServerResponse只是OutgoingMessage的子类,Server实例是通过request事件来绑定我们的createServer回调,同时它也通过connection事件绑定一个connectionListener的方法,connectionListener巨长,里面会emit request事件。

    if (!util.isUndefined(req.headers.expect) &&
(req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
continueExpression.test(req.headers['expect'])) {
res._expect_continue = true;
if (EventEmitter.listenerCount(self, 'checkContinue') > 0) {
self.emit('checkContinue', req, res);
} else {
res.writeContinue();
self.emit('request', req, res);
}
} else {
self.emit('request', req, res);
}

res是ServerResponse的实例var res = new ServerResponse(req);,req是parserOnIncoming方法传进来的,而parserOnIncoming则是 parser.onIncoming 的一个方法.
追踪到_http_common内部模块,发现以下几句:


   parser.onIncoming(parser.incoming, info.shouldKeepAlive)//毫无疑问,parser.incoming就是我们的req对象

   parser.incoming = new IncomingMessage(parser.socket);

好了,一切真相大白,req是IncomingMessage的实例, res是ServerResponse亦即OutgoingMessage的实例。这里不得不吐槽,nodejs的源码太混乱了!

★★★我们又切换为简单模式。明白以上那段话,我们就知道我们为什么需要重点学习“http.ServerResponse”与“http.IncomingMessage”,如果大家看过express的源码,就会发现其request模块与response模块就在这上面进行扩展的。

//https://github.com/strongloop/express/blob/master/lib/request.js
var req = exports = module.exports = {
__proto__: http.IncomingMessage.prototype
};
//https://github.com/strongloop/express/blob/master/lib/response.js
var res = module.exports = {
__proto__: http.ServerResponse.prototype
};

在好久之前,我们是通过fs模块的 fs.readFile 来读取服务器上的某个文件返回给前端:


var http = require("http");
var fs = require('fs');
exports.start = function(){
http.createServer(function(request, response) {
fs.readFile('./index.html', 'utf-8',function (err, data) {//读取内容
if (err) throw err;
response.writeHead(200, {"Content-Type": "text/html"});//注意这里
response.write(data);
response.end();
});
}).listen(8888);
console.log("server start...");
}

前面已经说过,现在已经是流的时代了,response是一个可写流(对应可读流),我们创建一个可读流就行了。

var http = require("http");
var fs = require("fs")
http.createServer(function (request, response) {
var readable = fs.createReadStream("./index.html")
response.writeHead(200, {"Content-Type": "text/html"});
readable.pipe(response); }).listen(8888);

如果发生错误想跳转到404页面

var http = require("http");
var fs = require("fs")
http.createServer(function (request, response) {
var readable = fs.createReadStream("./index.html")
response.writeHead(200, {"Content-Type": "text/html"});
readable.pipe(response);
readable.on("error", function () {
var readable = fs.createReadStream("./404.html")
readable.pipe(response);
})
}).listen(8888);

原生nodejs 学习笔记1的更多相关文章

  1. 原生nodejs 学习笔记2

    本章节学习流, 流的一个好处在于减少各种异步IO的回调地狱.IO操作遍及我们各种操作,比如数据库读写,文件读写, 文件转换压缩--别的不说,比如第一节,我们要将一个HTML文件返回浏览器,就涉及IO操 ...

  2. Nodejs学习笔记(四)——支持Mongodb

    前言:回顾前面零零碎碎写的三篇挂着Nodejs学习笔记的文章,着实有点名不副实,当然,这篇可能还是要继续走着离主线越走越远的路子,从简短的介绍什么是Nodejs,到如何寻找一个可以调试的Nodejs ...

  3. Nodejs学习笔记(三)——一张图看懂Nodejs建站

    前言:一条线,竖着放,如果做不到精进至深,那就旋转90°,至少也图个幅度宽广. 通俗解释上面的胡言乱语:还没学会爬,就学起走了?! 继上篇<Nodejs学习笔记(二)——Eclipse中运行调试 ...

  4. Nodejs学习笔记(二)——Eclipse中运行调试Nodejs

    前篇<Nodejs学习笔记(一)——初识Nodejs>主要介绍了在搭建node环境过程中遇到的小问题以及搭建Eclipse开发Node环境的前提步骤.本篇主要介绍如何在Eclipse中运行 ...

  5. NodeJS学习笔记之Connect中间件模块(一)

    NodeJS学习笔记之Connect中间件模块(一) http://www.jb51.net/article/60430.htm NodeJS学习笔记之Connect中间件模块(二) http://w ...

  6. Nodejs学习笔记(六)--- Node.js + Express 构建网站预备知识

    目录 前言 新建express项目并自定义路由规则 如何提取页面中的公共部分? 如何提交表单并接收参数? GET 方式 POST 方式 如何字符串加密? 如何使用session? 如何使用cookie ...

  7. Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...

  8. Nodejs学习笔记(十六)--- Pomelo介绍&入门

    目录 前言&介绍 安装Pomelo 创建项目并启动 创建项目 项目结构说明 启动 测试连接 聊天服务器 新建gate和chat服务器 配置master.json 配置servers.json ...

  9. [转]Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    本文转自:https://www.cnblogs.com/zhongweiv/p/nodejs_koa2_webapp.html 目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装 ...

随机推荐

  1. 高大上的JS工具

    EtherPad (协同文件编辑): EtherCalc (协同电子表格编辑)

  2. bzoj 3572 [Hnoi2014]世界树——虚树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3572 关于虚树:https://www.cnblogs.com/zzqsblog/p/556 ...

  3. 浅谈fhq_treap

    \(BST\) 二叉查找树,首先它是一颗二叉树,其次它里面每个点都满足以该点左儿子为根的子树里结点的值都小于自己的值,以该点右儿子为根的子树里结点的值都大于自己的值.如果不进行修改,每次查询都是\(O ...

  4. 我的Quartz笔记

    代码: package com.smt.autorun; import java.io.File; import java.util.ArrayList; import java.util.Date; ...

  5. form 表单提交浏览器的enctype(编码方式)

    1. method 为 get 时 enctype :x-www-form-urlencoded(默认), 把form数据append到对应的url后面: 2. method 为 post 时 Bro ...

  6. java操作Excel之POI(5)利用POI实现使用模板批量导出数据

    后台导出方法: 在源文件夹src下面放个准备好的模板:/com/cy/template/userExportTemplate.xls,这个模板有头部一行: /** * 后台导出方法 * 利用POI实现 ...

  7. java实验三——求平均数,数组排序(有关java保留小数位数,由于编译器版本未到1.5导致的报错format函数第二个参数不对,要求是Object[])

    package hello; import java.util.Arrays; public class 实验三更正版 { public static void main(String[] args) ...

  8. 9-16Jenkins-1第一个任务

    jenkins安装搭建 #mac安装jenkins brew install jenkins #或下载war包 java -jar jenkins.war 第一个jenkins任务 创建一个自由风格的 ...

  9. 补充 3:Golang 一些特性

    1 丰富的内置类型 2 函数多返回值 3 Go的错误处理 :   Go语言引入了3个关键字用于标准的错误处理流程,这3个关键字分别为defer. panic和 recover 4 在Go语言中,所有的 ...

  10. Scrapy爬取人人网

    Scrapy发送Post请求 防止爬虫被反主要有以下几个策略 动态设置User-Agent(随机切换User-Agent,模拟不同用户的浏览器信息) 禁用Cookies(也就是不启用cookies m ...