Node.js基础

Node.js基础

Node.js 介绍

是什么

  • 简单的说 Node.js 就是运行在服务端的 JavaScript。
  • Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
  • Node.js 脱离了浏览器,可以操作网络、文件等
  • Node.js = ECMAScript + 各种基于 ES 的模块(net、http、fs)

用途

  • 服务端开发(通过网络提供各种数据)
  • 工具开发:vue-cli / create-react-app
  • 桌面端(electorn, nw.js):vsCode

安装Node.js

  • Node.js官网下载稳定版本,左边为稳定版本,右边为非稳定版本
  • 安装完Node.js会自动安装NPM(Node Package Manager) - 包管理工具
  • 通过指令 node -v 来查看是否安装完成和查看node版本号;npm -v 来查看npm版本

Node.js 创建第一个应用

步骤一、引入 required 模块

使用 require 指令来载入 http 模块,并将实例化的 HTTP 赋值给变量 http

var http = require("http");
步骤二、创建服务器

使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定 8888 端口。 函数通过 request, response 参数来接收和响应数据。

var http = require('http');

http.createServer(function(request, response) {

    // 发送 HTTP 头部 
    // HTTP 状态值: 200 : OK
    // 内容类型: text/plain
    response.writeHead(200, { 'Content-Type': 'text/html;charset=UTF-8' });

    // 发送响应数据 "第一个应用"
    response.end('第一个应用\n');
}).listen(8888);

// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');

使用 node 命令执行以上的代码:

node server.js
Server running at http://127.0.0.1:8888/

打开浏览器访问 http://127.0.0.1:8888/
在这里插入图片描述

Node.js模块系统

node没有全局作用域。在Node.js中,通过require方法加载和执行多个JavaScript脚本文件。文件与文件之间由于是模块作用域,即使加载执行多个文件,可以完全避免变量命名冲突污染。但是某些情况下,模块与模块是需要进行通信的,可通过require方法得加载文件模块导出的接口对象。即:

  • 模块作用域
  • 通过require方法,加载文件模块和执行里面的代码
  • 通过require方法,得加载文件模块导出的接口对象exports
引入模块

在 Node.js 中,引入一个模块非常简单,如下我们创建一个 main.js 文件并引入 hello 模块

var hello = require('./hello');
hello.world();

以上实例中,代码 require(’./hello’) 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)
Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
接下来创建 hello.js 文件

exports.world = function() {
  console.log('Hello 这是hello模块');
}

如果只是想把一个对象封装到模块中

代码格式如下:
module.exports = function() {
  // ...
}

例如:
//hello.js 
function Hello() { 
    var name; 
    this.setName = function(thyName) { 
        name = thyName; 
    }; 
    this.sayHello = function() { 
        console.log('Hello ' + name); 
    }; 
}; 
module.exports = Hello;
//main.js 
var Hello = require('./hello'); 
hello = new Hello(); 
hello.setName('yy'); 
hello.sayHello(); 

模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。

Node.js 中自带了一个叫做 http 的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。
这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象
exports 和 module.exports 的使用

如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(类似class,包含了很多属性和方法),就用 module.exports。

不建议同时使用 exports 和 module.exports。
如果先使用 exports 对外暴露属性或方法,再使用 module.exports 暴露对象,会使得 exports 上暴露的属性或者方法失效。原因在于,exports 仅仅是 module.exports 的一个引用

内置模块
  • 为了实现一些文件操作,网络系统等操作,这些都是原本 JavaScript 不能实现的,由 Node.js 提供,我们也可以称他们为内置模块或者是官方模块,我们不需要进行下载,也不需要自定义,他已经集成在 Node.js 内,我们也可以理解为这些模块是随着我们安装 Node.js 的时候一并安装进来的
  • Node.js 内置模块 - Buffer,C/C++Addons,Child Processes,Cluster,Console,Crypto,Debugger,DNS,Domain,Errors,Events,File System,Globals,HTTP,HTTPS,Modules,Net,OS,Path,Process,P unycode,Query Strings,Readline,REPL,Stream,String De coder,Timers,TLS/SSL,TTY,UDP/Datagram,URL, Utilities,V8,VM,ZLIB等
