Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境
Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效
Node.js 的包管理器 npm,是全球最大的开源库生态系统
? 本文主要介绍构建一个 Node.js 应用的基本步骤和模块,并假定你已经对 Node.js Api 有必定的了解javascript
? 本文引用部分代码做为例子,若是但愿参看所有源码,欢迎去 github 查阅(若是以为有必定帮助,欢迎star)html
整个 Node.js 应用的架构设计java
require
指令来引入 Node.js 模块// 经过 require 引入 http 模块,并将实例化的 HTTP 赋值给 http 变量 const http = require('http'); // 引入 url 模块,用来解析数据 const url = require('url'); function ylone(router, handleObj) { const hostname = '127.0.0.1'; const port = 7777; // http.createServer(function(){}) 方法建立服务器,并返回一个对象 const server = http.createServer((req, res) => { const path = url.parse(req.url); const pathName = path.pathname; // 处理node.js每次自动请求favicon.ico if (pathName !== '/favicon.ico') { const content = router(handleObj, pathName, res, req); } }); // server.listen() 方法绑定主机和端口 server.listen(port, hostname, () => { console.log(`服务运行在${hostname}:${port}`); }); } exports.ylone = ylone;
http.createServer((req, res) => {...})
是一个典型的回调,事实上,这就是 Node.js 原生的工做方式index.js
去调用相应的模块来引导和启动应用const server = require('./http'); const router = require('./route'); const handle = require('./requestHandle'); var handleObj = {}; // 入口 Case handleObj['/'] = handle.hello; // 非阻塞 Case handleObj['/vlone'] = handle.vlone; // post Case handleObj['/supreme'] = handle.supreme; // get Case handleObj['/adidas'] = handle.adidas; server.ylone(router.router, handleObj);
http.createServer((req, res) => {...})
的 req 参数中,为了解析 req,须要额外引入 url
和 querystring
Node.js 模块http.createServer((req, res) => {...})
内解析 req 参数,而后调用 router 方法function router(handleObj, pathName, res, req) { if (typeof handleObj[pathName] === 'function') { return handleObj[pathName](res, req); } else { res.writeHead(200, { 'Content-type': 'text/plain' }); const content = '404 Not Found'; res.write(content); res.end(); } } exports.router = router;
由于文章篇幅缘由,这里只展现关键代码,源码参看 githubnode
const { exec } = require('child_process'); const querystring = require('querystring'); const url = require('url'); function createHttp(type, res, val) { const content = val; const conType = { plain: 'text/plain;charset=utf-8', html: 'text/html', }; // 为隐式的响应头设置值 res.writeHead(200, { 'Content-type': conType[type] }); // 发送响应主体 res.write(content); // http 完成响应 res.end(); } ... something else function vlone(res) { exec('node --version', (error, stdout, stderr) => { if (error) { console.log(error, stdout, stderr); return; } const content = stdout; const type = 'plain'; createHttp(type, res, content); }); } ... something else
A() 方法读取文件,所以须要必定的响应时间,B() 方法表明其余须要执行的代码git
阻塞:在A() 执行的过程当中,B() 处于等待状态,当A() 访问文件数据准备就绪后,B() 才开始执行github
由上图能够看出,应用程序从进行系统调用到复制数据报到应用进程缓冲区的整段过程是阻塞的,直到数据报被复制到用户空间完成后,用户进程才解除阻塞状态,继续执行下一个应用程序npm
非阻塞:在A() 执行的过程当中,B() 同时执行,且当A() 访问文件数据准备就绪后,A() 会被执行完成编程
由上图能够看出,应用程序在调用过程当中,若是数据报尚未准备就绪,会先返回一个错误信息(EWOULDBLOCK),此时当前进程能够执行其余方法,而不会阻塞。而 A() 会轮询内核,返回缓冲区数据是否准备就绪api
url.parse()
的第二参数 parseQueryString 若是为 true,则 query
属性老是会经过 querystring 模块的 parse() 方法生成一个对象node ylone.js
命令执行脚本http://localhost:7777/
)意味着向服务器发出请求,从而触发服务器建立时的回调函数http://localhost:7777/
)时,控制台可能会输出两次 req 的数据,那是由于大部分浏览器会在访问网页时尝试读取 favicon.ico 文件/favicon.ico
的问题,能够在 http 中对其进行过滤,不执行操做res.writeHead(200, {'Content-type': 'text/plain'})
的 Content-type 设置为 text/html
res.writeHead(200, {'Content-type': 'text/plain;charset=utf-8'})
加上 charset=utf-8
配置解决--Respect Node.js--浏览器