Nodejs开发指南-笔记

时间:2022-12-25 05:26:58

第三章 异步式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进行对象的创建