node 简介 
node.js 是⼀个 JS 的服务端运⾏环境,基于 V8,是在 JS 语⾔规范的基础上,封装了⼀些服务端的 runtime,能够简单实现⾮常多的业务功能。在 2009 年 (第一版 npm 被创建) 诞生之初是为了实现高性能的 web 服务器,再后来 node.js 慢慢演化为了一门服务端 “语言”
 
引入 node 作用 
跨平台开发:  PC  web  H5  RN  Weex 
后端开发:  API, RPC 
前端开发:  前端工具链 
工具开发: 脚本、脚手架、命令行。 
 
举例 
压缩: UglifyJS、JSMin 
管理: npm、yarn、bower 
模块系统: Commonjs, ESM 
模块构建: Babel、Browserify、Webpack、Gulp、Grunt 
生成器: yeoman、slush、CRA、CLI 
 
node 缺陷 
单线程很脆弱,但是可以通过 cluster / pm2 多核并发实现负载均衡 
node 对 MongoDB、Mysql、redis 的支持比较好,对 neo4j、tigerGraph 的支持比较差 
安全问题 
 
vs 浏览器 
Node 环境中是没有 DOM, BOM, 同样的,浏览器中也没有 fs, path 这些模块。 
事件循环
node 的事件循环 
浏览器:  微任务、宏任务、raf、 render、 requestIdleCallback 
 
 
cjs 和 esm
Node.js 使用 CommonJS 模块系统,而在浏览器中我们开始看到正在实施的 ESM 标准。 
 
 
内核 
npm npm install 工作流程 
npm CI 
Continuous Integration (持续集成),npm 从 5.7.0 版本开始引入的一个命令,专门为自动化的持续集成环境设计。和 install 的不同点
必须要有 package-lock.json 文件 
且下载完全依赖该文件 
会删除 node_modules 
如果和 package.json 冲突,则直接报错 
只能一次性安装 
永不改写 package.json 和 package-lock.json 文件 
 
 
Dependencies 
dependencies: 项目依赖 (lodash (debounce, deepMerge)) 
devDependencies: 开发依赖 (webpack, rollup, jest) 
peerDependencies: 同版本依赖。比如 vue 组件库,如果说连 vue 都没,那这个项目没有意义 
bundledDependencies: 捆绑依赖 
optionalDependencies: 可选依赖 
 
npm、cnpm、yarn、pnmp、npx 
npm: 包管理器,方便开发者分享和下载开源包。经历了许多重大版本的更新,各方面已经和 yarn 在同一水平
npx: npm@5.2的产物,方便运行本地命令
 
npx 会帮你执行依赖包里的二进制文件: 不需要在 scripts 中声明命令 
npx 原理: 运行的时候,会到 node_modules/.bin 路径和环境变量$PATH里面,检查命令是否存在。由于 npx 会检查环境变量$PATH,所以系统命令也可以调用。 
避免全局安装模块: npx 将 create-react-app 下载到一个临时目录,使用以后再删除。所以,以后再次执行上面的命令,会重新下载 create-react-app。 
—no-install: 如果想让 npx 强制使用本地模块,不下载远程模块,可以使用—no-install 参数。如果本地不存在该模块,就会报错。 
—ignore-existing: 如果忽略本地的同名模块,强制安装使用远程模块,可以使用—ignore-existing 参数。 
使用不同版本的 node: $ npx node@0.12.8 -v,原理是从 npm 下载这个版本的 node,使用后再删掉。某些场景下,这个方法用来切换 Node 版本,要比 nvm 那样的版本管理器方便一些。 
-p: 用于指定 npx 所要安装的模块。$ npx -p node@0.12.8 node -v,先指定安装node@0.12.8,然后再执行 node -v 命令。 
-c: 如果 npx 安装多个模块,默认情况下,所执行的命令之中,只有第一个可执行项会使用 npx 安装的模块,后面的可执行项还是会交给 Shell 解释。-c 参数可以将所有命令都用 npx 解释 
 
