Node的基本使用手册

基础知识补充

CMD基本知识

命令行窗口(小黑屏)、CMD窗口、终端、shell
开始菜单 --> 运行 --> CMD --> 回车
Win + R --> CMD --> 回车

命令 功能
dir 列出当前目录下的所有文件
cd 目录名 进入到指定的目录
md 目录名 创建一个文件夹
rd 目录名 删除一个文件夹

目录

.表示当前目录
..表示上一级目录

环境变量(window系统中的变量 )

​ PATH :C:\Program Files\Microsoft SQL Server\130\Tools\Binn\

当我们在命令行窗口打开一个文件,或调用一个程序时,系统会首先在当前目录下寻找文件程序,如果找到了则直接打开
如果没有找到则会依次到环境变量path的路径中寻找,直到找到为止如果没找到则报错类似于作用域链
所以我们可以将一些经常需要访问的程序和文件的路径添加到path中,这样我们就可以在任意位置来访问这些文件和程序了

进程和线程

进程: 进程负责为程序的运行提供必备的环境
进程就相当于工厂中的车间

线程: 线程是计算机中最小的计算单位,线程负责执行进程中的程序
线程就相当于工厂中的工人

单线程:JS是单线程,在执行JS代码的时候网页是停止渲染的。

多线程:主流的语言,如Java

Node.js 概念

Node.js是一个能够在服务器端运行JavaScript的开放源代码、跨平台JavaScript运行环境。

Node采用Google开发的V8引擎运行js代码,使用事件驱动、非阻塞和异步I/O模型等技术来提高性能,可优化应用程序的传输量和规模。

Node大部分基本模块都用JavaScript编写。在Node出现之前,JS通常作为客户端程序设计语言使用,以JS写出的程序常在用户的浏览器上运行。

目前,Node已被IBM、Microsoft、Yahoo!、Walmart、Groupon、SAP、 LinkedIn、Rakuten、PayPal、VoxerGoDaddy等企业采用。

Node主要用于编写像Web服务器一样的网络应用,这和PHP和Python是类似的。但是Node与其他语言最大的不同之处在于,PHP等语言是阻塞的而Node是非阻塞的。

Node是事件驱动的。开发者可以在不使用线程的情况下开发出一个能够承载高并发的服务器。其他服务器端语言难以开发高并发应用,而且即使开发出来,性能也不尽人意,Node正是在这个前提下被创造出来。

Node把JS的易学易用和Unix网络编程的强大结合到了一起。

Node.js允许通过JS和一系列模块来编写服务器端应用和网络相关的应用。

核心模块包括文件系统I/O、网络(HTTP、TCP、UDP、DNS、TLS/SSL等)、二进制数据流、加密算法、数据流等等。Node模块的API形式简单,降低了编程的复杂度。

使用框架可以加速开发。常用的框架有Express.js、Socket.IO和Connect等。Node.js的程序可以在Microsoft Windows、Linux、Unix、Mac OS X等服务器上运行。

Node.js也可以使用CoffeeScript、TypeScript、Dart语言,以及其他能够编译成JavaScript的语言编程。

Node的用途

• Web服务API,比如REST
• 实时多人游戏
• 后端的Web服务,例如跨域、服务器端的请求
• 基于Web的应用
• 多客户端的通信,如即时通信

I/O (Input/Output)

I/O操作指的是对磁盘的读写操作

Node语言是JS模型

Node是对ES标准一个实现,Node也是一个JS引擎,通过Node可以使js代码在服务器端执行
Node仅仅对ES标准进行了实现,所以在Node中不包含DOM 和 BOM
Node编写都是单线程的服务器
Node处理请求时是单线程,但是在后台拥有一个I/O线程池

Node中使用的内建对象

String Number Boolean Math Date RegExp Function Object Array
BOMDOM不能使用,但是可以使用 console 也可以使用定时器setTimeout() setInterval()

ECMAScript标准的缺陷

没有模块系统,标准库较少,没有标准接口,缺乏管理系统

模块化(require)

如果程序设计的规模达到了一定程度,则必须对其进行模块化。
模块化可以有多种形式,但至少应该提供能够将代码分割为多个源文件的机制。
CommonJS 的模块功能可以帮我们解决该问题。
CommonJS规范的提出,主要是为了弥补当前JavaScript没有模块化标准的缺陷。
CommonJS规范为JS指定了一个美好的愿景,希望JS能够在任何地方运行。
CommonJS对模块的定义十分简单: – 模块引用 – 模块定义 – 模块标识

模块标识

