1. node.js是什么
是一个app, 可以运行js的代码的一个程序
使用一套命令来操作js文件.
并不是一种编程语言.
准确的说, node.js是一个js代码的运行环境, 可以使的js代码脱离浏览器运行, 实现在操作系统上运行js的代码的目的.
基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
2. node.js可以做什么
总所周知, 像go\java等可以构建一个后端的服务器程序, 但是go或者java一样要依赖于各自的环境, 如今的项目基本都是前后端分离的项目.
所以, node.js就是用来构建前端服务器的.
再者, 前端的框架也是基于node.js的.
- 构建server应用
- 开发工具类应用: 例如webpack, vite, babel
- 开发c端应用, 例如: vscode, figma, postman
3. 和浏览器的不同
node.js中, js的代码并不同调用到bom和dom相关的APi, 这个很好理解. 都不是一个运行环境, 怎么调的到.
在浏览器中, js的顶级对象是window, 但是在node的环境中, js的顶级对象是global.
4. node操作内存: Buffer
就是一个可以直接操作内存的, 长度固定, 不可以不改变的, 字节数组, 其实就是内存上的一段空间.
创建方式有三种:
// 1. alloc : 分配
let buf = Buffer.alloc(10);
// 2. alloc : 不安全的分配
let buf2 = Buffer.allocunsafe(10); // 并不会对该内存空间做一个清零的擦除动作, 也就是说, 有会初始脏数据
// 3. from
let buf3 = Buffer.from('hello'); // 会将一个字符串转换成buffer, 值得一提的是, 每一个字符就是先转换成依据万国码表对应的十六进制数字, 然后再转成二进制数字, 写进内存, 但是打印时, 又会打印成十六进制的数据
5. node操作硬盘:fs
全称是file system
即文件管理系统
是node.js的一个内置模块
这个用来实现和计算机的硬盘实现交互,封装了操作文件和文件夹的操作
// 导入fs模块
const fs = require('fs');
// 写入文件
// 参数1:file 文件路径
// 参数2:data 文件内容
// 参数3:options 可选参数
// 参数4:callback 回调函数
// fs.writeFile(file, data[, options], callback)
fs.writeFile('./test.txt', 'hello world', (err) => {
// err 写入失败:err就是错误对象,如果写入成功,err就是null
if (err){
console.log('文件写入失败');
}
console.log('文件已被保存');
});
// 那么事实上,data就是内存中的数据,通过这个api就能够写入到硬盘中了
5.1 同步和异步
事实上,以上的代码是异步的
也就是说,代码自上而下执行,到了这里之后,发现有写入磁盘的代码,node会新开一条线程单独的去执行写入的动作,并不会阻塞在这里,代码会继续向下执行。
那么怎么实现该线程同步的写入呢?
同步写入
fs.writeFileSync(file, data[, options])
这个方法的前三个参数是一样的,但是少了最后的一个回调函数
如果写入失败,会抛出异常,所以需要try catch来捕获异常
try {
fs.writeFileSync('./test_Sync.txt', 'hello world');
console.log('文件已被保存');
} catch (error) {
console.log('文件写入失败');
}
5.2 追加写入
第一种方法:appendFile
// 追加写入
fs.appendFile('./test.txt', 'hello world', (err) => {
if (err){
console.log('文件追加写入失败');
}
console.log('文件追加成功');
});
// 同步追加写入
try {
fs.appendFileSync('./test.txt', 'hello world');
console.log('文件追加成功');
} catch (error) {
console.log('文件追加写入失败');
}
第二种方法:
// 第二种追加写入的方法
// fs.writeFile
// fs.writeFileSync
// 这两个方法的第三个参数,都是可选参数,可以传入一个对象
// {flag: 'a'} a表示append,追加写入
fs.writeFile('./test.txt', 'hello world', {flag: 'a'}, (err) => {
if (err){
console.log('文件追加写入失败');
}
console.log('文件追加成功');
});
5.3 流式写入
本质就是创建一个写入流对象
指定一个url,也就是文件的路径,建立起一条通道。
这个通道在被销毁之前是可以复用的,随时可以写入数据。
// 流式写入
let wio = fs.createWriteStream("./test.txt", {flags: 'a'});
wio.write('hello world');
// 一定要关闭流
wio.end();
// 关闭之后,就不能再写入了,可以监听finish事件,来判断什么时候关闭流,并处理后续逻辑
wio.on('finish', () => {
console.log('文件写入成功');
});
5.4 文件写入的应用场景
文件写入的应用场景一句话来总结就是,将程序产生的数据保存到磁盘:
例如:文件的下载、安装软件、保存日志、视频录制等等
// 读取文件
// 文件异步读取
fs.readFile('./test.txt', (err, data) => {
if (err){
console.log('文件读取失败');
}
console.log(data.toString());
});
// 文件同步读取
try {
let data = fs.readFileSync('./test.txt');
console.log(data.length);
console.log(data.toString());
} catch (error) {
console.log('文件读取失败');
}
// 流式读取
let rio = fs.createReadStream('./test.txt');
// 监听data事件,当有数据流入的时候,就会触发data事件
rio.on('data', (data) => {
console.log(data.length); // 65536个字节,64kb
console.log(data.toString());
});
// 监听end事件,当数据读取完毕的时候,就会触发end事件
rio.on('end', () => {
console.log('文件读取完毕');
});
5.5 文件的移动和重命名
// 对文件进行重命名,和移动
fs.rename('./test.txt', './test1.txt', (err) => {
if (err){
console.log('重命名失败');
}
console.log('重命名成功');
});
fs.rename('./test1.txt', './test/test1.txt', (err) => {
if (err){
console.log('移动失败');
}
console.log('移动成功');
});
5.6 文件删除
// 删除文件
fs.unlink('./test/test1.txt', (err) => {
if (err){
console.log('删除失败');
}
console.log('删除成功');
});
5.7 文件是否存在
// 判断文件是否存在
fs.access('./test/test1.txt', (err) => {
if (err){
console.log('文件不存在');
}
console.log('文件存在');
});
5.8 创建文件夹
// 创建文件夹
fs.mkdir('./test', (err) => {
if (err){
console.log('文件夹创建失败');
}
console.log('文件夹创建成功');
});
// 递归创建文件夹
fs.mkdir('./test/test1/test2', {recursive: true}, (err) => {
if (err){
console.log('文件夹创建失败');
}
console.log('文件夹创建成功');
});
读取文件夹
fs.readdir('./test', (err, files) => { // 得到一个数组, 数组中的每一项就是文件夹中的文件名
if (err){
console.log('文件夹读取失败');
}
console.log(files);
});
5.9 删除文件夹
// 删除文件夹
fs.rmdir('./test', (err) => {
if (err){
console.log('文件夹删除失败');
}
console.log('文件夹删除成功');
});
// 递归删除
fs.rmdir('./test', {recursive: true}, (err) => {
if (err){
console.log('文件夹删除失败');
}
console.log('文件夹删除成功');
});
5.10 查看文件的状信息
// 查看文件的状态
fs.stat('./test.txt', (err, stats) => {
if (err){
console.log('文件状态查看失败');
}
console.log(stats);
console.log(stats.isFile()); // 是否是文件
console.log(stats.isDirectory()); // 是否是文件夹
console.log(stats.size); // 文件的大小
console.log(stats.birthtime); // 创建时间
console.log(stats.mtime); // 修改时间
});
6. node操作路径:path
// 导入path
const path = require('path');
// 使用绝对路径和相对路径,生成一条绝对路径
path.resolve(__dirname, './test.txt');
//注意: __dirname是node中的一个全局变量,表示当前文件所在的目录,是一个绝对路径,不是一个函数
//第二个参数必须是一个相对路径,不能是一个绝对路径, 也就是不能用/开头
//如果第二个参数是一个绝对路径,那么第一个参数就会失效
//如果第二个参数是一个相对路径,那么就会根据第一个参数生成一个绝对路径
// 获取路径的分隔符
path.sep; // windows: \ linux: / mac: / (只有windows中的路径分隔符是反斜杠, 其他都是正斜杠)
// 不同操作系统中的路径分隔符不一样, 但是在node中, 只要使用path.sep就可以获取到当前操作系统的路径分隔符
// 解析文件路径并返回文件对象
path.parse('./test/test.txt');
// 返回的是一个对象, 对象中包含了文件的信息
// 获取路径的基础名和目录名和扩展名
path.basename('./test/test.txt'); // test.txt
path.dirname('./test/test.txt'); // ./test
path.extname('./test/test.txt'); // .txt
7. node创建http服务
转换一个概念
以前是:
浏览器 和 后端服务器
现在是:
浏览器 和 前端服务器 和 后端服务器
// 导入http模块
var http = require('http');
// 创建服务对象
var server = http.createServer((resquest, response) => {
respponse.end('hello world'); // end方法: 设置响应体,并结束响应
});
// 监听端口
server.listen(3000, () => { // 第二个参数是一个回调函数,当服务器启动成功后,会自动调用回调函数
console.log('服务器启动成功了');
});
8. 模块化
介绍:
将一个复杂的程序文件依据一定的规则拆分成多个文件的过程就是模块化
其中拆分出来的每一个文件就是一个模块,模块内部的数据是私有的,但是模块可以暴露内部的数据给其他模块使用。
什么是模块化项目?
编码时是按照模块一个一个编码的,整个项目就是一个模块化的项目
模块化的好处
防止命名冲突、高复用性、高维护性
8.1 暴露数据的方法
// 定义一个打印1的方法
function my_len() {
console.log(1);
}
// 定义一个打印2的方法
function my_len2() {
console.log(2);
}
// 向外暴露这个方法
// module.exports = my_len;
// 向外暴露多个方法
module.exports = {
my_len,
my_len2
}
// 不要使用exports.xxx = xxx的方式向外暴露, 因为这种方式只能暴露一个方法
// exports.my_len = my_len;
// 为什么要使用module.exports = xxx的方式向外暴露?
// 因为nodejs中,每个模块都有一个module对象, 该对象中有一个exports属性, 该属性就是一个空对象{}
// 所以,如果直接使用exports.xxx = xxx的方式向外暴露, 那么module.exports就会被覆盖, 从而导致暴露失败
// 所以,如果想要向外暴露多个方法, 那么就需要使用module.exports = xxx的方式向外暴露
8.2 导入模块
// 导入moduls_lea.js
var moduls_lea = require('./moduls_lea.js'); //通常使用相对路径
// 对于js和json文件,可以省略后缀名
var moduls_lea = require('./moduls_lea'); //通常使用相对路径
// 对于node_modules中的模块,必须使用绝对路径, 例如
// var jquery = require('jquery'); // 这样写会报错
var jquery = require('D:/node_modules/jquery'); // 这样写才可以
// 对于内置模块,可以省略路径,例如
var fs = require('fs'); // 这样写可以
导入模块的注意事项:
- 对于自己创建的模块,导入时一般是写相对路径,且不能省略./或者…/
- 如果导入的是一个文件夹,则首先检测文件夹下的package.json文件中的main属性对应的文件,如果存在就会导入,反之如果文件不存在就会报错,如果main属性不存在的情况下,或者package.json不存在,则会去尝试着导入文件夹下的index.js或者index.json,如果还是没有找到,就会报错。
9. 包管理工具
9.1 包是什么
也即是package,代表了一组特定功能的源码集合
9.2 包管理工具
管理包的应用软件,可以对包进行下载安装,更新,删除,上传的操作
借助包管理工具,可以快速的开发项目,提升开发效率
包管理工具是一个通用的概念,很多编程语言都有包管理工具,所以掌握好包管理工具很重要
9.3 常用的包管理工具
- npm(node.js官方内置的管理工具)
- yarn
- cnpm
9.4 包的初始化
npm init
带领我们创建一个package.json的文件
使用一中交互式的方法
问我们问题,我们回答,直到生成一个包的配置文件
也就是:package.json文件
创建一个新目录,然后以此目录作为工作目录,启动命令工具,执行npm init。
这个命令将文件夹初始化为一个包,package.json是包的配置文件,每个包都必须要有配置文件。
初始化过程的注意事项:
- package name (包名)不能使用中文和大写,默认值是文件夹的名称,所以文件夹的名称也不能是中文和大写。
- version(版本号)要求 x.x.x 的形式定义,x必须是数字,默认值是1.0.0
- ISC证书与MIT证书功能上是相同的,关于开源证书扩展阅读。
- package.json可以手动创建与修改
- 使用npm init -y 或者npm init --yes极速创建package.json
9.5 搜索包
搜索包的方式有两种:
- 命令行 npm s 关键字
- 网站搜索 网址:http//www.npmjs.com/
9.6 下载包
npm i 包名
运行之后,文件夹下会增加两个资源
一个是node_modules:存放下载的包
一个是package-lock.json:包的锁文件,用来锁定包的版本
9.7 引用npm包的基本流程
- 在当前的文件夹下node_modules中寻找同步的文件夹
- 如果没有,就往上一级的文件夹中node_modules里寻找同名的文件夹, 直至到磁盘的根目录.