node 如何解决循环引用的问题
// b module require('./a.js')如果已引用过a.js时不会load新的a module, 而是将已经load但是还未完成的a module的exports属性返回给b module
工具
- nodemon 自动监听文件的变化
pageage.json中添加scripts
"scripts": {
"dev": "nodemon ./indexc.js"
},
__dirname与__filename
- __dirname:当前运行脚本所在文件位置:
- __filename:当前运行脚本所在位置 :
console.log(__filename) // /Users/chenyifan/myOcean/node/index.js
console.log(__dirname) // /Users/chenyifan/myOcean/node
常用api
内置对象
- resolve 为url或http插入或者替换原有标签 (总结:替换 域名后面第一个“/”后的内容)
var url = require('url');
var a = url.resolve('/one/two/three', 'four') ,
b = url.resolve('http://example.com/', '/one'),
c = url.resolve('http://example.com/one', '/two');
console.log(a +","+ b +","+ c);
//输出结果
/one/two/four
http://example.com/one
http://example.com/two
glob 匹配文件路径
- 单个星号
*: 用于匹配单个片段中的零个或多个字符
1. `src/*.js` 表示 `src` 目录下所有以 `js` 结尾的文件,
但是不能匹配 `src` 子目录中的文件,例如 `src/login/login.js`
- 两个星号
**可以跨片段匹配零个或多个字符,也就是说**是递归匹配所有文件和目录的,如果后面有分隔符,即**/的话,则表示只递归匹配所有目录
1. `/var/log/**` 匹配 `/var/log` 目录下所有文件和文件夹,以及文件夹里面所有子文件和子文件夹
2. `/var/log/**/*.log` 匹配 `/var/log` 及其子目录下的所有以 `.log` 结尾的文件
示例:
//`src` 目录下所有 `js` 和 `jsx` 文件
const glob = require('glob')
const files = glob.sync('src/**/*.js{,x}')
console.log(files)
读取文件
- readFile:异步读取
- readFileSync:同步读取
- 使用node的读写流时,若data是string,则可以指定encoding参数,以指定的编码类型来读写字符串;若data是buffer,则忽略encoding参数
var fs = require('fs'); //获取fs模块
console.log('start async read'); //异步读取开始
fs.readFile('test2.js',function(error,date){ //读取文件,回调函数第一个参数表示错误信息,第二个参数为读取的文本内容
if(error){
console.log(error);
}else{
console.log('end async read'); //异步读取结束
}
});
console.log('start sync read'); //同步读取开始
var date = fs.readFileSync('log.txt','utf-8');//指定为utf格式
console.log('end sync read'); //同步读取结束
- readFile 和createReadStream 函数的区别
1. createReadStream读取时被分块发送,需要的内存小,readFile将读取整个文件,需要的内存大
2. 如果在100MB的文件上运行这两个函数,第一个函数将使用100MB内存来加载文件,而后者最多只使用4KB
3. pipe()只是可读流的方法,也就是说只能从可读流中通过pipe方法拷贝数据到可写流,反之则不行,写的时候要注意顺序
fs.createReadStream('a.mp4').pipe(fs.createWriteStream('b.mp4)); // pipe自动调用了data,end等事件
- 创建buffer
- Buffer.from()从字符串或者数组创建一个buffer,
- Buffer.alloc()是创建一个指定大小的buffer。
Nodejs不能控制数据传输的速度和到达时间,只能决定何时发送数据,如果还没到发送时间,则将数据放在Buffer中- Buffer对象中的每个元素都是16进制的两位数(即0到255的数值)
const buffer1 = Buffer.from('geekbanghh')//<Buffer 67 65 65 6b 62 61 6e 67 68 68>
const buffer2 = Buffer.from([1, 2, 3, 4])//<Buffer 01 02 03 04>
const buffer3 = Buffer.alloc(20)//<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>
/// 向buffer2的buffer中写入数字12,类型为int8,(int8 只占用两位),从图中第1个位置写入,
buffer2.writeInt8(12, 1)//<Buffer 01 0c 03 04>
// 向buffer2中写入数字512,并且写入位置从第2个位置写入, 因为是Int16,所以需要占用两位,512的十六进制数字为 02 00 2*16^2=512
buffer2.writeInt16LE(512, 2)//<Buffer 01 0c 02 00>
process 进程对象
- process.argv 属性返回一个数组,这个数组包含了启动Node.js进程时的命令行参数,(可以获取到启动Node.js输入的参数)
chenyifanMacdeMacBook-Pro:section8 chenyifan$ node my-spr.js my-spr
[
'/usr/local/bin/node',
'/Users/chenyifan/myOcean/node/geek-nodejs/section8/my-spr.js',
'my-spr'
]
模块规范
- require返回的对象,和***.js里的exports对象属于同一个引用
---index.js
console.log('start require');
var lib = require('./lib')
console.log('end require', lib);
// 参见lib.js注释里的知识点1
console.log(lib.tencent);
// 参见lib.js注释里的知识点2
// require返回的对象,和lib.js里的exports对象属于同一个引用
// 因此此处加的属性能在里面体现出来。
lib.additional属性 = 'test'
---lib.js
console.log('this is module');
exports.geekbang = { 'hello': 'haha' }
exports.tencent = function () {
console.log('good')
}
setTimeout(()=> {
// 验证index.js里加的additional属性是否生效
// 用于确定外部require到的对象和此处的exports是否是同一个属性
console.log(exports)//导出对象中有additional属性
}, 2000)
- 最终导出的是module.exports 所以exports={}会断开与module.exports的连接,只能听过exports.= 的方法导出变量
npm
pageage.json 表示该目录是一个npm包
- 安装包:npm install *** -S / npm install *** -D
- 卸载包:npm uninstall ***
非阻塞 I/O
阻塞 I/O 和非阻塞 I/O 的区别就在于系统接收输入再到输出期间,能不能接收其他输入
eventLoop
node的事件循环会重新开启一个调用栈,所以,错误捕获如果用try catch会抛出到全局,一般使用callback的方法
function interview(callback) {
setTimeout(() => {
if (Math.random() > 0.2) {
callback(null, 'success')
} else {
// throw new Error('fail');//捕获不到,开启了一个全新的作用栈,故不会像下传递
callback(new Error('fail'))
}
}, 500)
}
interview(function (err, res) {
if (err) {//捕获错误
console.log('cry')
return;
}
console.log('smile')
})
解析url
- new URL() //最新方式
- querystring
Koa2 vs Express
- Express request/response 简化
- request: pathname、query 等
- response: send()、json()、jsonp() 等
中间件
-
更好地组织流程代码
-
异步会打破 Express 的洋葱模型
-
koa
-
ctx.status = 200
-
ctx.body = 'hello world' 使用 async function 实现的中间件
-
有“暂停执行”的能力
-
在异步的情况下也符合洋葱模型
http通信和RPC通信
- html编码格式:json/html格式
- RPC编码格式:二进制 [0001 1110 0000 0000]更小的数据包体积,更快的编解码速率
- Buffer.from
- Buffer.alloc Buffer对象中的每个元素都是16进制的两位数(即0到255的数值)
const buffer1 = Buffer.from('geekbangqq')//10为长度的buffer
const buffer2 = Buffer.from([1, 2, 3, 4])//4为长度的buffer
const buffer3 = Buffer.alloc(20)//长度为20的空buffer
console.log(buffer1)
console.log(buffer2)
console.log(buffer3)
//打印结果如下
<Buffer 67 65 65 6b 62 61 6e 67 71 71>
<Buffer 01 02 03 04>
<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>
3.protocol-buffers 编码二进制/解码二进制
//test.proto 定义数据格式
message Course {
required float id = 1;
required string name = 2;
repeated Lesson lesson = 3;
}
//protobuf.js
const schemas = protobuf(fs.readFileSync(`${__dirname}/test.proto`));
//编码
const buffer = schemas.Course.encode({
id: 4,
name: 'hh',
lesson: []
})
console.log( buffer);//<Buffer 67 65 65 6b 62 61 6e 67 71 71>
//解码
console.log(schemas.Course.decode(buffer));//解码出定义的对象
项目
- easy_sock 与服务端进行RPC通信的
const easySock = new EasySock({
ip: '127.0.0.1',
port: 4000,
timeout: 500,
keepAlive: true //是否为全双工通信
})
- koa-graphql 用于聚合数据, 启动服务后,请求地址:http://localhost:3000/?query={comment{name}}
//server.js
const app = new (require('koa'))()
const graphqlHTTP = require('koa-graphql')
app.use(
graphqlHTTP({
schema: require('./schema')
})
)
app.listen(3000)
//schema.js
const { graphql, buildSchema } = require('graphql');
const schema = buildSchema(`
type Comment {
id: Int
avatar: String
name: String
isTop: Boolean
content: String
publishDate: String
commentNum: Int
praiseNum: Int
}
type Query {
comment: [Comment]
}
`)
schema.getQueryType().getFields().comment.resolve = () => {
return [{
id: 1,
avatar: "https://static001.geekbang.org/account/avatar/00/0f/52/62/1b3ebed5.jpg",
name: "testName",
isTop: true,
content: "哈哈哈哈",
publishDate: "今天",
commentNum: 10,
praiseNum: 5
}]
}
module.exports = schema;
- 发送post请求
//schema.js新增type Mutation
const schema = buildSchema(`
...
type Mutation {
praise(id: Int): Int
}
`)
schema.getMutationType().getFields().praise.resolve = (args0, { id }) => {
mockDatabase[id].praiseNum++;
return mockDatabase[id].praiseNum
}
node 性能分析
计算性能优化的本质
- 减少不必要的计算
- 空间换时间
性能分析评估
压测工具
- ab -c200 -n1600 http:127.0.0.1:3000/download //-c:ab模拟多少个客户端(200人同时执行我们的网页服务器) -n:总空执行1600次请求
devtools
1.node --inspect-brk entry.js //压测:-brk启动调试会暂停代码的执行
2.浏览器输入:chrome://inspect/#devices ->Target->inspect
3.
- node --inspect --inspect-brk entry.js //-brk启动调试会暂停代码的执行
- 将计算移除到中间件之外