全方面认识Node.js

node基础知识

Posted by AzirKxs on 2022-10-10
Estimated Reading Time 11 Minutes
Words 2.9k In Total
Viewed Times

全方面认识Node.js

初识node.js

为什么JavaScript可以在浏览器中运行?

因为浏览器有对应的JavaScript解析引擎,例如Chrome浏览器的V8引擎

为什么JavaScript可以操纵DOM?

因为浏览器提供了对应的API(DOM.BOM,Ajax,canvas等),如果宿主环境没有DOM API则不能操控DOM

JavaScript可以执行后端代码吗?

JavaScript只是一种语言,代码的执行离不开运行环境,如果执行在浏览器中就可以做前端开发,如果执行在node.js环境下就可以做后端开发

什么是node.js?

node.js是一个基于Chrome V8引擎的JavaScript运行环境,node.js无法调用DOM和BOM等浏览器内置API

node.js运行环境

Node.js架构

node.js架构

我们编写的JavaScript代码会经过V8引擎,再通过Node.js 的bindings,将任务放到libuv事件循环中

  • Node Bindings 是沟通 JS 和 C++ 的桥梁,封装 V8 引擎 和 Libuv 的细节,向上层提供基础 API 服务。

  • libuv使用C语言编写,提供了事件循环,文件系统读写,网络IO,线程池等

node.js应用

  • 目前前端开发的库都是以node包形式进行管理,npm,yarn,pnpm工具成为前端开发使用最多的工具
  • web服务器开发,中间件,代理服务器
  • 借助node.js完成前后端渲染的同构应用
  • 编写脚本的工具
  • 使用Electron开发桌面应用程序

node中的输入和输出

输出

1
2
3
console.log(); //在控制台打印
console.clear();//清空控制台
console.trace();//打印执行的调用栈

输入

在执行node.js文件时传入num=20并通过process.argv获取

1
node ./node01.js num1 = 20

借助process对象

1
2
3
4
5
6
7
console.log(process.argv);
/* [
'C:\\Program Files\\nodejs\\node.exe',
'D:\\2022\\node学习\\node模块化\\node01.js',
'num1=20'
] */
console.log(process.argv[2]); //20

Node模块化

什么是模块化?

模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程,对于整个系统来说,模块是可组合,分解和更换的单元

模块化的好处

  • 提高代码的复用性
  • 提高了代码的可维护性
  • 实现了按需加载

Node.js 中根据模块来源不同,将模块分为了三大类,分别是:

  • 内置模块(fs,path,http等)
  • 自定义模块(用户创建的js文件)
  • 第三方模块

Node.js遵循CommonJS规范,更多模块化相关的内容,详情请看:前端的模块化是什么? - AzirKxs的博客 | 分享与记录

全局对象

console对象

1
2
3
console.log(); //在控制台打印
console.clear();//清空控制台
console.trace();//打印执行的调用栈

__dirname

获取当前文件所在的路径

1
console.log(__dirname);//D:\2022\node学习\node模块化

__filename

获取当前文件所在的路径以及文件名

1
console.log(__filename);//D:\2022\node学习\node模块化\node01.js

require对象

require用于加载模块

1
const 模块 = require('模块名称或路径');

使用require加载模块时,会执行被加载模块中的代码

模块作用域:在自定义模块中定义的变量,方法等,未经暴露只能在当前模块内被访问

module对象

每个 .js自定义模块中都有一个module对象,里面存储了和当前模块有关的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Module {
id: '.',
path: 'D:\\2022\\node学习\\node模块化',
exports: {},
filename: 'D:\\2022\\node学习\\node模块化\\node01.js',
loaded: false,
children: [],
paths: [
'D:\\2022\\node学习\\node模块化\\node_modules',
'D:\\2022\\node学习\\node_modules',
'D:\\2022\\node_modules',
'D:\\node_modules'
]
}

向外暴露模块作用域中的成员

1
module.exports.要暴露的对象名 = 要暴露的内容

使用require()方法导入模块时,导入的结果永远以module.exports指向的对象为准

exports对象

默认情况下,module.exports和exports指向的同一对象,为了简写module.exports的写法,node提供了exports对象

process对象

存储进程相关的信息,比如node的运行环境,参数信息等

定时器函数

setInterval(callback,delay)每过delay毫秒执行一次callback

setTimeout(callback,delay)过delay毫秒后执行一次

setImmediat(callback)立即执行

process.nextTick(callback)添加到下一次的tick队列中

global对象

全局对象,类似于浏览器中的window对象,process,console,定时器函数等都有被放到global中

Node模块的具体作用细节

模块导入的本质

在person.js中向exports上添加属性:

1
2
exports.name = 'azirkxs';
exports.age = '21';

从另外一个文件中导入:

1
const person = require('./person');
  • require通过各种查找方式,最终找到了exports这个对象
  • 将exports对象赋值给了person变量
  • person变量就是exports对象

Node中模块的导入本质上就是一个引用赋值,指向的是同一个对象

导出的真正实现者

node中存在exports与module.exports,二者指向的是同一个对象,但是,我们一般都使用Moudel.exports导出。

CommonJS是没有module.exports的概念的,为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Moudle的实例,也就是说每个.js模块都会被抽象成一个module对象,module对象的exports属性是对exports对象的一个引用,因此在node中,module才是导出的真正实现者。导入的结果永远以module.exports指向的对象为准。

require的细节

1
const x = require(X);

情况1:X是一个node的核心模块,直接导入

情况2:X是一个路径,以./,…/开头

1.如果没有后缀名,会按照如下顺序

  • 直接查找文件X
  • 查找文件X.js
  • 查找文件X.json,
  • 查找文件X.node

