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.jsvar adder = function(a,b) {    return `the sum of the 2 numbers is ${a+b}`;}module.exports = adder
// app.jsvar adder = require('./utils.js');adder(10,20) // 30

如果咱们要导出多个对象能够如下代码

// utils.jsvar 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 = addermodule.exports.counter = counter
// app.jsvar utils = require('./utils.js');utils.adder(10,20) // 30utils.counter([10,20,30]) // 3如上咱们导出了多个对象,然而写法过于麻烦,还能够改成如下
// utilsvar 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 filevar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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.jsvar 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不仅有以上性能,他还有很多配置,然而对于目前咱们的需要来说不太须要,在这里不做叙述