这是我参与「第四届青训营 」笔记创作活动的的第14天
node.js基本介绍
node.js是基于chrome V8引擎的js运行环境
- 浏览器是js的前端运行环境
- node.js是js的后端运行环境
- node.js中无法调用DOM和BOM等浏览器内置API
node.js的使用
node.js中执行js文件
- 先切换到文件目录下--> node js文件名
- 文件右键 powershell打开--> node js文件名
终端中的快捷键
- 向上箭头,快速定位到上一次执行的命令
- tab键,自动补全文件名
- esc键,快速清空当前输入的命令
fs文件系统模块
fs是node.js提供用来操作文件的模块,它提供了一系列的方法和属性,用来满足用户对文件操作的需求
在使用fs之前,需要先导入const fs = require('fs')
文件读取 fs.readFile()
fs.readFile(path[,options],callback) 可以读取指定文件的内容
参数:
- 参数1:必选参数,字符串,表示文件路径。
- 参数2:可选参数,表示以什么编码格式来读取文件,默认utf8
- 参数3:必选参数,文件读取完成后,通过回调函数拿到读取得结果,err失败 datastr成功
const fs = require('fs');
fs.readFile('./file/01.txt','utf8',function(err,dataStr){
//打印结果
//1.打印成功err值为 null
//2.打印失败err的值为 错误对象,dataStr的值为 undefined
console.log(err);
//打印读取数据
console.log(dataStr)
)
判断文件是否读取成功
const fs = require('fs');
fs.readFile('./file/01.txt','utf8',function(err,result){
if(err){
return console.log("文件读取失败"+err.message);
)
console.log(result);
写入文件内容 fs.writeFile()
fs.writeFile(file,data[,options],callback) 可以读取指定文件的内容
参数:
- 参数1:必选参数,字符串,表示文件路径(不一定是存在的)
- 参数2:必选参数,表示写入内容
- 参数3:可选参数,表示以什么编码格式来读取文件,默认utf8
- 参数4:必选参数,文件写入后的回调函数
注意:只能创建文件,不能创建路径,新写入的内容会覆盖原先的内容
const fs = require('fs');
fs.writeFile('./file/01.txt','abcd','utf8',function(err){
//写入结果
//1.写入成功err值为 null
//2.写入失败err的值为 错误对象
console.log(err);
)
案例
arr.split(),arr.replace(),arr.join() 对数据进行处理
//引入fs模块
const fs = require('fs');
//读取文件
fs.readFile('./成绩.txt','utf8',function (err,dataStr) {
//判断是否读取成功
if(err){
return console.log('读取失败'+err.message);
}
// console.log(dataStr);
// 处理数据
const arrOld = dataStr.split(' ');
const arrNew =[];
arrOld.forEach(item => {
arrNew.push(item.replace('=',':'));
})
const newStr = arrNew.join('\r\n');
// console.log(newStr);
//将处理完成后的数据写入新的文件中
fs.writeFile('./成绩-OK.txt',newStr,'utf8',function (err) {
if(err){
return console.log('写入失败');
}
console.log('写入成功');
})
});
path路径模块
path模块是node.js官方提供的,用来处理路径的模块,它提供了一系列的方法和属性,用来满足用户对路径的处理需求
在使用path模块之前,需要先导入 const path = require('path')
路径问题
在使用fs模块操作文件时,如果提供的操作路径是以./或../开头的相对路径时,很容易出现路径动态拼接错误问题
原因:代码在运行时,会以执行node命令所在的目录动态拼接出被操作文件的完整路径
解决:
- 使用完整路径,'C://files//01.txt',但移植性差不易于维护;
- 可使用'__dirname+'file/01.txt''进行拼接,'__dirname'表示当前js文件所在的目录;
路径拼接 path.join()
path.join([,path])用来将多个路径片段拼接成一个完整的路径字符串
'../'会抵消前面的一层路径
const pathStr = path.join('/a','/b/c','../',''/d),
console.log(pathStr);//\a\b\d
path.basename() 获取路径中的文件名
path.basename(path[,ext])用来从路径字符串中,将文件名解析出来
- 参数1:必选参数,表示一个路径的字符串;
- 参数2:可选参数,表示文件的扩展名;
const fpath = 'a/s/v/index.html'
const nameWithoutExt0 = path.basename(fpath);
console.log(nameWithoutExt0);//index.html
const nameWithoutExt1 = path.basename(fpath,'.html');
console.log(nameWithoutExt1);//index
const nameWithoutExt2 = path.basename(fpath, 'html');
console.log(nameWithoutExt2);//index.
path.extname() 获取路径中文件扩展名
path.extname(path) 用来获取文件扩展名,返回一个字符串
const fpath = 'a/s/v/index.html'
const ext1 = path.extname(fpath);
console.log(ext1);//.html
fs和path的综合案例
- 正则表达式
- fs模块
- path模块
- exec() 方法在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 [
null] - writeFile只能创建文件,不能创建路径,新写入的内容会覆盖原先的内容
const fs = require('fs');
const path = require('path')
//正则表达式
const regStyle = /<style>[\s\S]*<\/style>/
const regScript = /<script>[\s\S]*<\/script>/
fs.readFile(path.join(__dirname, '../素材/index.html'), 'utf8', (err, dataStr) => {
if (err) return console.log('读取失败');
// console.log(dataStr);
resolveCSS(dataStr);
resolveJS(dataStr);
resolveHTML(dataStr);
})
//写入CSS样式
function resolveCSS(htmlStr) {
const r1 = regStyle.exec(htmlStr);
const newCSS = r1[0].replace('<style>', '').replace('</style>', '');
fs.writeFile(path.join(__dirname,'../素材/index.css'),newCSS,'utf8',err => {
if(err) return console.log('写入CSS样式失败'+err.message)
console.log('写入css样式成功');
})
}
//写入JS脚本
function resolveJS(htmlStr) {
const r1 = regStyle.exec(htmlStr);
const newCSS = r1[0].replace('<script>', '').replace('</script>', '');
fs.writeFile(path.join(__dirname,'../素材/index.js'), newCSS, 'utf8', err => {
if (err) return console.log('写入JS脚本失败' + err.message)
console.log('写入JS脚本成功');
})
}
//写入HTML文件
function resolveHTML(htmlStr){
const newHTML = htmlStr.replace(regStyle,'<link rel="stylesheet" href="./index.css">').
replace(regScript,'<script src="./index.js"></script>');
fs.writeFile(path.join(__dirname,'../素材/index.html'),newHTML,(err) => {
if (err) return console.log('写入HTML失败' + err.message)
console.log('写入HTML成功');
})
}
http模块
http模块是node.js提供的用来创建web服务器的模块,通过http模块提供的http.createServer()方法,就能方便的把一台普通的电脑,变成一台web服务器,从而对外提供web资源服务
在使用http模块之前,需要引入 const http = require('http')
服务器的基本知识
- IP地址:互联网上每台计算机上的唯一地址,通常使用“点分十进制”表示成(a.b.c.d)的形式,其中,abcd都是0-255之前的十进制整数;
- 域名服务器:提供IP地址和域名之间转换服务的服务器;
- 端口号:客户端发送过来的网络请求,通过端口号,可以准确的交给对应的web服务进行处理,在实际应用中,URL中的80端口可以被省略;
创建一个基本的web服务器
graph TD
导入http模块 --> 创建web服务器实例 --> 为服务器实例绑定request事件-监听客户端的请求 --> 启动服务器 --> ctrl+c停止服务器
//1.导入http模块
const http = require('http');
//2.创建web服务器实例
const server = http.createServer();
//3.为服务器实例绑定request事件,监听客户端请求
server.on('request',function(req,res){
console.log('someone visit our web server');
})
//4.启动服务器
server.listen(80,function(){
console.log('server running at http://127.0.0.1:8080');
})
模块化
node.js 中根据来源的不同,将模块分为了三个大类,分别是:
- 内置模块:内置模块是由node.js官方提供的,例如fs,path,http
- 自定义模块:用户创建的每个js文件,都是自定义模块
- 第三方模块:由第三方开发出来的模块,并非官方提供的内置模块,也不是自定义模块,使用前需要先下载
//内置fs模块
const fs = require('fs')
//自定义模块,可以省略.js后缀名
const comment = require('./comment.js')
//第三方模块
const moment = require('moment')
使用require()方法加载其他模块是,会执行被加载模块中的代码
模块作用域
定义:和函数作用域类似,在自定义模块中定义的变量和方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,就叫做模块作用域
作用:防止全局变量污染。<script>标签引入会造成全局变量污染,就是引入的js文件中的相同名称的变量会起冲突
module对象
在每一个.js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息
module.exports对象——向外共享模块作用域内的成员
在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用。在外界用require()方法导入自定义模块时,得到的就是module.exports所指向的对象
exports对象
由于module.exports单词写起来比较复杂,为了简化向外共享成员的代码,node提供了exports对象。默认情况下,exports和module.exports指向同一个对象。最终共享的结果,还是一module.exports指向的对象为准
console.log(module.exports===exports);//true
module.exports 与 exports的区别
require()模块时,永远得到的是module.exports指向的对象
为了避免混乱,在同一个模块不要同时使用module.exports和exports
test01.js
exports.username = 'marry';
module.exports= {
username:'tom',
age:18
}
console.log(exports === module.exports);//false
exe01.js
const test = require('../素材/test')
console.log(test);//输出module.exports[{ username: 'tom', age: 18 }]
node.js中的模块化规范
node.js遵循了commonJS模块化规范,commonJS规定了模块的特性和各模块之间如何相互依赖
CommonJS规定
- 每个模块内部,module变量代表当前模块;
- module变量是一个对象,它的exports属性(即module.exports)是对外的接口;
- 加载某个模块,其实是加载该模块的module.exports属性。require()方法用域加载模块;
包与npm
node.js中的·第三方模块又叫做包 由于node.js的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发的时候,效率很低。 包是基于内置模块封装出来的,提供了更高级,更方便的API,极大的提高了开发效率 包和内置模块之间的快洗,类似于jquery和浏览器内置API之间的关系
在项目中安装包的命令:npm install 包的完整名称
安装指定版本的包:npm i 包名@版本号
一次安装多个包,包名用空格分开
版本号介绍
- 第一位数字:大版本
- 第二位数字:功能版本
- 第三位数字:bug修复版本
版本号提升规则:只要前面的版本号增长了,则后面的版本号归零
//导入需要的包
const moment = require(''moment);
//查阅文档搜索包的用法
const dt = moment().format('YYYY-MM-DD HH:mm:ss');
初次安装包后多了哪些文件
node_modules文件夹用来存放所有已安装到项目中的包,require()导入第三方包时,就是从这个目录中查找并加载包package-lock.json配置文件用来记录node_modules目录下的每一个包的下载信息,例如报的名字、版本号、下载地址等
包管理配置文件
npm规定,在项目根目录中,必须提供一个叫做package.json的包管理配置文件。用来记录与项目有关的一些配置信息。例如:
- 项目名称、版本号、描述等
- 项目中都用到了哪些包
- 哪些包只在开发期间会用到
- 哪些包在开发和部署时都需要用到
快速创建package.json
命令:npm init -y
上述命令只能在英文的目录下成功运行,所以,项目文件夹的名称一定要使用英文命名,不要使用中文,不能出现空格
如果希望一次性安装所有的包,可以运行npm install(npm i)
卸载包
命令:npm uninstall 包名
devDependencies节点和dependencies节点
如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDependencies节点中 如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点中
- 安装指定的包,并记录到devDependencies节点中
npm i 包名 -D - 上述命令是简写形式,等价于
npm install 包名 --save-dev
解决下包速度慢的问题
切换npm的下包镜像源
//查看当前的下包镜像源
npm config get registry
//将下包的镜像源切换为淘宝镜像源
npm config set registry=http://registry.npm.taobao.org/
//检查镜像源是否下载成功
npm config get registry
nrm——一个切换下包的镜像源
为了更方便的切换下包的镜像源,我们可以安装nrm这个小工具,利用nrm提供的终端命令,可以快速查看和切换下包的镜像源
//通过npm包管理器,将nrm安装为安全可用的工具
npm i nrm -g
//查看所有可用的镜像源
nrm ls
//将下包的镜像源切换为taobao镜像
nrm use taobao
项目包和全局包
项目包
那些被安装在项目的node_modules目录下的包都是项目包
- 开发依赖包:被记录到devDependencies节点中的包,只在开发期间会用到
- 核心依赖包:被记录在dependencies节点中的包,在开发期间和项目上线之后都会用到
//开发依赖包
npm i 包名 -D
//核心依赖包
npm i 包名
全局包
在执行npm i 命令时,如果提供了-g参数,则会把包安装为全局包
全局包会被安装在C:\Users\admin\AppData\Roaming\npm\node_modules目录下
- 全局安装指定包:
npm i 包名 -g - 卸载全局安装的包:
npm uninstall 包名 -g
i5ting_toc——一个可以把md文档转为html页面的一个小工具
使用步骤如下:
//将i5ting_toc安装为全局包
npm install -gi5ing_toc
//调用i5ting_toc,将md->html
i5ing_toc -f 要转换的md文件路径 -o
规范包的结构
一个规范的包,它的组成结构,必须符合以下三点要求:
- 包必须以单独的目录而存在
- 包的顶级目录下要必须包含
package.json这个包管理配置文件 - package.json中必须包含
name,version,main这三个属性,分别代表包的名字,版本号,包的入口
开发属于自己的包
示例
- 初始化包的基本结构
- 新建xxx文件夹,作为包的根目录
- 在xxx文件夹中,新建【package.json,index.js,README.md】
- 初始化package.json(npm中搜索一下有没有撞名)
{
"name": "xxx",
"versoin": "1.0.0",
"main": "index.js",
"description": "提供了格式化时间,HTMLEScape的功能",
"keywords": ["xxx","dateFormat","escape"],
"license": "ISC"
}
- 在index.js中定义格式化时间的方法
5. 在index.js中定义转义HTML的方法
6. 将不同功能进行模块化拆分
- 编写包的说明文档README.md: 安装方式、导入方式、格式化时间、转义HTML中的特殊字符、还原HTML中的特殊字符、开源协议
- 发布包
- 注册npm账号
- 登录npm账号:npm账号注册完成后,可以在终端执行
npm login命令,依次出入用户名,密码,邮箱后,即可登陆成功,注意:在运行npm login 命令之前必须先把下包的服务器地址切换为npm的官方服务器,否则会导致发布包失败
- 把包发布到npm上:将终端切换到包的根目录之后,运行
npm publish命令,即可将包发布到npm上 - 删除已经发布的包:运行
npm unpublish 包名 --force,即可从npm删除已发布的包- npm unpublish命令只能删除72小时以内发布的包
- npm unpublish 删除的包,在24小时内不允许重复发布
- 发布包的时候要慎重,尽量不要往npm上发布没有意义的包
模块的加载机制
优先从缓存中加载
模块在第一次加载之后会被缓存。这也意味着多次调用require()不会导致模块的代码被执行多次 注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率
内置模块的加载机制
内置模块是由node.js官方提供的模块,内置模块的加载优先级最高 例如,require('fs')始终返回内置的fs模块,即使在node_modules目录下有名字相同的包也叫做fs
自定义模块的加载机制
使用require()加载自定义模块时,必须指定以./或是../开头的路径标识符。在加载自定义模块的时候。如果没有指定./或是../这样的路径标识符,则node会把它当作内置模块或是第三方模块进行加载
同时,在使用require()导入自定义模块时,如果省略文件扩展名,则node.js会按顺序分别尝试加载以下的文件:
- 按照
确切的文件名进行加载 - 补全
.js扩展名进行加载 - 补全
.json扩展名进行加载 - 补全
.node扩展名进行加载 - 加载失败,终端报错
第三方模块加载机制
如果传递require()的模块标识符不是一个内置模块,也没有./或是../开头,则node.js会从当前模块的父目录开始,尝试从/node_modules文件夹中加载第三方模块
如果没有找到对应的第三方模块,则移动到再上一层的父目录中,进行加载直到文件系统的根目录
目录作为模块
当把目录作为模块标识符,传递给require()进行加载的时候,有三种加载方式
- 在被加载的目录下查找一个叫做package.json的文件,并寻找main属性,作为require()加载的入口
- 如果目录里没有package.json文件,或者main入口不存在或者无法解析,则node.js将会试图加载目录下的index.js文件
- 如果上述两步都失败了,则node.js会在终端打印错误信息,报告模块的缺失:Error:Cannot find module 'XXX'