path 模块
  • __dirname - 指向被执行 js 文件的绝对路径
  • __firename - 指向被执行 js 文件的绝对路径 + js文件名
  • ./ - 你执行 Node.js 命令的路径,即工作路径
规范化路径

path.normalize 不同系统的路径有可能不同,例如,Unix系统是 /,Windows系统是 \ 该方法可规范化路径

连接路径

path.join 该方法path使用特定于平台的分隔符作为分隔符将所有给定的段连接在一起,然后对结果路径进行规范化

path.join('..', 'fs', '1', '1.txt'); // windows下:..\fs\1\1.txt
拼接路径段为绝对路径

path.resolve( [from…], to ) 与 path.join 不同,前者只是简单的将路径段进行连接,而 path.resolve 会对路径段进行解析,给定的路径的序列是从右往左被处理的,后面每个 path 被依次解析,直到构造完成一个绝对路径

path.resolve('/foo/bar', './baz')   // returns '/foo/bar/baz'
path.resolve('/foo/bar', 'baz')   // returns '/foo/bar/baz'
path.resolve('/foo/bar', '/baz')   // returns '/baz'
path.resolve('/foo/bar', '../baz')   // returns '/foo/baz'
path.resolve('home','/foo/bar', '../baz')   // returns '/foo/baz'
path.resolve('home','./foo/bar', '../baz')   // returns '/home/foo/baz'
path.resolve('home','foo/bar', '../baz')   // returns '/home/foo/baz'
path.resolve('home', 'foo', 'build','aaaa','aadada','../../..', 'asset') //return '/home/foo/asset'
判断参数 path 是否是绝对路径

path.isAbsolute 返回布尔值

path.isAbsolute(__dirname); // true
将绝对路径转为相对路径

path.relative(from, to) 基于当前工作目录,返回从 from 到 to 的相对路径

path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb'); // ../../impl/bbb
返回路径最后一部分/去除后缀

path.basename(path[, ext]) 此方法区分大小写,包括扩展名

path.basename('/foo/bar/baz/asdf/quux.html'); // quux.html
path.basename('/foo/bar/baz/asdf/quux.html', '.html'); // quux
path.basename('/foo/bar/baz/asdf/quux.HTML', '.html'); // quux.HTML
返回路径后缀名
  • path.parse
    返回路径字符串的对象
  • path.format(pathObject)
    从对象中返回路径字符串,和 path.parse 相反
  • 对象属性:
    root - 根路径
    dir - 路径
    base - 文件名及后缀
    name - 文件名
    ext - 文件后缀
path.parse('/home/user/dir/file.txt'); // { root: '/', dir: '/home/user/dir', base: 'file.txt', ext: '.txt', name: 'file' }
path.format({ root: '/', dir: '/home/user/dir', base: 'file.txt', ext: '.txt', name: 'file' }); // /home/user/dir/file.txt
File system(fs) 模块

fs是文件操作模块,跟数据库操作有些类似,大的方向为 - 增、删、改、查
区别:

  • 文件操作
  • 目录操作(文件夹/容器)
文件操作
  • 文件写入
    • fs.writeFile(file, data[, options], callback)
      异步模式下写入文件的语法格式 writeFile 直接打开文件默认是 w 模式,所以如果文件存在,该方法写入的内容会覆盖旧的文件内容。
const fs = require('fs'); // 文件操作

fs.writeFile('1.txt', '我是内容', err => {
    if (err) return console.log(err);
    console.log('写入成功');
});
fs.writeFile('1.txt', '我是内容', { flag: 'w' }, err => { });
  • 文件读取
    fs.read(fd, buffer, offset, length, position, callback) 异步模式下读取文件的语法格式
    不写编码格式的情况下,我们可以通过 .toString() 的方法转化成中文
    (接收一个参数 - 编码格式,如不填写参数,则将使用 utf-8 作为默认值,需确保内容为字符串)