cnpm: 方便中国开发者下载依赖包而诞生的下载器
yarn: 解决了 npm@5 之前的一些让人诟病的问题,同时拥有一些其它的优点。例如离线安装、失败自动重试安装和并行下载等
pnpm: 通过连接的方式,让多个项目的依赖公用同一个包,大大节省了磁盘空间
 
pnpm 运行起来非常的快,超过了 npm 和 yarn 
pnpm 采用了一种巧妙的方法,利用硬链接和符号链接来避免复制所有本地缓存源文件, yarn 的最大的性能弱点之一 
使用链接并不容易,会带来一堆问题需要考虑 
pnpm 继承了 yarn 的所有优点,包括离线模式和确定性安装 
 
node API Buffer 
Buffer 是一种计算机中数据流结构。计算机中是以二进制的方式,进行数据存取的。而 js 在一开始,没有文件读写能力的,就要借助 Buffer 来实现一些缓冲区的内容。
 
Buffer 的声明 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let  buf1 = Buffer .alloc (5 ); let  buf2 = Buffer .from ("麓一" ); let  buf3 = Buffer .from ([0xe9 , 0xba , 0x93 ]);console .log (buf1); console .log (buf2); console .log (buf3.toString ()); buf2.copy (buf1, 0 , 0 , 2 ); let  bigBuffer = Buffer .concat ([buf1, buf2], 6 );buf1.slice (0 , 6 ); Buffer .isBuffer (buf);
Stream 
防止淹没可用内存: Buffer 不适合大文件的读取,适合比较小的文件,对于大文件,需要使用流
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 const  fs = require ("fs" );const  path = require ("path" );const  res = fs.createReadStream (path.resolve (__dirname, "../package.json" ), {  flags : "r" ,   start : 0 ,   end : 20 ,   highWaterMark : 5 ,    autoClose : true ,   emitClose : true , }); let  arr = [];res.on ("open" , function  (fd ) {   console .log ("fd" , fd); }); res.on ("data" , function  (data ) {   console .log ("data" , data);   arr.push (data); }); res.on ("end" , function  (   console .log ("end" , Buffer .concat (arr).toString ()); }); res.on ("close" , function  (   console .log ("close" ); }); res.on ("error" , function  (   console .log ("error" ); }); 
cluster 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const  fs = require ("fs" );const  path = require ("path" );const  http = require ("http" );const  os = require ("os" );const  { default : cluster } = require ("cluster" );const  cpu_num = os.cpus ();if  (cluster.isMaster ) {  for  (let  i = 0 ; i < cpu_num.length ; i++) {     cluster.fork ();   } } else  {   http     .createServer ((req, res ) =>  {       res.end ("childPid" , process.pid );     })     .listen (3000 ); } 
事件循环 浏览器的事件循环 
messageBump: 宏任务 -> 微任务 -> RAF -> Layout -> RequestIdleCallback
 
Node 的事件循环 
基于 Libuv。Libuv 是一个高性能的、事件驱动的 I/O 库,为 Node.js 提供了跨平台的异步 I/O 能力,使之能够高效地处理大量并发请求
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27              同步的代码                  |      process.nextTick / promise...                  |    ┌───────────────────────────┐ ┌─>│           timers          │ 定时器:  setTimeout / setInterval │  └─────────────┬─────────────┘ |    process.nextTick / promise... │  ┌─────────────┴─────────────┐ │  │     pending callbacks     │ 执行延迟到下一个循环迭代的I/O回调 │  └─────────────┬─────────────┘ |    process.nextTick / promise... │  ┌─────────────┴─────────────┐ │  │       idle, prepare       │ 系统内部使用 │  └─────────────┬─────────────┘      ┌───────────────┐ |    process.nextTick / promise... │  ┌─────────────┴─────────────┐      │   incoming:   │ │  │           poll            │<─────┤  connections, │ │  └─────────────┬─────────────┘      │   data, etc.  │ |    process.nextTick / promise... │  ┌─────────────┴─────────────┐      └───────────────┘ │  │           check           │ setImmediate │  └─────────────┬─────────────┘ |    process.nextTick / promise... │  ┌─────────────┴─────────────┐ └──┤      close callbacks      │ 关闭回调函数    └───────────────────────────┘ 
定时器: 本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。 
待定回调: 执行延迟到下一个循环迭代的 I/O 回调。 
idle, prepare: 仅系统内部使用。 
轮询: 检索新的 I/O 事件; 执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。 
检测: setImmediate() 回调函数在这里执行。 
关闭的回调函数: 一些关闭的回调函数,如: socket.on(‘close’, …)。 
 