模块标识其实就是模块的名字,也就是传递给require()方法的参数,它必须是符合驼峰命名法的字符串,或者是以.、…开头的相对路径、或者绝对路径。
模块的定义十分简单,接口也十分简洁。每个模块具有独立的空间,它们互不干扰,在引用时也显得干净利落。

核心模块

由node引擎提供的模块,核心模块的标识就是,模块的名字,里面封装着内置方法
var fs = require("fs");

文件模块

由用户自己创建的模块,文件模块的标识就是文件的路径(绝对路径,相对路径)
相对路径使用.或..开头

模块定义

在运行环境中,提供了exports对象用于导出当前模块的方法或者变量,并且它是唯一的导出的出口。
在模块中还存在一个module对象,它代表模块自身,而exports是module的属性。
在Node中一个文件就是一个模块。

模块引用

在规范中,定义了require()方法,这个方法接手模块标识,以此将一个模块引入到当前运行环境中。
模块引用的示例代码:require('模块').math

模块实例

Node中虽然使用的是CommonJS规范,但是其自身也对规范做了一些取舍。
在Node中引入模块,需要经历3个步骤 – 路径分析 – 文件定位 – 编译执行
在Node中,模块分为三类:一类是底层由C++编写的内建模块,一类是Node提供的核心模块;还有一类是用户编写的模块,称为文件模块。

// 扩展模块
var math = require("./math");
// 核心模块
var fs = require("fs");
console.log(math.add(123,456));
console.log(fs);

我们可以通过 exports 来向外部暴露变量和方法只需要将需要暴露给外部的变量或方法设置为exports的属性即可

//向外部暴露属性或方法
exports.x = "我是02.module.js中的x";
exports.y = "我是y";
exports.fn = function () {};

全局对象(global)

在全局中创建的变量和函数都会作为global的属性和方法保存

实际上模块中的代码都是包装在一个函数中执行的,并且在函数执行时,同时传递进了5个实参

// 当node在执行模块中的代码时,它会首先在代码的最顶部,添加如下代码
function (exports, require, module, __filename, __dirname) {
  exports = module.exports
  // ....
} // 在代码的最底部,添加}代码
  • exports:该对象用来将变量或函数暴露到外部
  • require:函数,用来引入外部的模块
  • module:代表的是当前模块本身
  • __filename:当前模块的完整路径
    C:\Users\lilichao\WebstormProjects\class0705\01.node\04.module.js
  • __dirname:当前模块所在文件夹的完整路径
    当前模块所在文件夹的完整路径
console.log(global.a);
console.log(arguments.callee + "");//- 这个属性保存的是当前执行的函数对象
console.log(arguments.length);
console.log(exports);
console.log(module.exports == exports);
console.log(__dirname);

exports 和 module.exports

通过exports只能使用.的方式来向外暴露内部变量
exports.xxx = xxx
而module.exports既可以通过.的形式,也可以直接赋值
module.exports.xxx = xxxx
module.exports = {}
这样有个好处,就是引入的时候可以直接使用返回的exports值,不用通过.xxx获取

// 模块一定义
module.exports = {}
// 模块二引入
var demo = require("模块一"); //demo = {}

模块包(package)

CommonJS的包规范允许我们将一组相关的模块组合到一起,形成一组完整的工具。
CommonJS的包规范由包结构和包描述文件两个部分组成。

引入包和使用包

var math = require('math')
console.log(math.add(123,456))

包结构: 组织包中的各种文件,实际上就是一个压缩文件,解压以后还原为目录。符合规范的目录,应该包含如下文件:
– package.json 描述文件 – bin 可执行二进制文件 – lib js代码 – doc 文档 – test 单元测试

包描述文件: 描述包的相关信息,用于表达非代码相关的信息,它是一个JSON格式的文件 – package.json,位于包的根目录下,是包的重要组成部分。
package.json中的字段: name、description、version、keywords、maintainers、contributors、bugs、licenses、repositories、dependencies、homepage、os、cpu、engine、builtin、directories、implements、scripts、author、bin、main、devDependencies。

缓冲区(Buffer)

从结构上看Buffer非常像一个数组,它的元素为16进制的两位数。一个元素就表示内存中的一个字节。
JS数组性能比其他语言的数组差,但Buffer中的内存不是通过JavaScript分配的,而是在底层通过C++申请的。也就是我们可以直接通过Buffer来创建内存中的空间。

  • Buffer的结构和数组很像,操作的方法也和数组类似
  • 数组中不能存储二进制的文件,而buffer就是专门用来存储二进制数据
  • 使用buffer不需要引入模块,直接使用即可
  • 在buffer中存储的都是二进制数据,但是在显示时都是以16进制的形式显示,buffer中每一个元素的范围是从
    00 - ff 0 - 255,00000000 - 11111111

    // 计算机 一个0 或一个1 我们称为1位(bit)
        8bit = 1byte(字节)
        1024byte = 1kb
        1024kb = 1mb
        1024mb = 1gb
        1024gb = 1tb
  • buffer中的一个元素,占用内存的一个字节
  • Buffer的大小一旦确定,则不能修改,Buffer实际上是对底层内存的直接操作

