Node.js介绍

486 阅读7分钟

本文致力介绍node.js以及常用模块

1. Node.js 和 javascript

a.一个完整的Javascript实现由以下三个不同的部分组成

  1. 核心(ECMAscript)
  2. 文档对象模型(DOM)
  3. 浏览器对象模型(BOM)

b. Node.js是在服务端运行的ECMAscript

  1. 浏览器中的js受到浏览器这个盒子的限制,node使用chrome v8引擎编译,打破限制。
  2. 同时Node中也不能访问DOM,BOM。
  3. 可以放心的使用ES6语法(除了import)

c. v8引擎介绍 官网

  1. V8是用C ++编写的Google开源高性能JavaScript和WebAssembly引擎。它用于Chrome和Node.js等
  2. JavaScript通常用于浏览器中的客户端脚本编写,例如用于处理文档对象模型(DOM)对象。但是,DOM通常不是由JavaScript引擎提供,而是由浏览器提供。 V8也是如此-Google Chrome提供了DOM。但是,V8确实提供了ECMA标准中指定的所有数据类型,运算符,对象和函数。
  3. v8优秀的垃圾回收机制是性能强大的原因之一。

d. Node.js初体验

  1. 官网下载安装http://nodejs.cn/
  2. 在终端中输入node -v显示版本即安装成功

3. 创建一个app.js文件,写入代码,在终端执行node app.js ,控制台输出打印信息。

    var f = ()=>{
      console.log(2333);
    }
    f();
    console.log(typeof document);
    console.log(typeof window);
    console.log(typeof global);

2. node.js的应用场景

  1. 打包工具- webpack
  2. 开发工具- webpack-dev-server mock服务器
  3. cdn上传脚本 cdnupload.js
  4. 后端开发 node.js 企业应用案例

3.Node.js中的模块化

a.模块的基础使用

circle.js

const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;

foo.js

const circle = require('./circle.js');
console.log(`半径为 4 的圆的面积是 ${circle.area(4)}`);

b.模块封装器

在执行模块代码之前,Node.js 会使用一个如下的函数封装器将其封装,这是一个闭包,并且注入了一些局部变量,下图我们只定义了一个变量a,控制台显示的局部变量却有很多。

 (function(exports, require, module, __filename, __dirname) {
// 模块的代码实际上在这里
});

注意

  • exports 变量是在模块的文件级作用域内可用的,且在模块执行之前赋值给 module.exports。
  • 如果为 exports 赋予了新值,则它将不再绑定到 module.exports
  • 当 module.exports 属性被新对象完全替换时,通常也会重新赋值 exports

c.模块引用

  • 如果按确切的文件名没有找到模块,则 Node.js 会尝试带上 .js、 .json 或 .node 拓展名再加载
  • 以 '/' 为前缀的模块是文件的绝对路径。 例如, require('/home/marco/foo.js') 会加载 /home/marco/foo.js 文件
  • 当没有以 '/'、 './' 或 '../' 开头来表示文件时,这个模块必须是一个核心模块或加载自 node_modules 目录
  • 若require参数为目录,加载顺序如下
    • ./package.json =>main
    • ./index.js
    • ./index.node

d. 与es6模块的区别

  1. node运行时加载,es6模块编译时加载。
  2. node模块路径可以使用变量,es6模块只能是静态路径,也因此可以在编译阶段解析哪些模块没有被使用,可以做静态优化(如 tree shaking)
  3. 需要webpack tree shaking时必须用import

webpack tree shaking文档

为了学会使用 tree shaking,你必须……
使用 ES2015 模块语法(即 import 和 export)。
在项目 package.json 文件中,添加一个 "sideEffects" 入口。
引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。

4. 事件触发器event

const EventEmitter = require('events');
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('A');
});
myEmitter.emit('event');
  • 大多数 Node.js 核心 API 构建于惯用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)。
  • node许多内置模块封装的对象都是'EventEmitter'的实例,系统设置了监听事件,以下为部分类和监听事件。
模块 事件 何时触发
stream-流 stream.Writable 'close' 可写流-底层资源被关闭,表明不会再触发其他事件
- stream.Readable 'data' 可读流-当流将数据块传送给消费者后触发
fs-文件操作 fs-文件操作 'change' 监听的文件夹改动时触发
http-网络通信 http.ClientRequest http.ClientRequest 当收到此请求的响应时触发。 此事件仅触发一次
- http.Server http.Server 每次有请求时都会触发

5.stream 流

  • 流(stream)是 Node.js 中处理流式数据的抽象接口。 stream 模块用于构建实现了流接口的对象
  • 流可以是可读的、可写的、或者可读可写的。 所有的流都是 EventEmitter 的实例。
  • Node.js 提供了多种流对象, 例如
模块 流类型
http-网络通信 http.IncomingMessage Writable
- http.ServerResponse Readable
fs-文件操作 fs.ReadStream Readable
- fs.ReadStream Writable
  • 可读流和可写流常用函数与事件
