Node.js模块
- 模块(包)是Node.js应用程序的基本组成部分。
- 大部分前端工程化的工具,是以模块的形式存在的。
Node.js模块的对应关系
内置模块
- fs、path、os、http...
- 官方提供的,跟随Node.js一起安装
- 官方链接:nodejs.cn/api/
自定义模块
- 工程师自己写的
第三方模块
- Less、Babel、Express...
- 社区维护的,需要单独下载才能使用
- www.npmjs.com/
web端的对象
宿主对象
- document、window、location...
自定义对象
- 工程师自己写的
第三方库
- jQuery、Boostrap...
总结:Node.js模块和web端的对象
- 如下图所示
内置模块 也叫核心模块
- 在官方文件中查找某个函数,可以直接使用ctrl+f调出搜索框,输入需要查找的函数名称。
核心模块 - console
- 控制台中输出的内容,通过不同的颜色标识不同的变量类型。
- 控制台中可以一次输出多个变量,多个变量之间,用逗号分隔
- 官方文档:nodejs.cn/api/console…
console方法的使用
- console.log()
var obj = {
name:"zs",
age:18
}
console.log(obj); // { name: 'zs', age: 18 }
- console.table()
var obj = {
name:"zs",
age:18
}
console.table(obj);
// 输出
// ┌─────────┬────────┐
// │ (index) │ Values │
// ├─────────┼────────┤
// │ name │ 'zs' │
// │ age │ 18 │
// └─────────┴────────┘
- console.time():启动一个计时器,用以计算一个操作的持续时间。
// 计时函数,需要前后的label相同
console.time("for");
for(let i = 0; i < 1000000; i++){
}
console.timeEnd("for"); //2.346ms
console.time("while");
var i = 0;
while(i < 1000000){
i++;
}
console.timeEnd("while"); // 1.964ms
// 推荐使用:while循环,要比for循环效率更高些
核心模块 - process
- process提供了有关当前Node.js进程的信息
process是全局变量
- (global.process),使用时无需require引入
- 官方文档:nodejs.cn/api/process…
- process是个对象,内部使用点调用属性
process调用属性
- process.version
// 输出Node.js的当前版本号
console.log(process.version); //v14.16.0
- process.arch
// 输出操作系统架构
console.log(process.arch); // x64
- process.platform
// 输出操作系统平台
console.log(process.platform); // win32
- process.cwd()
// 输出当前工作目录(文件夹) cwd = current working directory
// 第一种方式
console.log(process.cwd());
// 第二种方式
console.log(__dirname);
- process.env (env = environment)
// 输出:环境变量
console.log(process.env);
// 自定义环境变量: 用来标识开发环境
process.env.NODE_ENV = 'develop';
console.log(process.env);
- process.pid
- 操作系统给每一个进程都分配了唯一的编号(pid)。
- pid只有在进程运行期间,才能获取。
// 获取进程的编号
console.log(process.pid); // 27296
// 杀死进程 process.kill(进程编号)
console.log(process.kill(5634));
核心模块 - path
- path模块提供了有关路径操作的函数。
- 当前目录:./
- 上一级目录:../
- 使用之前,需要通过require引入
- 官方文档:nodejs.cn/api/path.ht…
输出文件目录
- 代码演示
// 输出当前文件所在的路径
console.log(__dirname);
// 输出当前文件的完整路径(包含当前文件的文件名)
console.log(__filename);
path的方法
// 不是全局变量,需要引入
var path = require("path");
- path.extname()
// 获取文件的扩展名 ext = extension
console.log(path.extname(__filename)); // .js
- path.dirname()
// 获取路径中的目录部分
// 与__dirname 输出相同
console.log(path.dirname(__filename));
- path.basename()
// 获取路径中的文件名
console.log(path.basename(__filename)); // hello.js
- path.join():可以将多个路径拼接起来
// 将路径拼接起来
// 下面方法含义:返回上一级目录
console.log(path.join(__dirname,'..'));
核心模块 - fs
- fs(file system)提供了文件操作的API
- 使用之前,需要通过require引入
- 官方文档:nodejs.cn/api/fs.html
- 目录就是文件夹,在程序的世界里,叫做目录。
文件操作(上半部分)
var fs = require("fs");
- fs.writeFile():如果文件只读,不能写入,就会报错
// fs.writeFile('文件路径','写入内容',回调函数)
fs.writeFile('./txt1.txt','今天天气真好!',(err) => {
// 如果文件只读,不能写入,就会报错
if(err) throw err;
console.log("写入成功");
});
- fs.readFile():如果读取的文件不存在,会报错
// 读文件
// readFile('文件路径',回调函数)
fs.readFile('./txt1.txt',(err,data) => {
if(err) throw err;
// 输出:<Buffer e4 bb 8a e5 a4 a9 e5 a4 a9 e6 b0 94 e7 9c 9f e5 a5 bd ef bc 81>
console.log(data);
});
// 直接输出data,输出的是十六进制数
// data其实在计算机中存储的时候是二进制的,输出的时候默认是十六进制的
// 利用方法toString(),将其转换成字符串输出
fs.readFile('./txt1.txt',(err,data) => {
if(err) throw err;
// 输出:<Buffer e4 bb 8a e5 a4 a9 e5 a4 a9 e6 b0 94 e7 9c 9f e5 a5 bd ef bc 81>
// 直接输出data,输出的是十六进制数
// data其实在计算机中存储的时候是二进制的,输出的时候默认是十六进制的
// console.log(data);
// 利用方法,将其转换成字符串输出
// 输出结果:今天天气真好!
console.log(data.toString());
});
- fs.unlink():文件不存在的时候,删除会报错
// 删除文件
// fs.unlink('文件路径',回调函数);
fs.unlink('./txt1.txt',(err) => {
if(err) throw err;
console.log('删除成功');
});
- fs.appendFile():
-
appendFile与writeFile的区别:writeFile只会将最后一次写入的内容加入到文件中,而不是追加内容到文件(appendFile)。
// fs.appendFile('文件路径','写入内容','回调函数'); fs.appendFile('./txt1.txt','今天天气真好',(err) => { if(err) throw err; // 多次执行,后会变成:今天天气真好今天天气真好今天天气真好.... console.log("文件追加成功"); }); -
不想每次追加的文字都显示在一行,想让其换行(添加\n)
// fs.appendFile('文件路径','写入内容','回调函数'); fs.appendFile('./txt1.txt','今天天气真好\n',(err) => { if(err) throw err; console.log("文件追加成功"); }); -
文件路径,使用合并路径方法(__dirname + '/txt1.txt')
// 使用拼接 fs.appendFile(__dirname + '/txt1.txt','今天天气真好\n',(err) => { if(err) throw err; console.log("文件追加成功"); }); -
循环将一个数组中的数据,写入到文件中
// 写入一个数组中的数据 var arr = ['今天出太阳了','好舒服','出金光吧!']; for(var k of arr){ fs.appendFile('./txt1.txt',k + '\n',(err) => { if(err) throw err; }); }
-
文件操作(中场部分)
- fs.mkdir()
// 创建目录(文件夹)
// fs.mkdir('目录路径',回调函数);
fs.mkdir('./d1',(err) => {
if(err) throw err;
console.log("创建目录成功");
});
-
fs.rmdir()
- 删除空目录 成功
// 删除目录 // fs.rmdir('目录路径',回调函数) fs.rmdir('./d1',(err) => { if(err) throw err; console.log("删除目录成功"); }); - 删除非空目录,会报错: directory not empty
- 解决问题(删除非空目录)
- 1.先删除目录下的普通文件(清空目录)
- 2.通过rmdir删除空目录
// 删除非空目录:两步走 // 1.先删除目录下的普通文件 fs.unlink('./d1/1.txt',(err) => { if(err) throw err; console.log("文件删除成功"); }); // 2.再删除目录 fs.rmdir('./d1',(err) => { if(err) throw err; console.log("目录删除成功"); }); - 删除空目录 成功
-
fs.rename(): 出现有同名目录时,就会报错
// 重命名目录
// fs.rename('旧名称','新名称',回调函数);
fs.rename(__dirname + '/d1',__dirname + '/d2',(err) => {
if(err) throw err;
console.log("目录重命名成功");
});
-
fs.readdir()
- 常规输出
// 读目录,读文件夹中所有的内容 // fs.readdir('目录路径',回调函数); fs.readdir(__dirname,(err,data) => { if(err) throw err; // 输出结果:[ '1_前端工程化.md', 'd1', 'd2', 'hello.js', 'img', 'txt1.txt' ] console.log(data); });
* 使用map遍历(和foreach有点相似),一个一个输出 ```js // 换行输出 fs.readdir(__dirname,(err,data) => { if(err) throw err; data.map((d) => { console.log(d); }); });- 读目录,并判断读取的是:目录 还是 文件
- 常规输出
文件操作(下半部分)
fs - 同步函数(synchronization)
- 在主程序中自上而下运行
- 例如:去火车站排队买票
- 当任务有先后顺序,用同步函数(sync)
- 例如:删除文件之前要先确保文件是存在的
// 删除文件之前,要先确保文件是存在的 // 返回值:true\false if(fs.existsSync(__dirname + './t1.txt')){ // 删除文件 fs.unlinkSync(__dirname + './t1.txt',(err) => { if(err) throw err; }); }else { console.log("文件不存在"); }
- 例如:删除文件之前要先确保文件是存在的
异步函数
- 通过回调函数在事件队列中运行
- 例如:委托黄牛买票,买好票后通知我(无需等待,可以做其他事)
总结(同步和异步函数)
- 同步函数名和异步函数名的区别,就是:同步函数名有sync
- 主程序(单线程)是同步执行,事件队列是异步执行。
fs实现文件复制
- 将d1/txt1.txt 复制到 d2/t.txt(d2目录存在),那么文件复制成功
- 将文件复制到不存在的目录中,复制不会成功,需要先创建新目录
// fs实现文件复制
// 将d1/txt1.txt 复制到 d2/t.txt 文件复制成功
// 将文件复制到不存在的目录中,复制不会成功,需要先创建新目录
// 1.读文件
if(fs.existsSync(__dirname + '/d1/txt1.txt')){
fs.readFile(__dirname + '/d1/txt1.txt',(err,data) => {
if(err) throw err;
else {
if(!fs.existsSync(__dirname + '/d3')){
// 2.创建新目录
fs.mkdir(__dirname + '/d3',(err) => {
if(err) throw err;
console.log("目录创建成功");
});
}
// 3.写文件
fs.writeFile(__dirname + '/d3/t.txt',data,() => {
if(err) throw err;
console.log("写入成功");
});
}
});
}
fs实现文件压缩
// fs实现文件压缩
// 使用正则表达式
// 去除空格
// 1.读取文件内容
var path = require("path");
// var ph = __dirname + '/d1/test.css';
var ph = path.join(__dirname,'d1/test.css');
if(fs.existsSync(ph)){
fs.readFile(ph,(err,data) => {
if(err) throw err;
else{
// 2.将内容中的空格 替换成 空字符串
// 还需要将注释也去掉 \s 表示 空白字符 \S 表示 非空白字符
// 不能写^ $ 限制,因为一旦写了就必须是这个开头和结尾,而不是在一段字符串中寻找。
var dataA = data.toString().replace(/\s+/g,'').replace(/\/\*{1,2}[\s\S]*\*\//g,'');
// 3.再次写入到文件中
fs.writeFile(ph,dataA,() => {
if(err) throw err;
console.log("写入成功");
});
}
});
}
文件流(缓冲vs流)
文件操作 - 缓冲方式
-
源文件 -> 内存缓冲 -> 目标文件
-
一直将内存填充满,操作系统才会将其给目标文件。
- 比喻:缓冲就像一个桶一样,和尚挑水要看桶的大小,桶要是小的话,就需要挑多次。
文件操作 - 流方式
- 源文件 -> 管道 -> 目标文件
- 比喻:流就相当于水龙头。
// 流的写法
var rs = fs.createReadStream('./d1/test.css');
var ws = fs.createWriteStream('./d2/test.css');
// 接通读文件和写文件之间的管道
rs.pipe(ws);
为什么选择流
- 内存效率提高
- 无需加载大量数据(缓冲要足够大才行)
- 流把大数据切成小块,占用内存更少
- 时间效率提高
- 接获数据后立即开始处理
- 无需等到内存缓冲填满
核心模块 - http
- http模块可以发布web服务
- 使用之前,通过require引入
- 官方文档:nodejs.cn/api/http.ht…
http创建服务器
- 代码演示(注意:有中文的话,一定要设置响应字符集,否则会乱码)
// 引入http
const http = require("http");
// 1.创建服务器
const server = http.createServer((req,res) => {
res.statusCode = 200;
// text/html是以html的形式输出,比如就会在页面上显示一个文本框。
// text/plain形式就会在页面上原样显示这段代码。
res.setHeader('Content-Type','text/plain; charset=utf-8');
res.end('你好:Node.js');
});
// 2.发布web服务
const port = 3000;
const host = 'localhost';
// 在浏览器中访问http://localhost:3000 然后能看到效果
server.listen(port,host,() => {
console.log(`服务器运行在 http://${host}:${port}`);
})
自定义模块(程序员自己写的Node.js模块)
- Node.js中每一个单独的.js文件,就是一个模块。
- 每个模块中都有一个module变量,其代表当前模块。(相当于function中的this)
自定义模块书写与使用
- 自定义模块写完之后,需要通过exports暴露出来,才能够被外部使用。
- 没有暴露出来的属性和方法,就成为其私有的属性和方法,外部没有办法调用。
- 自定义模块书写 代码演示
// 自定义模块
const PI = 3.14;
// 1.计算圆的周长
function perimeter (r){
return 2 * PI * r;
}
// 2.计算圆的面积
function area (r){
// return PI * r * r;
return PI * Math.pow(r,2);
}
// 3.通过exports将属性和方法暴露
module.exports = {
// 在ES6中,属性名和属性值相同时,可以只写一遍
perimeter
}
- 外部调用自定义模块的方法(引入写入自定义模块的路径)
// 引入自定义的模块(引入写入自定义模块的路径)
var circle = require("./1_自定义模块");
// 自定义半径变量的值
const r = 10;
// 调用自定义模块中的周长函数
console.log('周长:',circle.perimeter(r));
// 会报错,因为area()并没有通过exports暴露出来,因此属于自定义模块的私有方法,不能被外界调用
console.log('面积:',circle.area(r));
- 注意:在ES6中,属性名和属性值相同时,可以只写一遍
模块的加载逻辑(即:模块的引入)
按组织方式划分模块
- 文件模块:一个独立的.js文件。
- 目录模块:将多个.js文件放在一个目录中。
目录模块的加载逻辑
// 目录结构
> dir01
--- a.js
--- b.js
--- c.js
const dir01 = require('./dir01');
- 步骤:
- 1.确定入口文件
- 2.引入入口文件
- 3.然后在入口文件中引入其他文件
通过组织方式来详细讲解
文件模块
以路径开头引入
- require('./circle')
- 引入自定义的模块,后缀名.js可以省略。
不以路径开头引入
- require('path')
- 引入官方核心模块
目录模块
以路径开头引入
- require('./dir01')
- 找到指定路径下的dir01目录,然后引入入口文件。
- 入口文件:dir01目录下存在index.js
// ----------index.js--------------
// 这是d1文件夹的入口文件
function info(){
console.log('d1文件夹的入口文件');
}
module.exports = {
info
}
// -----------test.js---------------
// 现在引入目录模块中的自定义模块
// 通过路径引入:去d1文件夹中寻找入口文件
var dir1 = require('./d1');
console.log(dir1.info()); //d1文件夹的入口文件
- 入口文件:dir01目录下存在package.json
// ----------------package.json-------------------
{
"main":"1_自定义模块.js"
}
// ---------------test.js----------------------
// 现在引入目录模块中的自定义模块
// 通过路径引入:去d1文件夹中寻找入口文件
var dir1 = require('./d1');
console.log(dir1.info()); //d1文件夹的入口文件
不以路径开头引入
- 到当前目录下的node_modules目录中寻找dir02;
- 如果找不到会到上一级目录寻找,直到顶层目录;假如到顶层还没有找到,就会报错。
- 找到目录模块dir02后,引入dir02中的入口文件。
如何确定入口文件?
- 在目录中寻找package.json文件,入口文件通过其main属性指定。
- 如果找不到,则**默认引入index.js.
- 注:package.json是目录模块的描述文件。
如何在入口文件中引入其他的自定义模块文件
// ----------index.js--------------
// 这是d1文件夹的入口文件
// 引入其他自定义模块
const circle = require('./1_自定义模块');
const r = 10;
// 这是d1文件夹的入口文件
function info(){
console.log('inex.js文件中:' + circle.area(r));
console.log('d1文件夹的入口文件');
}
module.exports = {
info,
area: circle.area
}
// -----------test.js---------------
// 现在引入目录模块中的自定义模块
// 通过路径引入:去d1文件夹中寻找入口文件
var dir1 = require('d1');
// 自定义半径变量的值
const r = 10;
// 调用自定义模块中的周长函数
console.log('面积:',dir1.area(r)); // 输出:面积: 314
// 输出:inex.js文件中:314
console.log(dir1.info()); // 输出:d1文件夹的入口文件
第三方模块(社区维护的Node.js模块)
- 前端工程化的大部分工具,都是第三方模块
- 想要使用Node.js的第三方模块,需要通过单独安装。
npmjs.com
- npmjs.com就是第三方模块集中的管理平台
- 网址:www.npmjs.com/
npm(Node Package Manager)概述
- 是Node.js的包管理工具
- 包就是一坨代码,就是Node.js的第三方模块。
npm是一个命令
- 跟随Node.js一起安装
- npm可以下载(安装)包和包的依赖,例如Boostrap是依赖jQuery,那么下载的时候,会将其一起下载安装。
npm的作用
- 手动下载:1.打开网站 2.找到资源 3.点击下载
- npm安装:npm install <package-name>
npm镜像源
- npm从镜像源下载包
- npm下载类比之应用商店
修改npm镜像源
国外镜像源(响应时间过长)
国内镜像源
修改命令(在命令行进行修改)
- npm config set registry registry.npm.taobao.org
- npm config get registry
使用npm安装包
- npm install
全局安装
- 每个项目都能用到(将包当作全局工具使用)
- npm install --global
- 简写:npm i -g
步骤
- 1.明确你的需求
- 2.找到合适的包
- 3.通过npm安装包
- 4.使用包
项目(局部)安装
- 只在当前项目中使用
- npm install --save
- 简写:npm i -S
步骤
- 1.创建项目目录(mkdir project)
- 2.进入项目目录(cd project)
- 3.初始化项目(npm init)
- 4.在项目中安装包(注意目录)
局部安装和全局安装区别
- 局部安装:要初始化项目 npm init
-
局部安装:要在项目中执行局部安装命令,就会出现node_modules文件夹
- minify的命令:node_modules/.bin/minify下面执行命令
-
.bin文件夹中的是 minify和minify所依赖的包
.\node_modules\.bin\minify .\index.css > ./index.min.css
--save与--save-dev
npm安装命令的参数
- --save / -S :安装的包,开发和上线都需要
- 例如: jQuery
- --save-dev / -D:安装的包,只在开发环境使用
- 例如:minify
- 在项目中的package.json文件中的区别
- dependencies是开发和上线都需要依赖的包
- devdependencies只是开发需要的包
npm安装包的方式
- 全局安装
- npm i -g
- 项目安装
- npm i -S
- npm i -D
附录
- 快捷键打开任务管理器: ctrl+shift+esc
- 在ES6中,属性名和属性值相同时,可以只写一遍
- Bootstrap封装了前端最常使用的一些效果。