零、开始前1、先解释一下node.js是什么?2.node.js和javascript有什么区别?1)因为javascript主要用在浏览器,而node.js写在服务器或者你电脑上,所以不会有window/document变量,而是global/process.global和window一样,有很多内置函数和变量,比如setTimeout()/setInterval()/console...,但是也有很多不同,后面会讲到;1.模块——require()&exportsnode.js很重要的一个概念就是模块。除了一些核心模块(如events/http/fs/url...),还可以使用require()引入一些自定义模块。例如://stuff.jsvarcounter=function(arr){return'Thereare'+arr.length+'arguments.'};varadder=function(a,b){return`总和为${a+b};`};varpi=3.1415926;如果我想在其他文件中使用这个计数器功能怎么办?在stuff.js之后添加://第一个方法//modules.exports是一个object对象modules.exports.counter=counter;modules.exports.adder=加法器;modules.exports.pi=pi;上面方法的意思就到这里了,但是比较复杂。我们可以把modules.exports写成objects的形式//第二种方法modules.exports={counter:counter,adder:adder,pi:pi}还有一种方法可以直接写在函数中:modules.exports。counter=function(arr){return'Thereare'+arr.length+'arguments.'};modules.exports.adder=function(a,b){return`总和为${a+b};`};modules.exports.pi=3.1415926;然后使用require()://main.jsvarstuff=require('./stuff');//扩展名不用写,Node会自己添加;//使用:console.log(stuff.counter(['hello','world']);console.log(stuff.adder(5、stuff.pi);二、EventEmitter类在node.js中有一个核心模块叫event,event中只有一个对象,就是EventEmitter,主要用于封装事件触发和事件监听的功能(在javascript中使用观察者模式)如果你用过jquery,你一定对这种格式很熟悉:button.on('click',function(){alert('clicked!');}其实node中的EventEmitter和jQuery的事件监听函数是也写的很好类似的://首先介绍EventEmitter对象//EventEmitter是一个构造函数,可以使用原型继承vareventEmitter=require('event').EventEmitter;varevent=neweventEmitter();//绑定事件event.on('data',function(str){console.log('somethinghere:'+str.toString());};//触发事件//第一个参数是事件名称,后者是回调函数需要的变量eventemit('data','Tomwantssthtoeat.');前面说了,EventEmitter其实是一个构造函数,可以通过prototype继承;我们现在使用的是util模块在node中实现继承:varevent=require('event');varutil=require('util');//创建person构造函数,后面会继承eventEmitter;varPerson=function(name){this.name=名字;};//util.inherits是用来继承的,注意两个params的顺序;util.inherits(Person,event.EventEmitter);//现在我们来到新的两个人实例:varjames=newPerson('james');varellen=newPerson('ellen');//把它放在一个arraycalledpeople:varpeople=[james,ellen];//现在使用eventEmitter中的on方法绑定事件;//因为继承了Person实例,所以可以使用people。对于每个(功能(person){person.on('speak',function(str){console.log(`${person}says${str}`);});});//触发事件james.emit('speak','早上好!');ellen.emit('说','晚上好!');三、fs(文件系统)类fs类主要是用来处理文件的,方法有很多,但是在本小教程中只涉及读写。首先,我们先区分一下“同步”和“异步”的概念:“同步”——阻塞;解析器会一步步解析你的代码,如果上一行没有操作完,下一行就不会操作;“异步”——非阻塞;在解析器解析完这行代码之前,不会阻碍后面代码的进行,结果会在异步处理后通过回调函数进行处理,所以,异步的性能会比同步好很多。//node中同步读写的写法:varfs=require('fs');//同步为'readFileSync'varreadMe=fs.readFileSync('input.txt','utf8');fs.write('output.txt',readMe);//异步://err为错误,data为readFile后读取的数据fs.readFile('input.txt','utf8',function(err,data){if(err){console.error(err);}else{fs.writeFile('output.txt',data);}可以看到利用fs的回调函数,可以直接在readFile中写入writeFile,无需重新定义变量。再写一个在fs中创建和删除目录的方法,也有两个方法:'stuff');//创建目录asyncfs.mkdir('stuff',function(){fs.readFile('readMe.txt','utf8',function(err,data){fs.writeFile('./stuff/writeMe/txt',data);});});//删除一个目录async//必须先删除这个目录下的所有文件,然后才能删除这个文件夹fs.unlink('./stuff/writeMe.txt',function(){fs.rmdir('stuff');})4.Stream类在讲解stream之前,我们先来思考一下为什么有可读流和可写流?明明fs类中有readFile和writeFile方法,为什么还要加stream来捣乱呢?要了解这两者之间的区别,让我们了解“缓冲区”的概念。Buffer的介绍一般是这样的:在Node.js中,Buffer类是一个随Node内核发布的核心库。Buffer库为Node.js带来了一种存储原始数据的方式,并允许Nodejs处理二进制数据,每当您需要在Nodejs中处理I/O操作中移动的数据时,都可以使用Buffer库。简单的说buffer就是一个处理二进制数据的缓冲模块,那么它和stream有什么关系呢?可以看下图:在这张图中,可以将一大块数据进行分块,然后存储在缓冲区中,形成比较小的数据块;在这张图中,chunk一个接一个的forwardpass变成了一个stream。那么回到我们最初的问题,在fs中使用readablestream和readFile有什么区别?readFile在触发回调函数以继续下一步之前等待文件被读取;而stream将文件中的数据分成很多很多小块,回调函数每感知到一个数据块就会触发。以这种方式传递数据会更快。1.可读流varfs=require('fs');varmyReadStream=fs.createReadStream(__dirname+'/input.txt','utf8');//这里如果不加'utf8',会解码成二进制(因为buffer)myReadStream.on('data',function(chunk){console.log('newchunkreceived');console.log(chunk);});//stream也继承了eventEmitter//每次感知到数据chunk都会被触发,无需等待整个文件被读取2.可写流//类似于可读流//会创建一个输出。txt在目录中varfs=require('fs');varmyReadStream=fs.createReadStream(__dirname+'/input.txt','utf8');varmyWriteStream=fs.createWriteStream(__dirname+'/output.txt');myReadStream.on('data',function(chunk){console.log('收到新块');myWriteStream.write(chunk);});3.pipe因为在node.js中很常用将可读流转换为可写流,所以有一种更优雅的pipe使用方式:varfs=require('fs');varmyReadStream=fs.createReadStream(__dirname+'/input.txt','utf8');varmyWriteStream=fs.createWriteStream(__dirname+'/output.txt');//pipe只能从可读流开始,不能从可写流开始myReadStream.pipe(我的写流);如果你添加网络服务er可以这样写:varfs=require('fs');varhttp=require('http');varserver=http.createServer(function(req,res){console.log('requestwasmade:'+req.url);res.writeHead(200,{'Content-Type':'text/plain'});varmyReadStream=fs.createReadStream(__dirname+'/input.txt','utf8');myReadStream。pipe(res);//响应对象可写});server.listen(3000);console.log('监听3000端口');如果传递的数据是html格式:varfs=require('fs');varhttp=require('http');varserver=http.createServer(function(req,res){console.log('requestwasmade:'+req.url);//html格式也可以使用流传输,使用管道将可读流转换为可写流res.writeHead(200,{'Content-Type':'text/html'});varmyReadStream=fs.createReadStream(__dirname+'/index.html','utf8');myReadStream.pipe(res);//响应对象可写});server.listen(3000);console.log('监听3000端口');if传的数据是json//不要用stream,直接在res.end()中传//不过要注意end()中只接受string,不能直接放objectvarfs=要求('fs');varhttp=require('http');varserver=http.createServer(function(req,res){console.log('requestwasmade:'+req.url);res.writeHead(200,{'Content-Type':'application/json'});varmyobj={name:'Ryu',job:'ninja',age:29};res.end(JSON.stringify(myobj));});server.listen(3001);console.log('监听3001端口');5、Router介绍一种很简单的router的使用方法,就是用if判断req.url,然后用stream来写:varhttp=require('http');varfs=require('fs');varserver=http.createServer(function(req,res){console.log('requestwasmade:'+req.url);if(req.url==='/home'||req.url==='/'){res.writeHead(200,{'Content-Type':'text/html'});//使用fs管道将可读流变为可写流fs.createReadStream(__dirname+'/index.html').pipe(res);}elseif(req.url==='/sample'){res.writeHead(200,{'Content-Type':'text/html'});fs.createReadStream(__dirname+'/sample.html').pipe(res);}elseif(req.url==='/api/ninjas'){varninjas=[{name:'ryu',age:29},{name:'yoshi',age:32}];res.writeHead(200,{'Content-Type':'application/json'});res.end(JSON.stringify(忍者));}//其实你也可以再写一个404页面});server.listen(3000);console.log('现在正在监听3000端口');