BufferApi 方法

Buffer.from(str) 将一个字符串转换为buffer
Buffer.alloc(size) 创建一个指定大小的Buffer
Buffer.alloUnsafe(size) 创建一个指定大小的Buffer,但是可能包含敏感数据
buf.toString() 将缓冲区中的数据转换为字符串

BufferApi实例

var str = "Hello 尚硅谷";
//将一个字符串保存到buffer中
var buf = Buffer.from(str);
//创建一个10个字节的buffer
var buf2 = Buffer.alloc(10);
//通过索引,来操作buf中的元素
buf2[0] = 88;
buf2[1] = 255;
buf2[2] = 0xaa;
buf2[3] = 256; //超过二进制8位就会舍掉前面多的
buf2[10] = 15; //一旦确定长度,不可修改
console.log(buf2) // 
//只要数字在控制台或页面中输出一定是10进制
console.log(buf2[2]) // 170
//转换为16进制的字符串 
console.log(buf2[2].toString(16) // aa

文件系统(fs)

在Node中,与文件系统的交互是非常重要的,服务器的本质就将本地的文件发送给远程的客户端。Node通过fs模块来和文件系统进行交互。该模块提供了一些标准文件访问API来打开、读取、写入文件,以及与其交互。要使用fs模块,首先需要对其进行加载。

const fs = require("fs");

同步和异步调用

fs模块中所有的操作都有两种形式可供选择同步和异步。
同步文件系统会阻塞程序的执行,也就是除非操作完毕,否则不会向下执行代码。
异步文件系统不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回。

打开&关闭文件

异步打开文件

fs.open(path, flags[, mode], callback(fd))
fs.openSync(path, flags[, mode])

同步关闭文件

fs.close(fd, callback)
fs.closeSync(fd)

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

flags 参数列表

Flag 描述
r 以读取模式打开文件。如果文件不存在抛出异常。
r+ 以读写模式打开文件。如果文件不存在抛出异常。
rs 以同步的方式读取文件。
rs+ 以同步的方式读取和写入文件。
w 以写入模式打开文件,如果文件不存在则创建。
wx 类似 'w',但是如果文件路径存在,则文件写入失败
w+ 以读写模式打开文件,如果文件不存在则创建。
wx+ 类似 'w+', 但是如果文件路径存在,则文件读写失败。
a 以追加模式打开文件,如果文件不存在则创建。
ax 类似 'a', 但是如果文件路径存在,则文件追加失败。
a+ 以读取追加模式打开文件,如果文件不存在则创建。
ax+ 类似 'a+', 但是如果文件路径存在,则文件读取追加失败。

open实例

// 异步打开文件
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {if (!err) {
       console.log("文件打开成功!");     
}});
$ node file.js 
准备打开文件!
文件打开成功!

写入文件

fs中提供了四种不同的方式将数据写入文件
– 简单文件写入 – 同步文件写入 – 异步文件写入 – 流式文件写入

简单文件写入

fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])

  • file - 文件的路径。
  • data - 被写入的内容,可以是String或Buffer。
  • options - 对象,包含属性(encoding、mode、flag)
  • callback - 回调函数,带有两个参数如:callback(err, fd)。
fs.writeFile('hello.txt','这是通过writeFile写入的内容',{flag:'w'},(err)=>{
    if(!err){console.log('写入成功~~~')}else{console.log('失败')}
})

同步文件写入

fs.writeSync(fd, buffer, offset, length[, position])
fs.writeSync(fd, data[, position[, encoding]])
要完成同步写入文件,先需要通过openSync()打开文件来获取一个文件描述符,然后在通过writeSync()写入文件。

  • fd - 文件描述符,通过openSync()获取
  • data - 被写入的内容,可以是String或Buffer。
  • offset - buffer写入的偏移量
  • length - 写入的长度
  • position - 写入的起始位置
  • encoding - 写入编码
var fsTxt = fs.openSync('hello.txt','w');//打开文件
//文件编号为3
console.log(fsTxt)
//向文件中写入内容
fs.writeSync(fsTxt,'今天天气真不错~~~~',2,'utf-8');
//关闭文件
fs.closeSync(fsTxt)

异步文件写入