总结Node.js: microtask 在事件循环的各个阶段之间执行 > 浏览器: microtask 在事件循环的 macrotask 执行完之后执行
 
node 的初始化 
 
初始化 node 环境 
执行输入代码 
执行 process.nextTick 回调 
执行微任务队列 
 
进入 event-loop 
 
进入 timers 阶段(执行 setTimeout 和 SetInterval)
检查 timer 队列是否有到期的 timer 回调,如果有,将到期的 timer 回调按照 timerid 升序进行 
检查是否有 process.nextTick 任务,如果有全部执行 
检查是否有 microtask,有全部执行 
退出该阶段 
 
 
进入 IO 阶段
检查是否有 pending 的 io 回调,如果有,执行回调,如果没有,退出该阶段 
检查是否有 process.nextTick()任务,如果有,全部执行 
检查是否有 MicroTask,如果有全部执行 
退出该阶段 
 
 
 
进入 idle,prepare 阶段 
进入 poll 阶段 
 
首先检查是否存在尚未完成的回调,存在,则
如果 有可用回调, 
 
那么执行 
检查是否有 process.nextTick()回调,有全部执行 
检查是否有 MicroTask,如果有全部执行 
退出该阶段 
 
如果没有可用回调 
 
检查是否有 immediate 回调,如果有退出 poll 阶段,如果没有,阻塞在此阶段,等待新的事件通知 
 
 
如果不存在尚未完成的回调,退出 poll 阶段 
 
进入 check 阶段 
 
如果有 immediate 回调,执行所有回调 
检查是否有 process.nextTick()回调,如果有,全部执行 
检查是否有 MicroTask,如果有全部执行 
退出该阶段 
 
进入 closing 阶段 
 
如果有 immediate 回调,则执行所有 immediate 回调。 
检查是否有 process.nextTick 回调,如果有,全部执行。 
检查是否有 microtaks,如果有,全部执行。 
退出 closing 阶段 
 
检查是否有活跃的 handles(定时器、io 等事件) 
 
如果有,继续下一轮循环 
没有则结束事件循环,退出程序 
 
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 async  function  async1 (  console .log ("async1 started" );   await  async2 ();   console .log ("async end" );  } async  function  async2 (  console .log ("async2" ); } console .log ("script start." );setTimeout (() =>  {  console .log ("setTimeout0" );   setTimeout (() =>  {     console .log ("setTimeout1" );   }, 0 );   setImmediate (() =>  {     console .log ("setImmediate" );   }); }, 0 ); async1 ();process.nextTick (() =>  {   console .log ("nextTick" );  }); new  Promise ((resolve ) =>  {  console .log ("promise1" );   resolve ();   console .log ("promise2" ); }).then (() =>  {   console .log ("promise.then" );  }); console .log ("script end." );
Node 框架 express / koa 
express 是一个基于 node.js 平台的一个灵活的 web 应用开发框架,connect 中间件,内置了视图、static 等部分
 