fs.readFile('1.txt', 'utf-8', (err, data) => {
    if (err) return console.log(err);
    console.log(data);
});
fs.readFile('1.txt', (err, data) => { });
  • 文件操作的同步与异步
    所有文件操作都是有同步和异步之分,同步方法会加上 “Sync”,同步操作会造成代码阻塞,视情况采用不同的方法
  • 文件修改(文件名)
    fs.rename(oldPath, newPath, callback) 文件内容修改我们一般是先读取文件内容,再按需修改,重新写入,所以 fs 修改的是文件名
  • 文件删除
    fs.unlink(path, callback) 删除文件的语法格式
  • 文件复制
    fs.copyFile
    在以前版本的 Node.js 是没有复制方法的,我们需要通过 读取文件 -> 写入文件 进行复制
    参数:
    1. 需要复制的文件路径/名称
    2. 指定的文件路径/名称
    3. 函数回调
fs.copyFile('1.txt', '2.txt', err => {
    if (err) return console.log(err);
    console.log('复制成功');
});
目录操作
  • 创建目录
    fs.mkdir(path[, options], callback) 创建目录的语法格式
fs.mkdir('1', err => {
    if (err) return console.log(err);
    console.log('创建成功');
});
  • 读取目录
    fs.readdir(path, callback) 读取目录的语法格式
fs.readdir('1', (err, data) => {
    if (err) return console.log(err);
    console.log(data);
});
  • 目录修改(目录名)
    fs.rename 使用与 文件修改 一致
  • 目录删除
    fs.rmdir(path, callback) 删除目录的语法格式
    删除目录只能删除空目录/文件夹,否则会报错
    删除非空文件夹需要 - 先把目录内的文件/文件夹删除 -> 删除空目录
function removeDir(_path) {
    const data = fs.readdirSync(_path);
    data.forEach(item => {
        // 判断是否是文件 文件: 直接删除 目录:递归
        // 注意:item是名称,需要组装完整路径: path.join(_path, item)
        const url = path.join(_path, item);
        if (fs.statSync(url).isFile()) {
            // 文件: 直接删除
            fs.unlinkSync(url);
        } else {
            // 目录:递归
            removeDir(url)
        }
    });
    // 删除空目录
    fs.rmdirSync(_path);
}

removeDir('1');
文件与目录操作通用方法
  • fs.rename 修改名称
  • fs.existsSync 判断文件或者目录是否存在
  • fs.stat 获取文件或者目录的详细信息
http 模块

http模块被用于服务器开发,简单来说,就是监听网络(端口),当有客户端请求了,那么就返回对应的数据

  • http.createServer 接收一个回调函数,回调函数有两个传参
  • request - http.ClientRequest 类 储存当前请求的客户端信息和方法,客户端访问的地址(url)与后端的文件不是一对一的关系,他们只是以中虚拟映射的关系,这个关系是我们后端程序根据实际情况返回的
  • response - http.ServerResponse 类
    提供了服务器响应相关的信息和方法
    1. response.write(chunk[, encoding][, callback]) 浏览器会响应并把内容打在页面上,我们可以 html 等各种资源(数据)储存在外部文件中,然后通过 node 去读取,编译成浏览器可读的形式传给浏览器
    2. response.end([data[, encoding]][, callback]) 此方法向服务器发送信号,指示所有响应头和主体已发送。该服务器应认为此消息已完成。response.end()必须在每个响应上调用方法,否则页面会一直卡着
    3. response.setHeader() 设置头部信息
    4. response.writeHeader(statusCode[, statusMessage][, headers]) 发送写入头信息(包括状态码),response.writeHeader 必须在 response.setHeader 后面,response.setHeader 将合并传递给 response.writeHeader 给定的优先级
  • server.listen(‘端口号’,‘callback’)
    在当前电脑上监听一个指定的端口
