Node.js 通通通 (基础)

186 阅读10分钟

Node初认识

Node是什么

Node.js是一个基于Chrome V8引擎的JavaScript运行环境。(nodejs不是一门语言,是一个js运行环境)

Node主要做什么

web服务器开发

安装node

自带包管理工具npm
Npm:node package manager

官网nodejs.org(这个需要翻墙)。没有条件的同学可以用nodejs.org/zh-cn/(但是可能会比较慢)

选择 直接一路下载就好了(如果修改默认路径,需要设置path环境变量)

验证是否安装成功

有版本号表示安装成功

node环境下运行脚本

写一个js文件

const arr = [1,2,3];
arr.push(4);
console.log(arr);

打开终端控制台

node 文件名

就会执行脚本

nodemon

每次修改js文件需要重新执行才会生效,安装nodemon,可以监听文件改动,自动重启

npm i -g nodemon(如果下载失败可参考连接www.jianshu.com/p/ec571d26f… 或自行百度解决方法)

直接nodemon 文件名 即可启动

Node中的异步编程

nodejs 异步编程的直接体现在回调函数上

一边读文件,一边执行其它命令(而不是等待文件读取完毕再来操作)
异步:不等待 (非阻塞式IO操作)
优点:大大提高程序性能,处理大量的并发请求

这里写一个非阻塞代码

不使用promise 或 generate异步编程

第一步:用require来引入fs(fileSystem)模块

fs模块:fs模块用于对系统文件及目录进行读写操作。

第二步:readFile异步编程(异步编程要有回调函数)回调函数两个参数 一个是err 第二个是data

第三步:错误优先机制

//非阻塞式代码 
const fs = require('fs');
//异步编程(异步编程要有回调函数) 两个参数 一个是err 第二个是data 
fs.readFile('./01_runnode.js',function(err,data){
  //错误优先机制
  if(err){
    console.log(err.stack);
    return;
  }
  //得到的data是buffer 里面有个toString方法可以读取文件信息
  console.log(data.toString());
});
console.log('我没有被堵塞');

这里nodemon的小部分截图

ps:如果被堵塞打印出来的文字应该等readFile执行完才打印,所以异步编程可以边读取文件边进行操作,提高性能

fs.readFile有三个参数 第一个是路径 第二个是格式(默认是一个Buffer对象) 第三个是函数 如果第二个参数写成utf-8 data可以打印出来 不需要data.toString()

使用Promise异步编程

  1. 解构util工具包里的promisify (用util来使用es6语法)
  2. promisify 将fs.readFile包装成promise对象
  3. 用async await让异步代码同步化
const fs = require('fs');
const { promisify } = require('util');
// promisify 将fs.readFile包装成promise对象
const readFile = promisify(fs.readFile);

async function asyncReadFile(){
  try {
    //让异步代码同步化
    const data = await readFile('./01_runnode.js');
    console.log(data.toString());
  } catch (error) {
    console.log(err.stack);
  }
}

asyncReadFile();

使用Generate异步编程

const fs = require('fs');
const { promisify } = require('util');
const readFile = promisify(fs.readFile);

function* read(){
  yield readFile('./01_runnode.js');

}
let go = read();
go.next().value.then((result) =>{
  console.log(result.toString());
}).catch((error) =>{
  console.log(error);
})

Buffer对象(缓冲器)

所谓缓冲区Buffer,就是 "临时存贮区" 的意思,是暂时存放输入输出数据的一段内存。

JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一个与String对等的全局构造函数Buffer来提供对二进制数据的操作。除了可以读取文件得到Buffer的实例外,还能够直接构造

Buffer.alloc()

  1. (创建一个长度为 10、以零填充的 Buffer。)
const buf1 = Buffer.alloc(10);
console.log(buf1);

  1. 创建一个长度为 10 的 Buffer,以1填充
const buf2 = Buffer.alloc(10,1);

Buffer.allocUnsafe()

  1. allocUnsafe创建
// 创建一个长度为 10、且未初始化的 buffer。
// 这个方法比调用 Buffer.alloc() 更快,
// 但返回的 Buffer 实例可能包含旧数据,
// 因此需要使用 fill()、write() 或其他能填充 Buffer 的内容的函数进行重写。
const buf3 = Buffer.allocUnsafe(10);

Buffer.from()

4.创建一个包含字节 [1, 2, 3] 的 Buffer。

const buf3 = Buffer.from([1,2,3]);

ps:其中所有条目均使用 `(value & 255)` 进行截断以符合 0-255 的范围。

例:

const buf4 = Buffer.from([257, 257.5, -255, '1']);

会转换成

Buffer.write();

  1. 往buffer中写入数据 更改buffer数据 但是不会超出或者自动缩减长度 这里使用buf1(定义的长度是10)