2.如果没有找到对应的文件,会将X作为一个目录

  • 查找X/index.js
  • 查找X/index.json
  • 查找X/index.node

如果还没有找到,则报错not found

情况3:非路径格式,且X并不是一个核心模块

依次向上层的node_modules文件中查找

模块的加载过程

1.模块在第一次被引入时,模块中的js代码会被执行一次

2.模块被多次引入时,会缓存,最终只加载一次(执行后将module对象中的loaded属性值改为true表示已加载)

3.如果有结构复杂的循环引入,则会根据深度优先算法执行

fs文件系统模块

fs模块由node.js官方提供,提供了一些列操作文件的属性和方法

引入fs模块

1
const fs = require('fs');

读写文件

1
2
fs.readfile(path,format,function(err,result)) //读:路径,编码格式,回调函数
fs.writefile(path,content,function(err))//写:路径,内容,回调函数,如果文件不存在会自动创建文件,但无法自动创建路径(文件夹),新写入的内容会覆盖旧内容

如果读取成功,err为null,result为文件读取内容

如果读取成功,err为错误对象,result为undefined

  • 一个读取文件示例
1
2
3
4
5
6
7
8
const fs = require('fs');

fs.readFile('./files.txt', function(err, data) {
if(err){
return console.log("读取文件失败,错误信息"+err);
}
return console.log("读取文件成功"+data);
})
  • 写入文件示例
1
2
3
4
5
6
7
8
const fs = require('fs');

fs.writeFile('./files.txt','将此内容写入文件!', function(err) {
if(err){
return console.log("写入文件失败,错误信息"+err.message);
}
console.log("写入文件成功");
})

fs的路径动态拼接问题

node在执行JavaScript代码时,会根据执行node命令时所处的路径去动态的拼接成完整的路径

1
2
3
4
5
6
7
8
const fs = require('fs');

fs.writeFile('./files.txt','将此内容写入文件!', function(err) {
if(err){
return console.log("写入文件失败,错误信息"+err.message);
}
console.log("写入文件成功");
})
1
PS D:\2022\node学习\fs模块\成绩处理> node ./index.js #正常执行
1
PS D:\2022\node学习\fs模块> node 成绩处理/index.js #出错

如果执行这种命令就会出现路径拼接错误的问题从而导致找不到文件,实际上拼接的是当前执行node所在的目录路径D:\2022\node学习\fs模块\成绩处理+writeFile提供的相对路径files.txt

1
D:\2022\node学习\fs模块\files.txt

跳过了一层文件,导致没有找到files.txt,为了解决这个问题,我们需要为writeFile或者readFile提供完整的磁盘路径

__dirname:表示当前文件的所处目录

1
2
3
4
5
6
7
8
const fs = require('fs');

fs.writeFile(__dirname+'/files.txt','将此内容写入文件!', function(err) {
if(err){
return console.log("写入文件失败,错误信息"+err.message);
}
console.log("写入文件成功");
})

path路径模块

path模块由node.js官方提供,提供了一些列处理路径的属性和方法

引入path模块

1
const path = require('path');

path.join()

path.join() 用来拼接多个路径片段

1
const pathStr = path.join('a/b','c/d','../','./d'); //a/b/d

path.basename()

path.basename()用来获取路径中的最后一部分,通常用来获取路径中的文件名

1
2
3
4
5
6
7
8
const path = require('path');
const pathUrl = path.join(__dirname, 'file.txt');

const fullname = path.basename(pathUrl);
const filename = path.basename(pathUrl,'.txt')

console.log(fullname);//file.txt
console.log(filename);//file

path.extname()

path.extname()用来获取文件扩展名

1
2
const ext = path.extname(pathUrl);
console.log(ext);//.txt

http模块

http模块是由node.js官方提供的,用来创建web服务器的模块,不需要使用第三方的web服务器软件,可以使用node手写一个服务器从而对外提供web服务

创建基本的web服务器

  • 导入模块
1
const http = require('http');
  • 创建实例对象
1
const server = http.creatServer();
  • 为服务器绑定request事件,监听服务器请求
1
2
3
server.on('request',function(req,res){
console.log("someone is visiting our web server");
})
  • 启动服务器
1
2
3
server.listen(8080,function(){
console.log('server running at http://127.0.0.1:8080');
})

req对象

只要服务端收到了客户端请求,就会通过server.on为服务器绑定request事件处理函数,想要在事件处理函数中访问客户端相关的数据或属性,就可以使用req对象

  • 访问客户端相关的数据或属性
1
2
req.url //客户端请求的url地址
req.method //客户端的请求类型

res响应对象

在request事件处理函数中,如果想访问与服务器相关的数据或属性,就可以使用res对象

1
res.end(content);//向客户端发送指定的内容并结束此次请求的处理过程

使用res.end()方法发送中文内容的时候,会出现乱码的问题,需要手动设置编码格式

1
res.setHeader('Content-Type', 'text/html;charset=utf-8');

示例:创建服务器并返回响应内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const http = require('http');

const server = http.createServer();

server.on('request', (req, res) => {
console.log("someone is visiting our web server");
console.log(req.url);
const url = req.url;
let content = "<h1>404 not found</h1>";
if(url === '/'|| url ==='/index.html') {
content = "<h1>首页</h1>";
}else if(url === '/about.html') {
content ='<h1>关于</h1>'
}
console.log(req.method);
console.log(req.headers);
res.setHeader('Content-Type', 'text/html;charset=utf-8');
res.end(content);
})

server.listen(8080,function(){
console.log('server running at http://127.0.0.1:8080');
})

如果这篇文章对你有帮助,可以bilibili关注一波 ~ !此外,如果你觉得本人的文章侵犯了你的著作权,请联系我删除~谢谢!