基本

libuv:https://zhuanlan.zhihu.com/p/50497450
v8专栏:https://www.zhihu.com/column/v8core

EventEmitter

通过发布订阅模式进行事件处理。
主要功能:链式调用
on/once/prependListener/prependOnceListener(name, listener)
name事件加listener。其中:

  • once事件发生时会先移除在调用listener,只发生一次
  • prepend是将listener放在数组开头

off(name, listener)/removeAllListeners(...listeners)
移除name事件添加的这个listener函数。

  • off,如果该listener有多个重复的只删一个,想全删就多调用

emit(name[,...args]):给该名称的事件监听器派发事件,返回是否有监听器
具有两个事件:
newListener(eventName, listener)removeListener(eventName, listener)

Stream

http://nodejs.cn/api/stream.html
https://juejin.cn/post/7077511716564631566
说实话,流这个东西,叫管道更好理解。
它的意义,主要是进行多个过程操作的时候,每个过程都是一段管道,前面流出一点,后面就收一点然后继续往下流。说实话可以整成一个有向无环图(误)
一般使用四种管道类型:
Readable只能出不能入,类似水源。
Writeable只能入不能出,类似水桶。
Duplex能入,且能出。但是不同数据源。类似于水源和水桶捆起来。
Transform将写入转换后作为输出,就是实现转换的Duplex,类似滤水器。

缓冲区

所有流除了链接到真正的源/目标(如:底层文件系统)之外,在外部接口之间还有一个缓冲区。

readable

构造函数选项:
read([size]):真正从源(可能是文件系统)读入数据的过程。这里面需要调用push(chunk)将数据块压入缓冲区。

两种模式/三种状态

nodejs - 图1

  1. readableFlowingnull/false时,处于暂停状态。
    可以通过resume()pipe(writeable)使之流动。
    此外在初始null状态时,挂载data事件也可以启动
  2. true时,处于流动状态,数据会自动流出(此时应该有可写流接入了或者有事件接收了)
    可以通过pause()unpipe()移除所有接入的写管道,或者背压,改为false

    数据消费机制

    可选的有这几种:
    on('data')on('readable')pipe() 或异步迭代器。使用一种即可,尽量不要混用,不然非常confusing。不想纠结优先级相关。具体而言:

  3. 挂载data,然后数据自动流动,从datalistener中拿数据。
    通过pause()``resume()控制是否暂停,通过end事件确定数据读取完成。

  4. 挂载readable,手动一次次read()读取数据,直到读到的为null优先
  5. pipe(writeable),自动管理流速防止背压问题。注:这个是由stream的api实现的

事件:
readable表明流有新的数据可以读取。
end事件表明流已经榨干了,没新数据可读了

readable.read([size])从可读流读入。如果没有数据可读,会返回null并触发end事件
readable.[pipe](http://nodejs.cn/api/stream.html#readablepipedestination-options)(writable[,options])可读流灌入可写流,并返回可写流。然后可读流数据会自动持续的加入可写流。如果写入的是双工流,可以链式调用

writable

事件

pipe(readable)``unpipe(readable)指明哪个可读流灌入或移出
drain()是把缓冲区数据写入底层后触发的事件,这个触发之后可以再写了
finish()是所有数据已写入底层,结束写入后触发

writeable.write(content)写入
writeable.end(content)写完content之后结束写入。注意写流的结束是程序员主动结束的。

transform

转换流没有了“底层”,不用实现_read``_write,主要实现_transform(chunk, encoding, callback)(转换并通过push加入)和_flush(callback)(即将结束之前通过push额外写入一段数据)

全局变量

路径

__dirname:返回被执行的Js所在文件夹的目录
__filename:返回被执行的Js的绝对路径
process.cwd():返回运行node命令时所在的文件夹的绝对路径
./:在require()中跟__dirname效果相同,在其他地方跟process.cwd()相同

框架(express=>koa=>egg)

https://www.yuque.com/lipengzhou/nodejs-tutorial
nodejs的框架确实是一脉相承的。从expressKoaegg,都是非常类似的思路。
express确实是一个非常轻量级的框架,源代码只有4000行左右,比我写的一些项目都小,很不错了。
但是Koa源码更少,才600行左右,瞬间有了一种我上我也行的错觉。

基本业务逻辑说明

url解析

直接从ctx拿变量:
对于[http://localhost:3000/json?a=1&b=2#abc](http://localhost:3000/json?a=1&b=2#abc),结果如下:

  1. {
  2. "href": "http://localhost:3000/json?a=1&b=2",
  3. "path": "/json",
  4. "url": "/json?a=1&b=2",
  5. "query": {
  6. "a": "1",
  7. "b": "2"
  8. },
  9. "querystring": "a=1&b=2",
  10. "search": "?a=1&b=2",
  11. "host": "localhost:3000",
  12. "hostname": "localhost",
  13. "protocol": "http",
  14. "secure": false,
  15. "subdomains": [],
  16. "origin": "http://localhost:3000"
  17. }

设置cookie

直接通过ctx.cookies.get/set(name, opt)设置cookie,简单粗暴

获取具名参数

路由条件字符串中写入/:id/..,就可以将这一段的值赋给ctx.params.id

解析body

需要根据Content-Type对POST等类型的报文的 body 进行解析,从而进行操作。 GET是没有请求体的

使用koa-bodyparserenhance中间件,可以从req.request.body获取解析过的body内容。
如果使用koa-bodyparser还需要注意,参数要设置好可用类型:

  1. app.use(bodyparser({
  2. enableTypes: ['json', 'form', 'text']
  3. }))

表单处理

提交表单必然意味着页面的跳转。但是ajax请求不会跳转

request

response

中间件

Koa的中间件以回环的模式(洋葱圈模型)运作。函数参数ctxnext。没有next的是最内层。

名称 类型 作用
cookie-session enhance 通过cookie来运行c-s之间的会话
cookie-parser enhance 运行常见的cookie功能,比如写入,清除,读取,加密等

中间件类型定义:

  1. enhance:对ctx赋予新属性,重写方法等方式进行增强,供后面用。一定有next
  2. work:进行一些辅助的工作,一定有next。如日志等
  3. final:条件性或必然作为拐点节点,这时可以无next。比如拦截器、路由等。

对于中间件需要注意的事项(个人理解):

  1. 中间件的顺序、API设计很重要;尤其是细节上。
  2. 写的尽量简洁
  3. 个人认为中间件最好别依赖来依赖去的。分成几个必要的layer就可以

    模板引擎

eggjs

基本使用

测试

测试有两个显著的问题:

  1. test.js的断点位置会在测试后被偏移
  2. test.js报非断言错误的test.js的堆栈行不正确。

    package.json

    egg框架使用的字段列表:
    egg

    ts problem

    虽然egg提供了框架本身的ts支持,但这似乎也只是杯水车薪:
    ctx = this.ctx后,ctx就没有了Contextts支持。

    网络安全

    最基本的几个问题:
    XSS攻击:要对用户输入的内容,以及上传文件的内容进行严格的识别和转义,防止里面出现脚本文件
    CSRF:需要CSRF Token
    SQL注入:一般写程序搜索的时候,都是直接在代码里面拼接,这会使得sql注入这类问题出现,直接窃取服务器数据。
    反爬:限制频率,请求次数和请求带宽,尤其是疯狂请求大文件