buf1.write('hello');

Buffer.toString();

表示如何用什么来解码数据 默认是UTF-8 Buffer.toString('UTF-8')

Buffer.concat();

两个buffer合并 例:

Buffer.concat([buf1,buf6]);

Stream 流(文件传输的一种形式)

Node.js中由四种基本的流类型

  1. Writable —— 可写入数据的流(例如:fs.createWriteStream()).
  2. Readable —— 可读取数据的流(例如:fs.createReadStream()).
  3. Duplex —— 可写入又可读取数据的流 (例如:fs.createDeflate()).
  4. Transform —— 在读写过程中可以修改或转换数据的Duplex流(例如:zlib.createDeflate()).

管道流

创建一个可读流将一个文件中的数据传输到一个文件的可写流

用可读流的pipe方法进行传输 pipe(可写流)

//管道流
const fs = require('fs');
//创建可读流
const readerStream = fs.createReadStream('./reademe.md');;
//创建可写的流
const writerStream = fs.createWriteStream('./test.txt')

//通过管道进行传输
readerStream.pipe(writerStream);
console.log("执行完毕");

上面的操作将readme.md的数据传输到test.txt文件中 如果没有这个文件会自动创建

压缩与解压

压缩与解压利用了流来进行操作,如果要对一个文件进行压缩

压缩

  1. 引入fs与zlib模块
  2. 获取zlib中createGzip方法(用来压缩的方法)
  3. 可读流 . pipe(操作方法) . pipe(可写流)
const fs = require('fs');
const zlib = require('zlib');
const gzip = zlib.createGzip();
fs.createReadStream('test.txt').pipe(gzip).pipe(fs.createWriteStream('test.txt.gz'));

这里是将test.txt文件压缩 并且输出一个test.txt.gz压缩包

解压

  1. 引入fs与zlib模块
  2. 获取zlib中createGunzip方法(用来压缩的方法)
  3. 可读流 . pipe(操作方法) . pipe(可写流)
const fs = require('fs');
const zlib = require('zlib');
const gunzip = zlib.createGunzip();
fs.createReadStream('test.txt.gz').pipe(gunzip).pipe(fs.createWriteStream('test2.txt'));

这里是将test.txt.gz压缩包解压,并且输出一个test2.txt文件

events事件循环(了解)

Node.js使用事件驱动模型,web server一直接收请求而不等待任何读写操作(这被称为非阻塞式IO或者事件驱动IO)。在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。

为要操作的事件绑定处理程序

  1. 引入events模块 用events的EventEmitter方法new一个事件处理程序
  2. eventsEmitter.on绑定事件处理程序
  3. eventsEmitter.emit触发事件处理程序
const events = require('events');
const eventsEmitter = new events.EventEmitter();
//绑定事件处理程序 定好一个主题
eventsEmitter.on('con', function(){
  console.log('连接成功');

  //触发另外的事件处理 
  eventsEmitter.emit('data_received');
});
eventsEmitter.on('data_received', function(){
  console.log('数据接收成功 ');
})
//观察者 观察对应的主题要做什么事情 
eventsEmitter.emit('con');
console.log('执行完毕');

CommonJS规范以及模块系统

nodejs提供了exports和require两个对象,其中exports是模块公开的接口,require用于从外部获取一个模块的接口

一个js文件就可以是一个模块,可以抛出,可以被其他js文件所接收。抛出有两个方法

  1. exports.xx = ....
  2. module.exports = xx; xx可以是任何数据类型

不同点在于

  1. 如果用module.exports抛出的对象,是它自己本身 module.exports只能抛出一个对象
  2. 如果用exports.xx抛出的对象,xx会挂载到exports对象上 exports可以抛出多个参数 假设同个文件用不同的抛出方法 文件名为test, test文件里有个叫Dog的函数
const obj = require('./test')

用第一个方法与第二个方法接收的obj打印出来的分别是是

require方法中的文件查找策略(重要)

常用内置模块

fs

fs.open(path, flags[, mode], callback)(打开文件)

参数

  • path -文件路径
  • flags - 文件打开行为(见下图)
  • mode - 设置文件模式(权限),文件创建默认权限为0666(可读可写)
  • callback - 回调函数,带有两个参数(err,fd)

fs.writeFile(filename, data[, options], callback) (写入文件)

参数

  • path - 文件路径
  • data - 要写入文件的数据,可以使string或buffer对象
  • options - 该参数是一个对象,包含{encoding,mode, flag}。默认编码是utf-8,模式为0666,flag为‘w’
  • callback - 回调函数,只包含错误信息参数(err),在写入失败时返回。

fs.read(fd, buffer, offset, length,position, callback)(读取文件)

ps:参数中由fd的一般都在fs.open()里调用

