Nodejs开发指南-笔记
第三章 异步式I/O与事件编程
3.1 npm install -g supervisor
supervisor app.js 当后台修改代码后,服务器自动重启,生效修改的代码,不用手动停止/启动
3.2 单线程异步I/O
减少了多线程的开销,对于操作系统,创建线程的开销很大,需分配内存、列入调度。同时线程切换时
需要内存换页,CPU的缓存被清空,切换回来时,需要重新从内存中读取信息,破坏了数据的局部性。
异步读取文件:
var fs = require('fs') fs.readFile('test.txt','utf-8',function(err,data){ if(err){ console.log(err); } else console.log(data); }) console.log('end'); 输出: end; content of the test.txt
3.3模块和包
3.3.1、模块
什么是模块:
一个文件就是一个模块。
创建模块:
exports和require实现
举例说明
//module.js var name; exports.setName = function(thyname){ name = thyname; } exports.getName = function(){ console.log(name); } //getnodule.js var module = require('./module') module.setName('xxx') module.getName(); 对于对象的封装: //hello.js function Hello(){ var name; this.setName = function(thyname){ name = thyname; }; this.sayHello = function(){ console.log("Hello "+name); } } module.exports = Hello; //gethello.js var Hello = require('./hello') var hello = new Hello(); hello.setName("xxx"); hello.sayHello();
3.3.2、包
将一个文件夹封装成一个模块,即所谓的包
(1)作为文件夹的模板
1、建立一个somepackage文件夹
2、在somepackage下建立index.js文件 (必须是index.js)
3、文件中写入
exports.hello=function(){ console.log('Hello World'); }; 在somepackage文件夹外建立一个getpackage.js文件 写入 var somepackage=require('./somepackage'); somepackage.hello(); 3.2.2 package.json 在主目录下创建package.json,写入: { "main":"./lib/interface.js" } 在主目录中建立lib子文件夹,并在其中创建interface.js文件,在文件中写入 exports.hello=function(){ console.log('Hello Node.js'); }; 运行上面的getpackage.js得到相同的结果。
Node.js在调用某个包时,会首先检查包中package.json中的main字段,将其作为包的接口模块,如果main不存在,会尝试寻找index.js或者index.node作为包的接口
3.4nodejs的包管理器npm
npm install [包名]
npm install -g [包名]//全局目录安装
创建的全局模式的包,不能直接require使用,需要创建全局链接
npm link [包名]
./node_moudles/[包名] -> /usrt/local/lib/node_moudles/express
3.5npm包的发布
1、npm init 填写好package.json信息
2、npm adduser 按照提示填写账号信息,用于以后维护包
3、npm publish 发布包
打开浏览器 http://npmjs.org/可以查找到,而且在世界商任意一台主机上,都可以用npm install [包名]来安装你发布的包
3.6调试
3.6.1命令行调试
在设置断点的代码处添加debugger
node debug app.js
进入调试界面,help查看调试命令
3.6.2远程调试
//在一个终端里(不加port,默认是5858)
node --debug-brk[=port] app.js
或 node --debug[=port] app.js
//在另一个终端中
node debug 127.0.0.1:5858或 node debug 127.0.0.1:[port]
3.6.3node-inspector调试
npm install -g node-inspector
//运行脚本
node --debug-brk=5858 app.js
//启动node-inspector
$ node-inspector
//打开浏览器
http://127.0.0.1:8080/debug?port=5858
第四章nodejs核心模块
4.1全局对象
永远使用var定义变量,避免引入全局变量,因为你全局变量会污染明明空间。
4.1.1 process.argv命令行参数数组
第一个参数是node 第二个参数是脚本文件名,第三个参数是运行参数
4.1.2 process.nextTick(callback)
为事件循环设置一项任务,将复杂的任务,拆分成一个个较小的事件
举例说明: function doSomething(callback){ compile() callback() } doSomething(function onEnd){ compute() } 改进后 function doSomething(callback){ compile() process.nextTick(callback) } doSomething(function onEnd){ compute() } 改进后的程序会把上面耗时的操作拆分位两个事件,减少每个事件的执行时间,提高时间响应效率。
4.2常用工具util
4.2.1 util.inspect将人已对象转换成字符串。
util.inspect(object.[showHidden],[depth],[color])
showHidden可选参数,如果是true,输出更过隐藏信息
depth可选参数,最大递归层数,默认2,指定为null表示不想递归层数,完整遍历。
color可选参数,true表示输出格式以ANSI颜色编码,终端显示更漂亮。
4.3事件驱动events(最重要的模块)
4.3.1事件发射器
events.EventEmitter 时间发射和时间监听 var events = require('events') var emitter = new events.EventEmitter() emitter.on('someEvent',function(arg1,arg2){ console.log('listen ',arg1,arg2) }) emitter.emit('someEvent','byvoid',1991) 输出 listen byvoid 1991
4.4文件系统fs
4.4.1 fs.readFile()异步读取文件
fs.readFile('filename','utf-8',function(err,data){ if(err){ console.log(err) } else{ console.log(data) } })
4.4.2 fs.readFileSync(filename,[encoding])同步读取文件
4.4.3 fs.open(path,flags,[mode],[callback(err,fd)])
path:文件路径, flags:r,r+,w,w+,a,a+ 两个必选参数
mode:用于创建文件时给文件的指定权限,默认是0666
4.4.4 fs的其他常用函数:
1)读取文件
异步:fs.readFile(filename,[encoding],[callback(err,data)])
同步:fs.readFileSync(filename,[encoding])
2)写入文件(清空原有的)
异步:fs.writeFile(filename,data,[encoding],[callback(err)])
同步:fs.writeFileSync(filename,data,[encoding])
3)追加写入文件
异步:fs.appendFile(filename,data,[encoding],[callback(err)])
同步:fs.appendFileSync(filename,data,[encoding])
4)删除文件
异步fs.unlink(path,[callbacl(err)])
同步:fs.unlinkSync(path)
5)创建目录
异步:fs.mkdir(path,[mode],[callback(err)])
同步:fs.mkdirSync(path,[mode])
6)删除目录
异步:fs.rmdir(path,[callback(err)])
同步:fs.rmdirSync(path)
7)读取目录(读取目录下的所有文件,输出一个数组)
异步:fs.readdir(path,[callback(err)])
同步:fs.readdirSync(path)
8)获取文件真实路径
异步:fs.realpath(filename,[callback(err,files)])
同步:fs.realpathSync(filename)
9)更名
异步:fs.rename(path1,path2,[callback(err)])
同步:fs.renameSync(path1,path2)
10)更改权限
异步:fs.chmod(path,mode,[callback(err)])
同步:fs.chmodSync(path,mode)
11)更改所有权
异步:fs.chown(path,uid,gid,[callback(err)])//uid和gid为整形,linux下运行id命令可以查询
同步:fs.chownSync(path,uid,gid)
第六章 Nodejs进阶话题
6.1 加载缓存
nodejs不会重复加载,这是因为nodejs通过文件名缓存所有加载过的文件模块,所以以后再放问到时,就不会重复加载了。require后只需加载一次
6.2 Nodejs应用部署
6.2.1 不支持故障恢复
当程序发生错误时,后台直接垮掉
6.2.2 没有日志输出
解决方法:Express支持另种运行模式:开发模式和产品模式。前者用于调试,后者用于部署。
只需设置NODE_ENV变量即可,NODE_ENV = production实现产品模式,node app.js可以看到
Express server listening on port 3000 in production
在app.js的最上方添加一下代码,实现日志打印
var fs = require('fs'); var accessLogfile = fs.createWriteStream('access.log',{flags:'a'}); var errorLogfile = fs.createWriteStream('error.log',{flags:'a'}); app.use(express.logger({stream: accessLogfile})) //对于错误日志,需要单独实现错误响应 app.config('production',function(){ app.error(function(err,req,res,next){ var meta = '['+ new Date() +']' + req.url+'\n'; errorLogfile.write(meta+err.stack+'\n'); next(); }) })
6.2.3 无法利用多核提高性能(单线程)
解决方法:cluster模块。cluster的功能是生成与当前进程相同的子进程,并允许子进程和父进程之间共享端口。
调用cluster模块,将主进程生成若干工作进程。
6.2.4 独占端口
解决方法:利用ngix,通过反向代理实现nodejs虚拟主机
下面举例配置文件
server { listen 80; server_name mysite.com; location /{ proxy_pass http://localhost:3000; } }
该配置文件的功能是,监听访问mysite.com 80端口的请求,并将多有的请求转发给localhost:3000
6.2.5 需要手动启动(重启服务器的化)
解决方法:编写开机脚本
6.3 nodejs不适合做什么
6.3.1 不适合计算密集型程序
6.3.2 不适合单用户多任务应用。
node适合解决大量并发请求,单用户不存在并发请求。
6.3.3 不适合逻辑十分复杂的事物
node更善于处理逻辑简单但访问频繁的任务。
附录A JavaScript的高级特性
A-1、作用域
js的作用域是通过函数来定义的。变量的搜索由函数内向函数外扩展
var scop = "global" var f = function(){ console.log(scop);//输出undefined scop = "f"; }
注:js访问未定义或着定义了但未初始化的变量,都是undefined
A-2、对象
js只有对象,对象就是对象,不是类的实例
js中的对象不是基于类实现的,而是基于原型实现的。
1、var foo = {} //即为对象
foo.prop_1 = 'bar'
2、使用对象初始化器创建对象
var foo = { prop1:'bar', prop2:function(){ } }
3、使用构造函数创建对象
function User(name, uri){ this.name = name; this.uri = uri; this.display = function(){ return this.name; } } var someuser = new User('name','http://baidu.com')
4、上下文对象(call)
call可以实现继承。
user = { name:"xxxx", uri:'baudu.com', display: function(words){ console.log(this.name+" "+words) } } var foo = { name:'foobar' } user.display.call(foo,'hello') 输出: foobar hello
A-3、原型
利用原型来初始化对象
function Foo(){} Foo.prototype.name = "user" Foo..prototype.getName = function() { return this.name }; var foo = new Foo() foo.getName()//输出user
举例说明构造函数内创建属性和原型定义成员函数的区别
function Foo(){ this.prop = "prop"; this.getProp = function(){ return this.prop } } Foo.prototype.name = "user" Foo..prototype.getName = function() { return this.name }; var foo1 = new Foo() var foo2 = new Foo() console.log(foo1.getProp ==foo2.getProp)//false console.log(foo1.getName ==foo2.getName)//true
构造函数内定义的任何属性,包括函数在内都被重复构建,同一个构造函数产生的两个对象不共享实例。
所以尽量用原型定义成员函数,减少开销。
尽量在构造函数内定义一般成员,尤其是数组或对象,因为原型中定义的成员是多个实例共享的。
B、Nodejs编程规范
1、两个空格做缩进
2、分号做换行符
3、var定义变量 绝对不能使用赋值隐式定义的变量(全局变量,空间污染),确保每个语句定义一个变量,(不能定义多个变量,之间用','分割)
4、变量名和属性名(驼峰式命名空间)
5、对于函数定义,() {}之间要有一个空格
6、同意使用单引号
7、等号尽量使用===
8、命名函数
回调函数和构造函数,尽量给函数命名。
对于回调函数,Nodejs和第三方模块约定第一个参数是err
9、对象定义
所有成员函数,尽量用原型进行定义,属性在构造函数内定义,然后对构造函数用new进行对象的创建