fs.write(fd, buffer, offset, length[, position], callback)
fs.write(fd, data[, position[, encoding]], callback)
要使用异步写入文件,先需要通过open()打开文件,然后在回调函数中通过write()写入。

  • fd - 文件描述符,通过openSync()获取
  • data - 被写入的内容,可以是String或Buffer。
  • offset - buffer写入的偏移量
  • length - 写入的长度
  • position - 写入的起始位置
  • encoding - 写入编码
  • callback - 回调函数
//打开文件
var fd = fs.open('hello.txt','w',function(err,fd){if(!err){//判断是否出错
        //如果没有出错,则对文件进入写入
        fs.write(fd,'这是异步写入的内容',function(err){ //文件写入
            if(!err){console.log('写入成功')}
            fs.close(fd,function(){console.log('文件已关闭')})//关闭文件
            
        })
}else{console.log(err)}})//如果出错则弹出错误

流式文件写入

往一个文件中写入大量数据时,最好的方法之一是使用流。
若要将数据异步传送到文件,首需要使用以下语法创建一个Writable对象:
fs.createWriteStream(path[, options])

  • path - 文件路径
  • options - 对象,包含属性(encoding、mode、flag)

一旦你打开了Writable文件流,就可以使用write()方法来写入它,写入完成后,在调用end()方法来 关闭流。

var ws = fs.createWriteStream('hello.txt') // 开启hello.txt的文件流

可以通过监听流的open和close事件来监听流的打开和关闭
on(事件字符串,回调函数) 为对象绑定一个事件
once(事件字符串,回调函数) 为对象绑定一次性的事件

ws.once('open',()=>{console.log('流打开了~~~~')})
ws.once('close',()=>{console.log('流关闭了~~~~')})

调用文件流write方法写入

ws.write('通过可写流写入文件的内容')
ws.write('今天天气真不错')
ws.write('锄禾日当午')
ws.write('红掌拨清清')
ws.write('清清真漂亮')

调用文件流end方法关闭流

ws.end();

读取文件

fs中提供了四种读取文件的方式
– 简单文件读取 – 同步文件读取 – 异步文件读取 – 流式文件读取

简单文件读取

fs.readFile(file[, options], callback)
fs.readFileSync(file[, options])

  • file - 文件的路径。
  • options - 对象,包含属性(encoding、mode、flag)
  • callback - 回调函数,带有两个参数如:callback(err, fd)。
fs.readFile('派大星.jpg',(err,data)=>{if(!err){ // 读取文件
    fs.writeFile('成了.jpg',data,(err)=>{console.log(!err ?'成功':'失败')})// 写入文件
}else{console.log('读取出错')}})

同步文件读取

• fs.readSync(fd, buffer, offset, length, position)

  • fd - 文件描述符,通过openSync()获取
  • buffer - 读取文件的缓冲区
  • offset - buffer的开始写入的位置
  • length - 要读取的字节数
  • position - 写入的起始位置
fs.open('123.txt' , 'r' , function (err,fd){ // 读取文件
if(err){console.error(err);return;} 
 var buf = new Buffer(8);
 var readfile = fs.readSync(fd, buf, 0, 8, null); // 返回读取的字节数
})

异步文件读取

fs.read(fd, buffer, offset, length, position, callback)

  • fd - 文件描述符,通过openSync()获取
  • buffer - 读取文件的缓冲区
  • offset - buffer写入的偏移量
  • length - 要读取的字节数
  • position - 开始读取文件的位置
  • callback - 回调函数 参数err , bytesRead , buffer
fs.open('123.txt' , 'r' , function (err,fd){ // 读取文件
if(err){console.error(err);return;} 
 var buf = new Buffer(8);
 var readfile = fs.read(fd, buf, 0, 8, 0,(readfile)=>{
   console.log(readfile) // 返回读取的字节数
 }); 
})

流式文件读取

从一个文件中读取大量的数据时,最好的方法之一就是流式读取,这样将把一个文件作为Readable流 的形式打开。
要从异步从文件传输数据,首先需要通过以下语法创建一个Readable流对象:fs.createReadStream(path[, options])

  • path - 文件路径
  • options - {encoding:"",mode:"",flag:""}

当你打开Readable文件流以后,可以通过readable事件和read()请求,或通过data事件处理程序 轻松地从它读出。

var rs = fs.createReadStream('test.mp4')
var ws = fs.createWriteStream('HuoGuoP.mp4')
//监听流的开启和关闭
rs.once('open',()=>{console.log('读取流打开了~~~~')})
//数据读取完毕,关闭可写流
rs.once('close',()=>{console.log('读取流关闭了~~~~');ws.end()})
//如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件
rs.on('data',(data)=>{ //data事件绑定完毕,它会自动开始读取数据
    ws.write(data) // 写入文件
})