参数

  • fd - 通过 fs.open() 方法返回的文件描述符。
  • buffer - 数据写入的缓冲区。
  • offset - 缓冲区写入的写入偏移量。
  • length - 要从文件中读取的字节数。
  • position - 文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。
  • callback - 回调函数,有三个参数err, bytesRead, buffer,err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象。

fs.close(fd, callback)(关闭文件)

fs.ftruncate(fd, len, callback)(截取文件)

参数

  • fd - 通过fs.open()方法返回的文件描述符
  • len - 文件内容截取的长度
  • callback - 回调函数,没有参数

fs.unlink(path, callback)(删除文件)

fs.mkdir(path[, mode], callback)(创建目录)

参数

  • path - 文件路径
  • mode - 设置目录权限 默认为0777
  • callback - 回调函数 没有参数

fs.readdir(path, callback)(读取目录)

fs.rmdir(path, callback)(输出目录)

os

os 模块提供了一些基本的系统操作函数

详细可参考文章 nodejs.jakeyu.top/#os-%E6%A8%…

var os = require("os");

// CPU 的字节序
console.log('endianness : ' + os.endianness());

// 操作系统名
console.log('type : ' + os.type());

// 操作系统名
console.log('platform : ' + os.platform());

// 系统内存总量
console.log('total memory : ' + os.totalmem() + " bytes.");

// 操作系统空闲内存量
console.log('free memory : ' + os.freemem() + " bytes.");

url

url 模块用于处理与解析 URL

详细可参考文章 nodejs.cn/api/url.htm…

const myURL =
  new URL('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');
  console.log(myURL);

path

path 为路径模块

常用方法

  • path.join(_dirname);获取当前项目的绝对路径的目录名字
  • path.resolve('文件名');将相对路径转绝对路径
  • path.extname('文件名');获取路径中文件的后缀名
const path = require('path');
//__dirname 获取当前项目的绝对路径的目录名字   __filename当前项目的绝对路径
console.log(path.join(__dirname,'b'));

//resolve 将相对路径转绝对路径
console.log(path.resolve('11-path模块.js'));

//获取路径中文件的后缀名
console.log(path.extname('11-path模块.js'));

path模块更多信息可参考 nodejs.cn/api/path.ht…

http模块搭建webServer服务器

  1. 引入http模块
  2. 调用http的createServer方法
  3. 监听端口号
//1. 引入http模块
const http = require('http');

//2.创建appServer对象 request response
const app = http.createServer((req, res) => {
  res.end('hello node 撒拉嘿');
})

//3.监听端口号 http://localhost:3000/
app.listen(3000);

打开浏览器输入 http://localhost:3000/ (注意要先nodemon)

在上述代码的app里console.log(req)会返回一大串request信息,里面包含了url跟method

加载文本页面

const { url, method } = req;

从req中拿到url跟method去做判断 例:/index

  1. 判断url是否为/index method是否为GET
  2. 如果判断为true 则用fs.readFile()读取对应的文件(需要自行创建) fs.readFile()有三个参数,第一个是读取文件的路径,第二个是解码方式(可不写 默认utf-8),第三个是回调函数做处理
  3. 在回调函数中,如果出错返回错误 可设置res.statusCode = 500 来告知出错。如果有data则用res.end(data)渲染数据
if(url === '/index' && method === 'GET'){
    //返回一个首页给浏览器
    fs.readFile('./static/index.html',(err,data) => {
      if(err){
        //当读取文件路径出错 设置一个状态码表示错误
        res.statusCode = 500;
        res.end('500 - Interval Serval Error!')
      }else{
        res.statusCode = 200;
        //设置响应头 第一个参数是要设置的属性名,第二个是属性的类型
        res.setHeader('Content-Type', 'text/html');
        res.end(data);
      }
    })

用到的api

res.end();页面返回的数据

res.statuCode = xxx; 设置状态码

res.setHeader();设置响应头,第一个参数是要设置的属性名,第二个是属性的类型

加载图片

  1. 用req.headers.accept.indexOf('image/*') !== -1来作为判断条件
  2. 创建一个可读流 用__dirname+url来拼接图片路径
  3. 用pipe方法传输到response里
   if(req.headers.accept.indexOf('image/*') !== -1 		&& method === 'GET'){
    console.log(url); ///static/img/1.jpg
    //流的方式读取 创造一个可读流传输到response
    fs.createReadStream(__dirname+url).pipe(res);
  }

这里可能会报错 不用管 崩溃了重新nodemon

用到的api

req.headers.accept.indexOf('image/*') Accept 头属性能被用在浏览器响应能接受的media 类型

fs.createReadStream()创建可读流

pipe()流 流到何处

加载json

使用上述的res.setHeader();

res.setHeader('Content-Type', 'application/json');

就会把接收到的数据转化成json