事件/函数 触发条件/调用效果
stream.Writable writable.end([chunk[, encoding]][, callback]) 调用 writable.end() 表明已没有数据要被写入可写流
stream.Readable 'data' 事件 当流将数据块传送给消费者后触发
- readable.pipe(destination[, options]) 方法绑定可写流到可读流,将可读流自动切换到流动模式,并将可读流的所有数据推送到绑定的可写流。 数据流会被自动管理,所以即使可读流更快,目标可写流也不会超负荷。
- 'end' 事件 当流中没有数据可供消费时触发

6. 文件操作 fs模块

a. 简介

  • 文件系统中常用的api有读取/创建/写入/删除/修改文件,读取文件信息,监听文件改动.
  • 大部分操作提供了同步和异步两种操作api.
  • 异步api最后一个参数一定是callback,callback第一个参数一定是err。
  • 文档顺序先介绍类,再介绍函数,这些类由函数生成,类中可能包含属性,函数,和监听事件。

b.部分api

操作 同步api 异步api
获取目录/文件信息 fs.statSync(path[, options]) fs.stat(path[, options], callback)
读取文件 fs.readFileSync(path[, options]) fs.readFile(path[, options], callback)
创建文件监听 fs.watch(filename[, options][, listener]) --
创建可读流 fs.createReadStream(path[, options]) --

7. 网络通信 http 模块

  • http模块可以创建http.Server,响应http请求。
  • 也可以创建http.ClientRequest,发起http请求(因为请求不在浏览器中,所以没有跨域限制)

a. 简易服务器

const http=require('http');
const fs=require('fs');

http.createServer((req,res)=>{
    res.write('5678');
    res.end('23333');
    // var writeS=fs.createReadStream('index.html');
    // res.writeHead(200,{'Content-type':'text/html'});
    //writeS.pipe(res);
}).listen(80,()=>{
  console.log('server start');
});

b. 简易客户端

const http =require('http');
  var client=http.request({
      host:'127.0.0.1',
      port:'80',
      method:'get'
  },(res)=>{
      let data='';
      res.on('data',(chunk)=>{
          data+=chunk;
      });
      res.on('end',()=>{
         console.log( data );
      });
  });
  //client.write(请求数据);
  client.end();//必须调用

c. http模块重要类与event

实例何时被创建 说明 important events/function
http.ClientRequest 类 http.request(options[, callback])调用时被创建 客户端 'response'收到响应时触发
response (<http.IncomingMessage>)
http.Server 类 http.createServer([options][, requestListener]) 服务器 'request':收到请求时触发
request(<http.IncomingMessage>,response<http.ServerResponse>)
http.IncomingMessage 类 client response event emitted
server request event emitted
请求类,可读流 'data':收到数据时
'end':数据传输结束时
http.ServerResponse 类 server request event emitted 响应类
可写流
write() 写入数据
end() 数据写入完成
  • http.IncomingMessage 和 http.ServerResponse 分别实现了stream.Readable 和 stream.Writable 接口,详情请参照文档章节 stream 图示

8. node.js练习以及框架介绍

a. 一个简单的静态文件服务器

const mime = require("./mime").types;
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
http.createServer(function(request, response) {
      var pathname = url.parse(request.url).pathname;
      var realPath = "static" + pathname;
      fs.stat(realPath,(err,stat)=>{
          if (err || !stat.isFile()) {
              response.writeHead(404, {'Content-Type': 'text/plain'});
              response.write("This request URL " + pathname + " was not found on this server.");
              response.end();
          } else {
            var ext = path.extname(realPath);
            ext = ext ? ext.slice(1) : 'unknown';
            var contentType = mime[ext] || "text/plain";
            response.writeHead(200, {'Content-Type': contentType});
           var stream =  fs.createReadStream(realPath);
           stream.pipe(response);
          }
      })
  }).listen('3000',()=>{
    console.log('server start')
  });
  

b. node库介绍

node库主要是针对于网络请求进行封装。

  • express:基于 Node.js 平台,快速、开放、极简的 Web 开发框架
  • koa: 由 Express 幕后的原班人马打造, 更小、更富有表现力、更健壮。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件。

c. 库函数实现静态资源服务器

koa例子

const Koa = require('koa');
const path = require('path');
const KoaStatic = require('koa-static');
const app = new Koa();
const staticPath = './static'
app.use(KoaStatic(path.join( __dirname,  staticPath)))
app.listen(3000,()=>{
  console.log('server start');
})

9. node.js调试

a. 普通文件调试

1. node --inspect-brk app.js --inspect进入debugger,-brk会在第一行断点 ,使用时强烈建议加上-brk

2. 在任意页面打开chrome 控制台

3. 可在chrome进行调试

b. 对于npm run build 这样的命令,我们需要找到它真正首先我们需要找到真正执行的js文件.

  1. 打开 package.json,发现使用的命令是 vue-cli-service build

2. 打开node_modules/.bin,这个文件夹包含了所有node-modules下的可执行命令的快捷方式。
3. 找到vue-cli-service,通过这个个快捷方式,我们可以找到原文件路径'./node_modules/@vue/cli-service/bin/vue-cli-service.js'
4. 运行 node ./node_modules/@vue/cli-service/bin/vue-cli-service.js build,现在你知道如何断点了。