学习内容
学习笔记
1. 一个基础的HTTP服务器
[Node入门] 主文件叫做index.js或多或少是个标准格式,把服务器模块放进叫server.js的文件
// 使用require指令载入http模块
const http = require('http');
// 创建主机名和端口
const hostname = '127.0.0.1';
const port = 1337;
// 创建服务器方法http.createServer()
http.createServer((req, res) => {
// 发送http请求头,http状态码200,内容类型
res.writeHead(200, { 'Content-Type': 'text/plain' });
// 发送相应数据'Hello World'
res.end('Hello World\n');
}).listen(port, hostname, () => {
// 终端listen端口,主机
console.log(`Server running at http://${hostname}:${port}/`);
});
方法:http.createServer([requestListener])
说明:返回一个http.Server的新实例
来源:https://nodejs.org/dist/latest-v5.x/docs/api/http.html
注:’Hello World’ 样例来自NodeJs官网(https://nodejs.org/en/about/),使用了ES6语法。
2. 基于事件驱动的回调
2.1 阻塞、非阻塞、同步、异步
说明阻塞,非阻塞,异步,同步的一个来自网络的例子,作者不明:
老张爱喝茶,废话不说,煮开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步阻塞)
老张觉得自己有点傻
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)
老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。
所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
2.2 回调
[Node入门] 我们给某个方法传递了一个函数,这个方法在有相应事件发生时调用这个函数来进行回调 。
个人理解:
1. 老张 => 服务器, 水壶 => 用户请求。我们不知道水壶什么时候烧开水,亦即服务器不知道用户请求什么时候到来。
2. 假如使用响水壶,响水壶响了,亦即所谓“有相应事件发生时”。
3. Node不像PHP,每多一个水壶就需要多一个老张来照看,100个请求需要100个进程;对于Node,一个老张照顾所有的响水壶,100个请求一个进程管理,因为这个过程是异步的,非阻塞的。
2.3 实例
书中,用这样一个类似例子,来解释了基于事件驱动的回调
const http = require('http');
const hostname = '127.0.0.1';
const port = 1337;
http.createServer((req, res) => {
// 每一次请求,服务器刷新一段文本
console.log('Request received.');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
3. 封装服务器模块
创建一个server.js如下:
const http = require('http');
const hostname = '127.0.0.1';
const port = 1337;
function start() {
http.createServer((req, res) => {
console.log('Request received.');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
}
// 导出函数
exports.start = start;
通过创建index.js调用上述封装好的服务器模块,如下
// 读取服务器模块
var server = require('./server.js');
// 启动服务器
server.start();
4. 路由
4.1 准备工作:获取请求的URL路径
例如:一个URL 对象'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'
其中:pathname是'/p/a/t/h'
,search是'?query=string'
,path则是'/p/a/t/h?query=string'
等等,具体可从官方API中查询。
来源:https://nodejs.org/dist/latest-v5.x/docs/api/url.html
4.2 使用依赖注入,添加路由模块
创建一个router.js路由模块
// 简单的输出下请求路径信息
function route (pathname) {
console.log(`About to route a request for ${pathname}`);
}
exports.route = route;
更改server.js实现:
const http = require('http');
// 载入url模块
const url = require('url');
const hostname = '127.0.0.1';
const port = 1337;
function start(route) {
http.createServer((req, res) => {
// 获取请求中的url路径
var pathname = url.parse(req.url).pathname;
console.log(`Request for ${pathname} received.`);
// 添加路由功能
route(pathname);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
}
exports.start = start;
扩展index.js
var server = require('./server.js');
var router = require('./router.js');
server.start(router.route);
至此,一个简单的包含路由模块,路由模块的hello world级别的应用构建完毕。