1 2 3 4 5 6 7 8 const  express = require ("express" );const  path = require ("path" );const  app = express ();app.use (express.static (path.resolve (__dirname, "./public" ))); app.listen (3000 , () =>  console .log ("server is running in 3000" )); 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 const  Koa  = require ("koa" );const  app = new  Koa ();const  api  = (  new  Promise ((resolve ) =>  {     setTimeout (() =>  {       console .log ("timing..." );       resolve (100 );     }, 100 );   }); app.use (async  (ctx, next) => {   console .log ("querying start 1" );   const  result = await  api ();   ctx.result  = result;   await  next ();   console .log ("querying end 1" ); }); app.use (async  (ctx, next) => {   console .log ("querying start 2" , ctx.result );   next ();   console .log ("querying end 2" ); }); app.use (async  (ctx, next) => {   console .log ("querying start 3" );   next ();   console .log ("querying end 3" ); }); const  main  = (ctx ) => {  ctx.body  = "hello world" ; }; app.use (main); app.listen (3008 ); 
洋葱模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 function  num (ctx, next ) {  console .log ("starting num ..." );   next (ctx * 10 );   console .log ("ending num ..." ); } function  discount (ctx, next ) {  console .log ("starting discount ..." );   next (ctx * 0.8 );   console .log ("ending discount ..." ); } function  express (ctx, next ) {  console .log ("starting express ..." );   next (ctx + 12 );    console .log ("ending express ..." ); } function  compose (args ) {  let  result;   return  function  (ctx ) {          let  i = 0 ;     let  dispatch = function  (i, ctx ) {       let  fn;       if  (i < args.length ) fn = args[i];        if  (i === args.length ) {         result = ctx;         return ;       }       return  fn (ctx, dispatch.bind (null , ++i));     };     dispatch (i, ctx);     return  result;   }; } const  sell = compose ([num, discount, express]);console .log (sell (150 )); 
koa 框架原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 listen (...args ) {    debug ('listen' );     const  server = http.createServer (this .callback ());     return  server.listen (...args); } callback (		     const  fn = compose (this .middleware ); 		     if  (!this .listenerCount ('error' )) this .on ('error' , this .onerror ); 		     const  handleRequest  = (req, res ) => {       const  ctx = this .createContext (req, res);       return  this .handleRequest (ctx, fn);     };     return  handleRequest; } handleRequest (ctx, fnMiddleware ) {    const  res = ctx.res ;     res.statusCode  = 404 ;     const  onerror  = err => ctx.onerror (err);          const  handleResponse  = (respond (ctx);          onFinished (res, onerror);          return  fnMiddleware (ctx).then (handleResponse).catch (onerror); } use (fn ) {    ...     this .middleware .push (fn);     return  this ; } createContext (req, res ) {	     const  context = Object .create (this .context );     const  request = context.request  = Object .create (this .request );     const  response = context.response  = Object .create (this .response );          context.app  = request.app  = response.app  = this ;     context.req  = request.req  = response.req  = req;     context.res  = request.res  = response.res  = res;          request.ctx  = response.ctx  = context;     request.response  = response;     response.request  = request;     context.originalUrl  = request.originalUrl  = req.url ;     context.state  = {};     return  context; } 
koa-compose 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 "use strict" ;module .exports  = compose;function  compose (middleware ) {  if  (!Array .isArray (middleware))     throw  new  TypeError ("Middleware stack must be an array!" );   for  (const  fn of  middleware) {     if  (typeof  fn !== "function" )       throw  new  TypeError ("Middleware must be composed of functions!" );   }      return  function  (context, next ) {          let  index = -1 ;     return  dispatch (0 );     function  dispatch (i ) {       if  (i <= index)         return  Promise .reject (new  Error ("next() called multiple times" ));       index = i;       let  fn = middleware[i];       if  (i === middleware.length ) fn = next;       if  (!fn) return  Promise .resolve ();       try  {         return  Promise .resolve (fn (context, dispatch.bind (null , i + 1 )));       } catch  (err) {         return  Promise .reject (err);       }     }   }; } 
koa 常用中间件 
koa2-cors 
koa-static 
koa-bodyparser 
 
BFF 
Backends For Frontends 
在后端普遍采用微服务的情况下,作为适配层,更好的为前端服务。
 
Sequelize 
Sequelize 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能