【Node.js】初學
1. 初识Node.js
Node.js 是 JavaScript 运行环境,是一个基于 Chrome V8引擎的 JavaScript运行环境。
安装:https://nodejs.org/zh-cn/ 官网链接
查询版本:输入 node -v
打开终端: 【Shift】+【右键】 打开
补全文件路径:按【Tab】键
快速清空当前输入内容:【ESC】
清空终端: cls 命令
在 vscode 打开终端快捷键: 【Ctrl】+ 【~】
2. fs 文件系统模块
fs 模块 是node.js 官方提供的,用来操作文件的模块,提供了一系列的方法和属性。
使用 fs 模块需要先导入:
const fs = require('fs')
2.1 fs.readFile() 读取文件
语法:
fs.readFile(path [,options] ,callback)
path 文件路径,options 以什么编码格式来读取 如utf8, callback 读取完成后通过回调函数拿到读取的结果
读取 1.txt 文件
// 1. 导入 fs 模块,来操作文件
const fs = require('fs')
// 2. 调用 fs.readFile() 方法读取文件
// 参数1:文件路径
// 参数2:以什么编码格式读取
// 参数3:回调函数
fs.readFile('1.txt','utf8', function(err,dataStr){
// 如果读取成功,则 err 的值为 null
// 如果读取失败,则 err 的值为 错误对象,dataStr 的值为 undefined
console.log(err)
console.log(dataStr)
})
// 判断文件是否读取成功
fs.readFile('1111.txt','utf8',function(err,dataStr){
// 如果成功则err为null,否则为错误对象。
// 成功了就不执行if里面的了,失败了err为真对到return返回结果并结束执行
if(err){
return console.log('文件读取失败!'+ err.message)
}
console.log('读取成功!'+ dataStr)
})
2.2 fs.writeFile() 写入文件
向指定的文件中写入内容。
只能用来创建文件,不能创建路径。遇到不存在的文件夹会报错。新写入的会覆盖之前的内容。
语法:
fs.readFile(file, data [,options] ,callback)
参数1:必选,文件存放的路径。
参数2:必选,要写入的内容。
参数3:可选,以什么格式写入文件内容,默认是utf8
参数4:必选,完成后的回调函数
// 1. 导入 fs 文件系统模块
const fs = require('fs')
// 2. 调用 fs.writeFile() 方法,写入文件的内容
/*
参数1:必选,文件存放的路径。
参数2:必选,要写入的内容。
参数3:可选,以什么格式写入文件内容,默认是utf8
参数4:必选,完成后的回调函数
*/
/* fs.writeFile('2.txt','abcd', function(err){
// 成功err为null,否则为错误对象
console.log(err)
}) */
// 判断是否成功 成功时err为null假,也就不执行if里的
fs.writeFile('GG:\2.txt','abcd', function(err){
if(err){
return console.log('写入失败!'+err)
}
console.log('写入成功!')
})
2.3 路径动态拼接问题 __dirname 获取当前文件所处的目录
原因:代码在运行的时候,会以执行 node 命令时所处的目录,动态拼接出被操作文件的完整路径。
之前执行代码时,里面的文件路径都是相对路径,./1.txt 等,在执行时,会动态拼接完整的路径,当移动文件,就容易出现路径拼接错误的问题,比如在 ./a/1.txt 的移动到 ./b 文件夹下,运行代码时就会报错,因为 b 文件夹下没有 1.txt 这个文件。
因为使用了 ./ 当前目录 ../ 上一级 开头的相对路径,当文件地址发生改变时就会出错。
如果要解决这个问题,就要使用完整的路径,也就是 绝对路径,但移植性非常差,不利于维护。
所以,node.js 提供了一个成员 __dirname ,它表示当前文件所处的目录,
console.log(__dirname)
// 执行它
// > node .\9_动态路径__dirname.js
// 返回了当前所处的路径
// E:\Code\A2\1st\Node.js
例如:js执行文件和1.txt只有在一起,放哪个文件夹下都可以正常执行。
const fs = require('fs')
// __dirname 表示当前文件所处的目录
fs.readFile( __dirname + '/1.txt', 'utf8', function(err,dataStr){
if(err){
console.log("读取失败!")
}
console.log("读取成功!" + dataStr)
})
3. path 路径模块
node.js 官方提供的,用来处理路径的模块,提供了一些方法和属性。
例如:path.join() 方法,将多个路径片段拼接成一个完整的路径字符串。
path.basename() 方法,用来从路径字符串中,将文件名解析出来。
使用 path 模块需要先导入它:
const path = require('path') // 导入 path 模块
3.1 路径拼接 path.join()
path.join() 方法,将多个路径片段拼接成一个完整的路径字符串。
语法:
path.join( [...path] )
今后涉及到路径拼接的操作,需要使用 path.join() 方法进行处理。不要直接使用 + 号进行字符串的拼接。
const path = require('path')
// 注意: ../ 会抵消前面的路径,抵消一级,../../就抵消两级
const pathStr = path.join('/a','/b','/c','../','/d')
console.log(pathStr) // \a\b\d
// 拼接路径建议用 path.join() ,而 + 容错率较低
// 使用 + 拼接路径,结果拼接成了'E:\Code\A2\1st\Node.js.\1.txt'
fs.readFile(__dirname + './1.txt', 'utf8', function(err,dataStr){
if(err){
return console.log("读取失败!" + err)
}
})
// 使用path.join()拼接路径,它会把 ./ 给处理掉,不会造成错误的拼接
fs.readFile(path.join(__dirname, './1.txt'), 'utf8', function(err,dataStr){
if(err){
return console.log("读取失败!" + err)
}
console.log("读取成功!" + dataStr)
})
3.2 获取路径中的文件名 path.basename()
可以获取路径中的最后一部分,经常用来获取路径中的文件名。
语法:
path.basename(path [,ext]) // path 文件路径; ext 文件的扩展名,用来移除后缀
获取路径中的文件名,写上文件扩展名参数时,就移除了文件扩展名,只返回文件名字,不带扩展名。
// path.basename(path [,ext]) //获取路径中的文件名,path为路径,ext为文件扩展名 用来移除后缀名
const fpath = 'a/b/1.txt'
//文件 1.txt 移除文件扩展名.txt 变成了 1
const newFilePath = path.basename(fpath, '.txt')
console.log(newFilePath) //返回了 1
3.2 获取路径中的文件扩展名 path.extname()
可以获取路径中的扩展名部分。
语法:
path.extname(path) // path 文件路径
path:表示一个路径的字符串。
返回:返回得到的扩展名字符串
// path.ectname(path]) //path为路径字符串,返回文件扩展名字符串
const fpath2 = 'a/b/music.mp3'
const newfpath2 = path.extname(fpath2)
console.log(newfpath2) //返回了 .mp3
4. http 模块
用来创建 web 服务器的模块,通过 http 模块提供的 http.createServer() 方法,就可以创建服务器,从而对外提供web资源服务。
使用http模块需要导入:
const http = require('http')
服务器相关概念:
IP地址:是互联网上每一台计算机服务器的唯一地址。
域名:ip的别名,比如 youtube.com ,它对应了youtube的ip地址。
域名服务器 DNS:把域名转换为ip地址。提供ip与域名之间的转换服务的服务器。
测试期间的本地ip地址一般是 127.0.0.1 对应的是 localhost
端口号:每个web服务都对应一个唯一的端口号,客户端发送网络请求,通过端口号,可以准确地交给对应的web服务进行处理。
每个端口号不能同时被多个web服务占用。在URL中的80端口是可以被省略的。如:127.0.0.1:80 和 127.0.0.1 。
4.1 创建 web 服务器
步骤1:导入 http 模块
const http = require('http')
步骤2:创建 web 服务器实例 http.createSetver()
调用 http.createServer() 可以快速创建一个web服务器实例
const my_server = http.createServer()
步骤3:为服务器实例绑定 request 事件
为服务器实例绑定 request 事件,即可监听客户端发送来的网络请求。
my_server.on('request', (req, res) => {
// 只要有客户端来请求,就会触发 request事件,从而调用这个事件处理函数
console.log('。。。')
})
步骤4:启动服务器 .listen()
调用服务器实例的 .listen(端口号,cb回调) 方法,即可启动当前的 web 服务器实例。
// 调用 my_server.listen(端口号, cb回调) 方法启动web服务器
my_server.listen(80, () => {
console.log("http server running at http:127.0.0.1 服务器已启动...")
})
4.2 请求对象 req
只要服务器接收到了客户端的请求,就会调用通过 server.on() 为服务器绑定的 request 事件处理函数。
req 请求对象里面存储的东西,都是与客户端相关的数据和属性。
如果想在事件处理函数中,访问与客户端相关的数据或属性,就可以使用如下的方式:
req.url :是客户端请求的 URL 地址。是从请求地址url的端口号后面/开始的
req.method :是客户端的 method 请求类型 如get post等
server.on('request', (req) => {
// req.url 是客户端请求的 URL 地址
const url = req.url
// req.method 是客户端请求的 method 类型。如post get
const method = req.method
// 打印出req.url 和 req.method。其中req.url是从请求地址的端口号/后面开始的
console.log(`your request URL is ${url}, and request method is ${method}`)
})
打开浏览器,输入 127.0.0.1 回车,此时 打印出:your request URL is /, and request method is GET
如果输入 127.0.0.1/index.html ,就打印返回:your request URL is /index.html, and request method is GET
由此可见,req.url 返回的不是完整的请求地址,而是从端口号后面开始的
4.3 响应对象 res
在服务器的 request 事件处理函数中,如果想访问与服务器相关的数据和属性,可以使用如下的方式。
res 是响应对象,它包含了与服务器相关的数据和属性。
server.on('request', (req, res) => {
// res 是响应对象,它包含了与服务器相关的数据和属性。
// 要发送到客户端的字符串
const str = `your request URL is ${req.url}, and request method is ${req.method}`
//【res 响应对象】
// res.end() 方法的作用:响应一些内容
// 向客户端发送指定的内容,并结束这次请求的处理过程。
res.end(str)
})
打开浏览器,输入 127.0.0.1/index.html 回车,此时 页面显示:your request URL is /index.html, and request method is GET
4.4:中文乱码问题
当调用 res.end() 方法,向客户端发送中文内容时,会出现乱码问题,此时,需要手动设置内容的编码格式。
语法:res.setHeader('Content-Type', 'text/html; charset=utf-8')
server.on('request', (req, res) => {
const str = `您请求的 url 地址是${req.url},请求的 method 类型是${req.method}`
// 为了防止中文显示乱码,需设置响应头 Countent-Typr 的值为 text/html; charset=utf-8
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(str)
})
案例:根据不同的 url 响应不同的 html 内容
1、获取请求的url地址。
2、设置默认的响应内容为 404 Not Found。
3、判断用户请求的是否为 / 或 /index.html 首页。
4、判断用户请求的是否为 /about.html 关于页面。
5、设置 Content-Type 响应体,防止中文乱码。
6、使用 res.end() 把内容响应给客户端。
根据不同的 url 响应不同的 html 内容
const http = require('http')
const server = http.createServer()
// 监听web请求
server.on('request', (req, res) => {
// 获取请求的 url 地址
const url = req.url
// 设置默认的响应内容为 404 Not Found
let content = '<h1>404 Not Found. 页面不存在</h1>'
// 判断用户请求的是 / 或 /index.html首页,还是 /about.html关于页面
if(url === '/' || url === '/index.html'){
content = '<h1>欢迎访问首页━(*`∀´*)ノ亻! </h1>'
} else if (url === '/about.html'){
content = '<h1>关于页面</h1>'
}
// 设置响应头,编码格式,以防中文乱码
res.setHeader('Content-Type','text/html; charset=utf-8')
// 使用 res.end() 把内容响应给客户端
res.end(content)
})
// 启动 web 实例服务器
server.listen(80, () => {
console.log("服务器已启动... 端口为80 地址为http://127.0.0.1")
})
案例:打开/18/index.html
服务器充当的角色就是:字符串的搬运工。
/*
* 案例:
* 现有文件夹为'18'。
* 文件夹里面有 index.html 、 index.css、 index.js 文件,
* 在浏览器输入index.html打开网页,请求css js文件资源。
* 服务器充当的角色就是:字符串的搬运工。
* 把文件的实际存放路径作为每个资源的请求 url 地址,也可以把 url 地址当作文件的路径。
*/
// 1. 导入模块
const http = require('http')
const path = require('path')
const fs = require('fs')
// 2. 创建 web 服务器
const server = http.createServer()
// 3. request 监听请求,将资源的请求 url 地址映射为文件的存放路径
server.on('request', (req, res) => {
// 3.1 获取到客户端请求的 url 地址
const url = req.url
// 3.2 把请求的 url 地址,映射为 本地文件的存放路径
//const fPath = path.join(__dirname, url)
/* 5. 优化步骤3.2
* 用户每次输入127.0.0.1/18/index.html 打开首页,很麻烦
* 如果把后面的给自动补全就很方便,判断输入127.0.0.1/就进入首页,也就是请求地址是 / 的。
* 127.0.0.1/ 与 127.0.0.1一样,请求地址都是 / 。判断符合就给path路径补加上首页地址
* 如果不是 / ,比如是index.html,也给它补全首页路径。
* 这样就不用输入那个文件夹了,html引用的外部资源正常请求,无干扰。
*/
// 5.1 预定义空白的文件存放路径
let fPath = ''
if (url === '/') {
// 5.2 如果请求路径为 / 根路径 则手动指定文件的存放路径
fPath = path.join(__dirname, '/18/index.html')
} else {
// 5.3 如果请求路径不为 / 根路径 则加一个/18补全完整的路径
fPath = path.join(__dirname, '/18',url)
}
// 4.1 根据映射过来的文件路径读取文件
fs.readFile(fPath, (err, dataStr) => {
// 4.2 读取失败,向客户端响应错误消息
if(err) return res.end('404 Not Found') + console.log(err.message)
// 4.3 读取成功,将 "读取成功的内容" 响应给客户端
console.log("文件读取成功!正在处理...")
res.end(dataStr)
})
})
// 启动服务器
server.listen(80, () => {
console.log("服务器已启动...地址:http:/127.0.0.1:80")
})
5. 模块化
模块化 是只解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块化是可组合、分解和更换的单元。
在编程领域中模块化,就是遵循固定的规则,把一个大文件拆成独立并互相依赖的多个小模块。
优点:提高了代码的复用性、提高了代码的可维护性、可以实现按需加载。
模块化规范:对代码进行模块化的拆分与组合时,需要遵守的那些规则。大家都遵守同样的模块化规则写代码,方便了各个模块之间的互相调用。
Node.js 中模块化的分类:
内置模块:由 node.js 官方提供的,比如 fs、path、http等
自定义模块:用户创建的每个 .js 文件,都是自定义模块
第三方模块:由第三方开发出来的模块,并非官方提供的内置模块,使用前需要下载。
5.1 加载模块 require()
使用 require() 方法加载其他模块时,会执行被加载模块中的代码。
注意:使用 require() 加载自定义模块期间,可以省略 .js 的后缀名。
// 1. 加载内置的 fs 模块
const fs = require('fs')
// 2. 加载自定义模块
const myJs = require('./a/myJs.js')
const myJs = require('./a/myJs') // 使用 require() 加载期间,可以省略 .js 的后缀名
// 3. 加载第三方模块
const moment = require('moment')
5.2 模块作用域
和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块內被访问,这种模块级别的访问限制,叫做 模块作用域。
访问不了它的属性,只能让它干活。
模块作用域的好处:防止了全局全局变量污染的问题。
example :有两个js文件,第一个是调用第二个,第二个是被调用的。
const uName = '加菲猫'
function sayHi() {
console.log(`我的名字叫:${uName}`)
}
// 加载自定义模块,导入文件
const myjs = require('./1_myJs')
console.log(myjs) // 为空对象 {}
5.3 module 对象 (向外共享模块作用域中的成员
每一个自定义模块 .js 中都有一个 module 对象,它里面存储了和当前模块有关的信息。
console.log(module)
显示为空对象 {}
5.4 module.exports 对象 将私有成员共享
在自定义模块中,默认情况下,module.exports = {} 空对象。
在自定义模块中,可以使用 module.exports 对象,将模块內的成员共享出去,供外界使用。
外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象。
自定义模块:
// 在一个自定义模块中,默认情况下,module.exports = {} 空对象。
// 向 module.exports 对象上挂载 userName 属性
module.exports.userName = '黑猫'
// 向 module.exports 对象上挂载 sayHi 方法
module.exports.sayHi = function() {
console.log("你好!"+ userName)
}
// 把私有成员通过module.exports暴露出去,让外界访问
const age = 20
module.exports.age = age
调用自定义模块:
// 在外界使用 require 导入一个自定义的模块,得到的成员,
// 就是该模块中通过 module.exports 指向的那个对象
// 使用require() 方法导入模块时,导入的结果,永远以 module.exports 指定的对象为准。
const n = require('./22_模块化_module.exports对象')
console.log(n)
打印出的不是空对象,是: { userName: '黑猫', sayHi: [Function (anonymous)] }
如果在自定义模块里最下方,让 module.exports 指向一个全新的对象,就不再指向旧对象了,打印结果是最新的对象:
// 让 module.exports 指向一个全新的对象
module.exports = {
uName: '加菲猫',
sayHi: function() {
console.log('Hi ,')
}
}
// 打印结果为:{ uName: '加菲猫', sayHi: [Function: sayHi] }
5.5 exports 对象 将私有成员共享
由于 module.exports 写起来复杂,为了简化,Node 提供了 exports 对象。默认情况下和 module.exports 指向同一个对象。最终共享的结果,还是以 module.exports 指向的对象为准。
console.log(module.exports) // {}
console.log(exports) // {}
console.log(module.exports === exports) // true
const uName = '加菲猫' // 定义模块私有成员 uName
module.exports.uName = uName // 将 私有成员 共享出去
exports.uName2 = '欧迪' // 挂载新成员
exports.sayHi = function() { // 挂载方法
console.log('你好')
}
// 打印结果:{ uName: '加菲猫', uName2: '欧迪', sayHi: [Function (anonymous)] }
// 最终,向外界共享的结果,永远都 module.exports 所指向的对象
5.6 module.exports 与 exports 的误区
require() 一个模块时,得到的永远是 module.exports 指向的对象。
exports 永远以 module.exports 为准。
给 module.ports 指向新对象:
// require() 一个模块时,得到的永远是 module.exports 指向的对象。
// exports 永远以 module.exports 为准。
exports.userName = '加菲猫'
module.exports = {
like: '千层面'
}
// 最终结果:{ like: '千层面' }
// 首先,exports 对象指向了 userName = '加菲猫'
// 然后,module.exports 指向了新对象 like: '千层面'
// 因为 exports 永远是 module.exports 指向的对象,以它为准,所以,结果为 { like: '千层面' }
给 exports 指向新对象:
module.exports.userName2 = '加菲猫'
exports = {
like2: '披萨'
}
// 最终结果:{ userName2: '加菲猫' }
// 更改了 exports 的指向,但不会影响到 module.exports 的指向。
// 所以,exports 永远以 module.exports 为准。
分别给 exports、module.exports 添加属性:
exports.uname = '黑猫'
module.exports.like = '烤鱼'
// 最终结果:{ uname: '黑猫', like: '烤鱼' }
// 没有开辟新对象,就是没有创建新对象,只是在同一个对象中添加属性,挂载一些属性
exports 指向对象,赋值给 module.exports,再添加属性:
exports = {
username: '咖啡猫'
}
module.exports = exports
module.exports.age = '3'
// 最终结果:{ username: '咖啡猫', age: '3' }
// exports 指向一个对象,然后赋值给 module.exports,此时,module.exports 也指向了这个对象。
// 它俩指向同一个对象,然后再添加新属性。
// 浅拷贝,把 对象 的地址传给 exports,然后又传给了 module.exports。
5.7 CommonJS 模块化规范
6. npm 与包
6.1 npm i - 安裝包
例如:
在官网上找到一个想安装的包,https://www.npmjs.com/search?q=moment,知道包名就可以安装了。
拿 moment 这个格式化时间的包举例,下载,为:npm i moment ,然后本地就下载好了包文件,如图。
可以在当前项目文件夹內调用导入 moment 包。
安装完包后,多了 node_modules 文件夹和 package-look.json 文件。
其中,node_modules 里面存放着所有已安装到项目的包。require() 方法导入第三方包时,就是从这个目录中查找并加载。
package-look.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包名字、版本号、下载地址等。
-安装指定版本的包 @
npm i xxxx@2.22.2
使用 npm install 命令安装包的时候,会自动安装最新版本的包,如需安装指定版本,在 @ 后面加上版本号即可。
-包的语义化版本规范
第一位数字为:大版本。假如从底层重新设计更新,就是换代,加1。
第二位数字为:功能版本。更新了某些功能,就版本加1.
第三位数字为:Bug修复版本。更新修复了某些bug,再发布版本加1。
只要前面的版本号更新了,后面的版本号归零。
6.2 包管理配置文件
npm i 包名 -D
//简写形式。安装指定的包,并记录到 DevDependencies 节点中npm install 包名 --save-dev
//完整写法6.3 下载慢的问题
1、查看当前的下包镜像源
npm config get registry
2、更换下包镜像源
npm config set registry=https://registry.npmmirror.com/
3、nrm 工具
npm i nrm -g
nrm ls
nrm use 名称
nrm test
6.4 包的分类
1. 项目包
安装到项目的 node_modules 目录里的包,都是项目包。
开发依赖包:记录到 devDependencies 节点中的包,只在开发期间用。语法: npm i 包名 -D
核心依赖包:记录到 dependencies 节点中的包,在开发和上线都用的到。语法: npm i 包名
2. 全局包
在执行 npm install 命令时,带上参数 -g,就把包安装为全局包。
全局包安装到:C:\Users\用户\AppData\Roaming\npm\node_modules 目录下
安装全局包:npm i 包名 -g
卸载全局包:npm uninstall 包名 -g
注意,只有工具性质的包,才有全局安装的必要性,因为它们提供了一些好用的终端命令。比如 nrm。是否需要全局安装具体看包的使用文档。
6.5 规范的包结构
包以单独的目录而存在。
包的顶级目录下必须包含 package.json 包管理配置文件。
package.json 中必须包含 name , version , main 这三个属性,分别为:包名字、版本号、包的入口(例如某某文件夹下的某 index.js 文件)
6.6 制作一个包
{
"name": "包名", //包名
"version": "1.0.0", //版本号
"main": "./index.js", //包的入口文件
"description": "包的简短描述,在搜索时显示", //简短描述信息
"keywords": ["关键字", "关键字"], //搜索关键字
"license": "ISC" //开源协议
}
注意:
当 require() 导入一个模块时,如果该模块所在的目录內有 package.json 配置文件,然后配置文件有 main 属性指定了入口文件,此时,可以直接写导入该文件夹,照样正常运行。
例如, require('./aaa/b.js') ,这个 aaa 目录就是该模块,aaa 目录下有 package.json 文件,里面的 main 属性指定了入口文件,此时,就可以写成: require(./aaa')。
这个执行过程是,require 执行后,先去指定的文件夹內,然后没有指定具体的 js 文件,它就去找 package.json 文件,根据里面的 main 属性来找到入口文件,执行导入该 js 文件模块。
案例:制作一个 转移特殊符合的包
index.js
function chuli(str) {
//正则匹配 g是全部都匹配,匹配多次,也就是【循环替换】了
return str.replace(/<|>|"|&/g, (nnn) => {
switch (nnn) {
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
// 把所有 ><:& 都改成了转义字符,以免页面受影响
console.log( chuli('<h1>这是h1标签</h1>') )
// 向外暴露需要的成员
module.exports = {
chuli ,
}
package.json 文件
{
"name": "Zhuanyi", //包名
"version": "1.0.0", //版本号
"main": "./index.js", //包的入口文件
"description": "包的简短描述:转移处理 ><:& ", //简短描述信息
"keywords": ["转移", ">"], //搜索关键字
"license": "ISC" //开源协议
}
README.md 说明文档
# 说明文档
这是说明文档
6.7 发布包、删除包
1、先去官网 http://www.npmjs.com/ ,注册账号。
2、然后在本地终端执行 npm login
命令,按照提示输入用户名、密码、邮箱,完成登录。注意服务器必须为 npm 官方服务器!
3、切换到 包的根目录,执行 npm publish
命令,即可发布到 npm 上。注意包名不能与别人重名!
4、删除已发布的包,运行 npm unpublish 包名 --force
命令,即可删除发布在 npm 的包。
注意:此命令只能删除72小时內发布的包,被此命令删除的包在24小时內不允许重新发布,不要发无意义的包。
7. 模块的加载机制
7.1 优先从缓存中加载
模块在第一次加载后会被缓存,多次调用 require() 不会多次执行模块里的代码。
内置模块、用户自定义模块、第三方模块,它们都会优先从缓存中加载,从而提高加载效率。
7.2 内置模块 的加载机制
内置模块的加载优先级最高。
例如,require('fs') 始终返回内置的 fs 模块,即使在 node_modules 目录下有名字相同的名为 fs 的包。
7.3 自定义模块 的加载机制
使用 require() 加载自定义模块时,必须以 ./ 或 ../ 开头的路径标识符。如果没有这两个路径标识符,则 node 会把它当作 内置模块或第三方模块 进行加载。
require('./index') // 加载自定义模块 index.js
require('index') // 加载内置模块或第三方模块 index
在导入自定义模块时,如果省略文件扩展名,则 node.js 的加载顺序是:
1、按照确切的文件名进行加载
2、补全 .js 扩展名进行加载
3、补全 .json 扩展名进行加载
4、补全 .node 扩展名进行加载
5、加载失败。
也就是 require('./a'),加载的顺序是: a a.js a.json a.node 报错
7.4 第三方模块 的加载机制
如果传递给 require() 的模块标识符不是内置模块,也没有以 ./ ../ 开头,则 node.js 会从当前模块的父级目录开始,从 /node_modules 文件夹中加载第三方模块。
如果没有找到,则向上一级父目录找,直到文件系统根目录。
// 例如:在 d:\a\b\my.js 调用了 tools 模块,require('tools')
d:\a\b\node_modules\tools
d:\a\node_modules\tools
d:\node_modules\tools
7.5 目录 作为模块
传递给 require() 的是一个目录时,有三种加载方式。
1、在被加载的目录下查找一个叫 package.json 的文件,并寻找 main 属性,作为 require() 的加载入口。
2、如果目录里没有 package.json 文件,或没有 main 入口或无法解析,则 node.js 会试图加载目录下的 index.js 文件
3、以上两步都失败了,则就失败,并报错模块缺失。