pipe()方法将可读流内容直接输出到可写流中,并且自动关闭

var rs = fs.createReadStream('test.mp4')
var ws = fs.createWriteStream('HuoGuoP.mp4')
rs.pipe(ws)

其他操作

# 验证路径是否存在
    - fs.exists(path, callback)
    - fs.existsSync(path)
# 获取文件信息
    - fs.stat(path, callback)
    - fs.statSync(path)
# 删除文件
    - fs.unlink(path, callback)
    - fs.unlinkSync(path)
# 列出文件
    - fs.readdir(path[, options], callback)
    - fs.readdirSync(path[, options])
# 截断文件
    - fs.truncate(path, len, callback)
    - fs.truncateSync(path, len)
# 建立目录
    - fs.mkdir(path[, mode], callback)
    - fs.mkdirSync(path[, mode])
# 删除目录
    - fs.rmdir(path, callback)
    - fs.rmdirSync(path)
# 重命名文件/文件夹
    - fs.rename(oldPath, newPath, callback)
    - fs.renameSync(oldPath, newPath)
# 监视文件/文件夹更改写入
    - fs.watch(path, [, options], callback) => 性能较好
    - fs.watchFile(path, [, options], callback)

路径系统 (path)

path 模块提供了一些用于处理文件与目录的路径的实用工具。

# 提取文件扩展名
    - path.extname(path)
# 拼接路径字符串
    - path.join([...paths])
    - path.resolve([...paths])
# 解析路径完整信息
    - path.parse(path)

操作系统 (os)

os 模块提供了与操作系统相关的实用方法和属性。

# 查看CPU架构
    - os.arch()
# CPU内核信息/状态
    - os.cpus()
# 系统内存总量
    - os.totalmem()
# 系统空闲内存量
    - os.freemem()
# 操作系统名称
    - os.type()
# 系统正常运行时间
    - os.uptime()
# 操作系统主机名
    - os.hostname()

地址解析系统 (url)

const URL_Info = new URL('https://translate.google.cn/?search=6#view=home');
console.log(URL_Info)
// ===>
URL {
  href: 'https://translate.google.cn/?#view=home/search=6',     // 地址
  origin: 'https://translate.google.cn',     // 根路径地址
  protocol: 'https:',        // 请求协议
  username: '',        // 邮箱用户名部分
  password: '',        // 邮箱密码部分
  host: 'translate.google.cn',    // URL主机部分(包含端口)
  hostname: 'translate.google.cn',        // URL主机部分(不包含端口)
  port: '',        // URL端口部分
  pathname: '/',    // URL路径部分
  search: '?search=6',    // URL查询字符串部分
  searchParams: URLSearchParams { 'search' => '6' },    // // URL查询字符串组成的对象
  hash: '#view=home'    // 田子码符串部分
}

const url = require('url')
url.parse('/search?title=helloWord')
// ===>
/search?title=helloWord Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?title=helloWord',
  query: 'title=helloWord',
  pathname: '/search',
  path: '/search?title=helloWord',
  href: '/search?title=helloWord'
}

服务器系统 (http)

nodejs提供了http模块,自身就可以用来构建服务器,而且http模块是由C++实现的,性能可靠。

开启本地服务器的基本流程

// 引入本地服务器API
const http = require('http')
// 创建服务器实例
const server = http.createServer()
// 绑定接收请求事件
server.on('request', (req, res)=>{
  // 请求路径 (请求路径永远以 "/" 开头)
  console.log(req.url)
  // 根据请求路径处理事件, 返回对应数据给用户
  res.end('hello world')
})

// 绑定监听端口号, 开启服务器
server.listen(3000, ()=> console.log('服务器开启成功, 端口号为:3000'))

req (请求) 携带的常用参数

const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
    // 请求路径 (请求路径永远以 "/" 开头)
    console.log(req.url)
    // 请求头对象
    console.log(req.headers)
    // 请求类型
    console.log(req.method)

    res.end('hello world')
})
server.listen(3000, () => console.log('服务器开启成功, 端口号为:3000'))

req (响应) 携带的常用参数

const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
    // 设置状态码和响应头
    res.writeHead(200, {'Content-Type':'text/plain'})
    // 设置响应头
    res.setHeader('Content-Type', 'text/plain')
    // 写入内容
    res.write(fileData)
    // 结束响应
    res.end('hello word')
})
server.listen(3000, () => console.log('服务器开启成功, 端口号为:3000'))

你可能感兴趣的