require的加载规则
即根据模块标识来加载即:require('模块标识符')
  1.自己写的模块
    路径形式的模块:1./ 当前目录,不可省略 , 2../ 上一级目录,不可省略 3.js 后缀名可以省略
      var b = require('./foo.js')
      var b = require('./foo')
  2.核心模块
    核心模块的本质也是文件,已经被编译到了二进制文件中(下载后,编译在node.exe),我们只需要按照名字来加载就可以了
      var http = require('http')
      var fs = require('fs')
  3.第三方模块
       凡是第三方模块都必须通过 npm 来下载
       使用的时候就可以通过 require('包名') 的方式来进行加载才可以使用
       如: var template = require('art-template')
       整个加载过程中:
        先找到当前文件所处目录中的 node_modules 目录
        node_modules/art-template
        node_modules/art-template/
        node_modules/art-template/package.json
        node_modules/art-template/package.json 文件中的 main 属性
        main 属性中就记录了 art-template 的入口模块
        如果 package.json文件不存在或者main指定的入口模块是也没有,自动找该目录下的 index.js,index.js 是作为一个默认备选项
        如果以上所有任何一个条件都不成立,进入上一级目录找 node_modules
        按照这个规则依次往上找,直到磁盘根目录还找不到,最后报错:Can not find moudle xxx
        一个项目有且仅有一个 node_modules 而且是存放到项目的根目录
- 优先从缓存加载:再次加载某个模块,不会执行里面的代码,但可以从缓存中拿到其中的接口对象,这样可以避免重复加载,提高模块加载效率

Node.js EventEmitter

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。

events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

//EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。
//event.js 文件
var EventEmitter = require('events').EventEmitter; 
var event = new EventEmitter(); 
event.on('some_event', function() { 
    console.log('some_event 事件触发'); 
}); 
setTimeout(function() { 
    event.emit('some_event'); 
}, 1000); 
// ---方法
// 1.addListener(event, listener) 为指定事件添加一个监听器到监听器数组的尾部
// 2.on(event, listener) 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
// 3.once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
// 4.removeListener(event, listener) 移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称
// 5.removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
// 6.setMaxListeners(n) 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于改变监听器的默认限制的数量。
// 7.listeners(event) 返回指定事件的监听器数组。
// 8.emit(event, [arg1], [arg2], [...]) 按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。

// ---类方法 获取事件的监听器数量  events.emitter.listenerCount(eventName);

// ---事件 newListener    removeListener 从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。

Stream(流)

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。
Node.js,Stream 有四种流类型:

  1. Readable - 可读操作。
  2. Writable - 可写操作。
  3. Duplex - 可读可写操作.
  4. Transform - 操作被写入数据,然后读出结果。

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

  1. data - 当有数据可读时触发。
  2. end - 没有更多的数据可读时触发。
  3. error - 在接收和写入过程中发生错误时触发。
  4. finish - 所有数据已被写入到底层系统时触发。
var data = '';
var fs = require('fs');
// 创建可读流
var readStream = fs.createReadStream('input.txt');
// 设置编码为 utf8。
readStream.setEncoding('UTF8');
// 处理流事件 --> data, end, and error
readStream.on('data', function(chunk) {
    data += chunk;
});
readStream.on('end', function() {
    console.log(data + '流 文件读取');
});
readStream.on('error', function(err) {
    console.log(err.stack);
});
console.log('流 文件读取完毕');

var outputData = '流 写入文件-----';
var writeStream = fs.createWriteStream('output.txt');
writeStream.write(outputData, 'UTF8');
writeStream.end();
writeStream.on('finish', function() {
    console.log('写入完成');
});
writeStream.on('error', function(err) {
    console.log(err.stack);
});
console.log('流 文件写入完毕');

管道流 管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。

// 管道读写操作
// 创建一个可读流
var fs = require('fs');
var readerStream = fs.createReadStream('input.txt');
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
var writerStream = fs.createWriteStream('pipeStream.txt');
readerStream.pipe(writerStream);

链式流 链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。

var fs = require('fs');
var zlib = require('zlib');
fs.createReadStream('input.txt').pipe(zlib.createGzip()).pipe(fs.createWriteStream('input.txt.gz'));
console.log('文件压缩完成');
fs.createReadStream('input.txt.gz').pipe(zlib.createGunzip()).pipe(fs.createWriteStream('input.txt'));
console.log('文件解压完成');

你可能感兴趣的:(node.js,node.js,后端)