Nodejs 长处
①运行在 V8JavaScript 引擎上(高性能)
②事件驱动
③非阻塞的 IO 模型
④丰盛的生态圈(npm 下载资源)
Nodejs 装置
首先咱们能够在官网下载最新版本的 Nodejs
装置的流程非常简单,下载软件,依据提醒下一步进而实现装置
咱们须要相熟命令行的相干操作,此处不做赘述,能够参考 ES6 局部课程中的命令行操作
创立 app.js 文件,运行 Node 代码console.log("Hello Nodejs")
全局对象
Nodejs 提供了很多全局对象,这些对象能够间接应用,不须要做独自的引入例如:
在 js 中的日志信息打印就是其中之一,文档 (opens new window)console.log("测试");
定时器也是其中之一,文档(opens new window)
var time = 0;
var timer = setInterval(function() {
time += 2;
console.log(time + "seconds have passed");
if (time > 5) {clearInterval(timer);
}
}, 2000);
还用一些罕用的全局对象例如:
console.log(__dirname); // 取得以后门路
console.log(__filename); // 取得以后门路及文件名字
回调函数
咱们须要相熟一下回调函数,因为在整个 Nodejs 中,满满的都是回调函数
咱们先看上面的根底代码
function sayHi(){console.log("hi");
}
var sayBye = function(){console.log("bye");
}
sayHi();
sayBye();
以上是两种函数的申明形式
而后咱们减少回调函数
function sayHi(){console.log("hi");
}
var sayBye = function(){console.log("bye");
}
sayHi();
sayBye();
function callFunction(callback){callback()
}
callFunction(sayBye);
上述代码其中 callFunction 是一个回调函数,调用了 sayBye 函数的执行
咱们同样在回调函数中是能够传递参数的
var sayBye = function(name){console.log(name+":bye");
}
function callFunction(callback,name){callback(name)
}
callFunction(sayBye,'iwen');
咱们还能够变换书写形式
function callFunction(callback,name){callback(name)
}
callFunction(function(name){console.log(name+":bye");
},'iwen');
模块(Commonjs 标准)
在 JavaScript 的 ES5 版本中,最大的问题就是没有模块的概念,然而 nodejs 中,减少了 commonjs 标准来解决这一问题。
咱们看如下代码
// utils.js
var adder = function(a,b) {return `the sum of the 2 numbers is ${a+b}`;
}
module.exports = adder
// app.js
var adder = require('./utils.js');
adder(10,20) // 30
如果咱们要导出多个对象能够如下代码
// utils.js
var adder = function(a,b) {return `the sum of the 2 numbers is ${a+b}`;
}
var counter = function(arr) {return "There are" + arr.length + "elements in the array";}
module.exports.adder = adder
module.exports.counter = counter
// app.js
var utils = require('./utils.js');
utils.adder(10,20) // 30
utils.counter([10,20,30]) // 3
如上咱们导出了多个对象,然而写法过于麻烦,还能够改成如下
// utils
var adder = function(a,b) {return `the sum of the 2 numbers is ${a+b}`;
}
var counter = function(arr) {return "There are" + arr.length + "elements in the array";}
module.exports = {
adder:adder,
counter:counter
}
一会可能呈现导出的时候,间接附带函数
var adder = function(a,b) {return `the sum of the 2 numbers is ${a+b}`;
}
module.exports = {
adder:adder,
counter:function(arr) {return "There are" + arr.length + "elements in the array";}
}
事件
大多数 Node.js 外围 API 构建于习用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)。例如,net.Server 会在每次有新连贯时触发事件,fs.ReadStream 会在关上文件时触发事件,stream 会在数据可读时触发事件。所有能触发事件的对象都是 EventEmitter 类的实例。这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上。事件的命名通常是驼峰式的字符串,但也能够应用任何无效的 JavaScript 属性键。。当 EventEmitter 对象触发一个事件时,所有绑定在该事件上的函数都会被同步地调用。被调用的监听器返回的任何值都将会被疏忽并抛弃。
var events = require('events');
var myEmitter = new events.EventEmitter();
myEmitter.on('someEvent', function(message) {console.log(message);
})
myEmitter.emit('someEvent', 'the event was emitted');
出了间接应用事件,咱们还能够让对象继承事件,来给对象增加事件
var events = require('events');
var util = require('util');
var Person = function(name) {this.name = name}
// inherits 继承
util.inherits(Person, events.EventEmitter);
var xiaoming = new Person('xiaoming');
var lili = new Person('lili');
var lucy = new Person('lucy');
var person = [xiaoming, lili, lucy];
person.forEach(function(person) {person.on('speak', function(message) {console.log(person.name + "said:" + message);
})
})
xiaoming.emit('speak', 'hi');
lucy.emit('speak', 'I want a curry');
文件读写
在 Nodejs 中有文件系统,是对本地文件进行读写操作,当然,如果要应用咱们须要引入 fs 对象
var fs = require("fs");
var readMe = fs.readFileSync("./readme.txt",'utf8');
console.log(readMe);
咱们同样能够将数据写入到文件
var fs = require("fs");
var readMe = fs.readFileSync("./readme.txt",'utf8');
fs.writeFileSync("writeMe.txt",readMe)
接下来,咱们须要剖析一下异步与同步的问题,上述代码是同步的成果,测试如下
var fs = require("fs");
var readMe = fs.readFileSync("./readme.txt",'utf8');
console.log(readMe);
console.log("finished");
上述代码先打印 readMe 的后果,在打印 finished,咱们晓得的时候,文件的读取是一个比拟耗时的操作,然而后果仍然是上述成果,那就说阐明上述是同步代码,接下来咱们看看异步代码成果
var fs = require("fs");
var readMe = fs.readFile("./readme.txt",'utf8',function(err,data){console.log(data);
});
console.log("finished");
上述代码的打印与之前齐全相同,先打印 finished, 而后输入 data 数据,此时就是异步成果
咱们对耗时的代码肯定要进行解决,否则咱们的主线程则处于卡顿的期待过程,如下代码
var fs = require("fs");
var readMe = fs.readFile("./readme.txt",'utf8',function(err,data){console.log(data);
});
var waitTill = new Date(new Date().getTime() + 4 * 1000);
while (waitTill > new Date()) {}
console.log("finished");
咱们做了一个 4 秒的期待,所以,打印须要在 4 秒之后才会呈现。这就是卡顿的成果,用户体验肯定是极差的
接下里咱们对读写都应用异步的计划解决
var fs = require('fs');
var readMe = fs.readFile("readMe.txt", "utf8", function(err, data) {fs.writeFile('writeMe.txt', data, function() {console.log('writeMe has finished');
})
});
console.log("finished");
流和管道
流的概念并不难理解,例如:咱们平时前后端交互其实就是转换成流来进行交互的,咱们之前也讲过文件的读写,文件的读写也属于流的操作的体现。这是如果文件特地大的时候,咱们还是要采取 buffer 解决
咱们当初命令行中操作一个根底操作
ls
ls | grep app
ls | grep app 这个命令在 linux 或 mac 才适宜,或者 windows 的 git bash 也能够的。
如果是 windows 的命令提示符,对应的查找文件的命令应该是:dir | findstr app
上面咱们看一下流的具体操作
var fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/readMe.txt');
myReadStream.on('data', function(chunk) {console.log(chunk);
})
上述代码读到的是 buffer 对象,这也是他性能晋升的次要起因。如果文件过大,会解决成多个 buffer 对象
如果想间接读取数据如下
var fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/readMe.txt',"utf8");
myReadStream.on('data', function(chunk) {console.log(chunk);
})
然而个别咱们间接在 data 事件中应用数据,因为可能数据还没有读取实现,所以咱们能够监听 end
var fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/readMe.txt');
myReadStream.setEncoding('utf8');
var data = ""myReadStream.on('data', function(chunk) {data += chunk;})
myReadStream.on('end', function() {console.log(data);
})
上述是一个读取流的操作,那么写入流的操作如何应用呢,如下
var fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/readMe.txt',"utf8");
var myWriteStream = fs.createWriteStream(__dirname + '/writeMe.txt');
var data = ""myReadStream.on('data', function(chunk) {myWriteStream.write(chunk);
})
myReadStream.on('end', function() {console.log("end");
})
当然。咱们也能够独自实现写入流的操作
var fs = require('fs');
var myWriteStream = fs.createWriteStream(__dirname + '/writeMe.txt');
var writeData = "hello world";
myWriteStream.write(writeData);
myWriteStream.end();
myWriteStream.on('finish', function() {console.log('finished');
})
接下里咱们应用管道操作,如果应用管道操作,代码量更少
var fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/readMe.txt');
var myWriteStream = fs.createWriteStream(__dirname + '/writeMe.txt');
myReadStream.pipe(myWriteStream);
上面咱们来看一个示例,咱们能够应用管道及一些第三方,来进行一个压缩操作
// 压缩
var crypto = require('crypto');
var fs = require('fs');
var zlib = require('zlib');
var password = new Buffer(process.env.PASS || 'password');
var encryptStream = crypto.createCipher('aes-256-cbc', password);
var gzip = zlib.createGzip();
var readStream = fs.createReadStream(__dirname + "/readMe.txt"); // current file
var writeStream = fs.createWriteStream(__dirname + '/out.gz');
readStream // reads current file
.pipe(encryptStream) // encrypts
.pipe(gzip) // compresses
.pipe(writeStream) // writes to out file
.on('finish', function() { // all done
console.log('done');
});
上述代码实现了压缩,咱们也能够进行解压操作
// 解压
var crypto = require('crypto');
var fs = require('fs');
var zlib = require('zlib');
var password = new Buffer(process.env.PASS || 'password');
var decryptStream = crypto.createDecipher('aes-256-cbc', password);
var gzip = zlib.createGunzip();
var readStream = fs.createReadStream(__dirname + '/out.gz');
readStream // reads current file
.pipe(gzip) // uncompresses
.pipe(decryptStream) // decrypts
.pipe(process.stdout) // writes to terminal
.on('finish', function() { // finished
console.log('done');
});
Web 服务器输入内容
对于咱们本套课程来说,咱们用的最多的就是用 Node 构建一个服务器,并且输入内容,内容输入绝大多数为 JSON 数据
var http = require('http');
var onRequest = function(request, response) {console.log('Request received');
response.writeHead(200, { 'Content-Type': 'text/plain'});
// response.write('Hello from out application');
response.end('Hello from out application');
}
var server = http.createServer(onRequest);
server.listen(3000, '127.0.0.1');
console.log('Server started on localhost port 3000');
上述代码咱们实现了一个文本信息的输入,拜访如下
http://localhost:3000/
咱们还能够输入 JSON 格局的数据
var http = require('http');
var onRequest = function(request, response) {console.log('Request received');
response.writeHead(200, { 'Content-Type': 'application/json'});
// response.write('Hello from out application');
var myObj = {
name: "itbaizhan",
job: "learn",
age: 27
};
response.end(JSON.stringify(myObj));
}
var server = http.createServer(onRequest);
server.listen(3000, '127.0.0.1');
console.log('Server started on localhost port 3000');
当然,个别时候,咱们可能须要承受一个服务器返回的页面,例如,领取相干的返回个别都是返回一个页面间接渲染
var http = require('http');
var fs = require('fs');
var onRequest = function(request, response) {console.log('Request received');
response.writeHead(200, { 'Content-Type': 'text/html'});
var myReadStream = fs.createReadStream(__dirname + '/index.html', 'utf8');
// response.write('Hello from out application');
myReadStream.pipe(response);
}
var server = http.createServer(onRequest);
server.listen(3000, '127.0.0.1');
console.log('Server started on localhost port 3000');
模块化组织代码
代码的组织能力也很重要,所以咱们须要利用之前所学模块进行从新组织代码
// server.js
var http = require('http');
var fs = require('fs');
function startServer() {var onRequest = function(request, response) {console.log('Request received');
response.writeHead(200, { 'Content-Type': 'text/html'});
var myReadStream = fs.createReadStream(__dirname + '/index.html', 'utf8');
// response.write('Hello from out application');
myReadStream.pipe(response);
}
var server = http.createServer(onRequest);
server.listen(3000, '127.0.0.1');
console.log('Server started on localhost port 3000');
}
exports.startServer = startServer;
// app.js
var server = require('./server');
server.startServer();
路由
在理论的开发场景中,咱们须要依据不同的地址返回不同的数据,也就是咱们日常所说的路由成果
在上一大节中,如果咱们间接拜访 http://localhost:3000/home 和之前的拜访是没有区别的,也就是咱们无奈依据不同地址返回不同的数据,接下来咱们解决一下,造成路由
var http = require('http');
var fs = require('fs');
function startServer() {var onRequest = function(request, response) {console.log('Request received' + request.url);
if (request.url === '/' || request.url === '/home') {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/index.html', 'utf8').pipe(response);
} else if (request.url === '/review') {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/review.html', 'utf8').pipe(response);
} else if (request.url === '/api/v1/records') {response.writeHead(200, { 'Content-Type': 'application/json'});
var jsonObj = {name: "itbaizhan"};
response.end(JSON.stringify(jsonObj));
} else {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/404.html', 'utf8').pipe(response);
}
}
var server = http.createServer(onRequest);
server.listen(3000, '127.0.0.1');
console.log('Server started on localhost port 3000');
}
exports.startServer = startServer;
重构路由代码
上一大节曾经实现了路由的成果,然而代码是在难看。所以咱们须要重新整理代码构造
// app.js
var server = require('./server');
var router = require('./router');
var handler = require('./handler');
var handle = {};
handle["/"] = handler.home;
handle['/home'] = handler.home;
handle['/review'] = handler.review;
handle['/api/v1/records'] = handler.api_records;
server.startServer(router.route, handle);
// server.js
var http = require('http');
var fs = require('fs');
function startServer(route, handle) {var onRequest = function(request, response) {console.log('Request received' + request.url);
route(handle, request.url, response);
}
var server = http.createServer(onRequest);
server.listen(3000, '127.0.0.1');
console.log('Server started on localhost port 3000');
}
module.exports.startServer = startServer;
// router.js
var fs = require('fs');
function route(handle, pathname, response) {console.log('Routing a request for' + pathname);
if (typeof handle[pathname] === 'function') {handle[pathname](response);
} else {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/404.html', 'utf8').pipe(response);
}
}
module.exports.route = route;
// handler.js
var fs = require('fs');
function home(response) {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/index.html', 'utf8').pipe(response);
}
function review(response) {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/review.html', 'utf8').pipe(response);
}
function api_records(response) {response.writeHead(200, { 'Content-Type': 'application/json'});
var jsonObj = {name: "itbaizhan"};
response.end(JSON.stringify(jsonObj));
}
module.exports = {
home: home,
review: review,
api_records: api_records
}
应用 Get 或 POST 发送数据
咱们罕用的申请形式有很多,然而其中 get 和 post 是最罕用的,那么如何辨别是 get 申请还是 post 申请呢?
// server.js
var http = require('http');
var url = require('url');
var querystring = require('querystring');
function startServer(route, handle) {var onRequest = function(request, response) {var pathname = url.parse(request.url).pathname;
console.log('Request received' + pathname);
var data = [];
request.on("error", function(err) {console.error(err);
}).on("data", function(chunk) {data.push(chunk);
}).on('end', function() {if (request.method === "POST") {data = Buffer.concat(data).toString();
route(handle, pathname, response, querystring.parse(data));
} else {var params = url.parse(request.url, true).query;
route(handle, pathname, response, params);
}
});
}
var server = http.createServer(onRequest);
server.listen(3000, '127.0.0.1');
console.log('Server started on localhost port 3000');
}
module.exports.startServer = startServer;
// router.js
var fs = require('fs');
function route(handle, pathname, response,params) {console.log('Routing a request for' + pathname);
if (typeof handle[pathname] === 'function') {handle[pathname](response,params);
} else {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/404.html', 'utf8').pipe(response);
}
}
module.exports.route = route;
// handler.js
var fs = require('fs');
function home(response) {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/index.html', 'utf8').pipe(response);
}
function review(response) {response.writeHead(200, { 'Content-Type': 'text/html'});
fs.createReadStream(__dirname + '/review.html', 'utf8').pipe(response);
}
function api_records(response, params) {response.writeHead(200, { 'Content-Type': 'application/json'});
response.end(JSON.stringify(params));
}
module.exports = {
home: home,
review: review,
api_records: api_records
}
npm 命令
npm (opens new window)为你和你的团队关上了连贯整个 JavaScript 蠢才世界的一扇大门。它是世界上最大的软件注册表,每星期大概有 30 亿次的下载量,蕴含超过 600000 个 包(package)(即,代码模块)。来自各大洲的开源软件开发者应用 npm 相互分享和借鉴。包的构造使您可能轻松跟踪依赖项和版本。
咱们应用 npm 也防止了反复造轮子的问题
装置依赖
咱们须要第三方依赖,能够间接通过 npm 进行下载
npm install express
cnpm 镜像
npm 是在近程仓库下载,咱们晓得他的仓库并不在国内,所以,咱们须要找一个国内的仓库镜像npm install -g cnpm --registry=https://registry.npm.taobao.org
package.json
因为 node_modules 文件依赖文件太多,而且他也不属于咱们的源代码,所以咱们在上传源代码的时候并不会上传这个文件夹,那么他人如何晓得咱们装置过了哪些包呢?
npm init
初始化一个 package.json 文件
{
"name": "1",
"version": "1.0.0",
"description": "","main":"app.js","scripts": {"test":"echo \"Error: no test specified\" && exit 1","start":"node server.js"},"author":"",
"license": "ISC"
}
而后在装置依赖的时候。咱们须要在命令行上退出 –save 或者 –sage-dev
npm install --save express
npm install --save-dev gulp
上述两种形式,区别在于 –save 是生产环境,–save-dev 是开发环境
"dependencies": {"express": "^4.16.2"},
"devDependencies": {"gulp": "^3.9.1"},
有了上述的形容,咱们删除 node_modules 他人就能够依据形容来进行装置了npm install
scripts 脚本
咱们还有一个 scripts 脚本能够应用,一个我的项目的入口文件不是固定的,所以如果他人拿到你的代码,不晓得你的入口文件,则无奈运行你的我的项目,上面的脚本能够无效的解决这个问题
"scripts": {"start": "node app.js"},
nodemon
nodemon 是一种工具,能够自动检测到目录中的文件更改时通过重新启动应用程序来调试基于 node.js 的应用程序。
npm install -g nodemon
有了这个命令,咱们就不再须要每次批改结束代码进行重启了,他能够检测文件的扭转而主动重启
nodemon app.js
当然。nodemon 不仅有以上性能,他还有很多配置,然而对于目前咱们的需要来说不太须要,在这里不做叙述