【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 是响应对象,它包含了与服务器相关的数据和属性。

res.end() :向客户端响应一些内容
向客户端发送指定的内容,并结束这次请求的处理过程。
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)       // 为空对象  {}
执行第二个js文件,结果为空 {}

 

5.3 module 对象  (向外共享模块作用域中的成员

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

console.log(module)

显示为空对象 {}

5.4 module.exports 对象      将私有成员共享

在自定义模块中,默认情况下,module.exports = {}  空对象

在自定义模块中,可以使用 module.exports 对象,将模块內的成员共享出去,供外界使用。

外界用 require() 方法导入自定义模块时,得到的就是 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 模块化规范

CommonJS规定:
 
1. 每个模块内部,module 变量代表当前模块
2. module 变量是一个对象,它的 exports 属性( module.exports)是对外的接口
3. 加载某个模块,其实是加载该模块的 module.exports 属性。require() 方法用于加载模块。

 

6. npm 与包

1、什么是包?
Node.js 中的第三方模块 又叫做包。
第三方模块和包指的是同一个概念,只是叫法不同。

2、包的来源
不同于 Node.js 中内置模块与自定义模块,包是由第三方团队或个人开发的,供所有人使用,是开源的,免费下载使用。

3、为什么需要包
由于 Node.js 的内置模块仅提供了一些底层的 API,导致在基于内置模块进行开发时,效率很低。
包是基于内置模块封装出来的,提供了更高级、更方便的 API,极大提高了开发效率。
包和内置模块之间的关系,类似于 jQuery 和 浏览器内置 API 之间的关系。

4、从哪里下载包
www.npmjs.com,是全球最大的包共享平台,可以在此网站上搜索任何需要的包。
npm,Inc.公司提供了为 registry.npmjs.org 的服务器,对外共享所有的包,可以在此下载包。

5、如何下载包
npm,Inc. 公司提供了一个包管理工具,使用这个包管理工具,可以从服务器上下载包。
包管理工具(Node Package Manager)简称 npm 包管理工具。
例如查看当前 Node.js 版本,在终端使用命令: npm -v

6.1 npm i -   安裝包

在项目中安装包的命令:npm install 包的完整名称      或者简写: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 规定,在项目根目录中,必须提供一个叫做 package.json 的包管理配置文件。
用来记录一些跟项目有关的配置信息,例如项目名称、版本号、项目有哪些包、哪些只在开发期间用、哪些在开发和部署时都用。

1、多人协作的问题
共享时剔除 node_modules 文件夹,不用共享包文件夹。

2、记录项目中安装了哪些包
在项目根目录中,创建一个 package.json 配置文件,用来记录了项目中安装了哪些包。
在安装包时 npm i xxx,会自动创建一个 package.json 文件。

注意:今后项目开发中,要把 node_modules 文件夹添加到 .gitigone 忽略文件中。

3、快速创建 package.json
npm 包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,创建一个 package.json 包管理配置文件。
npm init -y         // 创建 package.json 配置文件

注意:目录路径不能带中文字符,不能带空格,一定要用全英文命名。
运行 npm install 命令安装包时,npm 包管理工具会自动把 包名称和版本号 ,记录到 package.json 中。

4、dependencies 节点
在 package.json 文件中,有一个 dependencies 节点,专门记录使用 npm install 安装了哪些包。

5、一次性安装所有的包 npm install
npm install 命令(或 npm i)会一次性安装所有的依赖包。

执行 npm install 命令时,npm 包管理工具会先读取 package.json 中的 dependencies 节点。
读取到记录的所有包名称和版本号之后,npm 会把这些包一次性下载到项目中。

6、卸载包 npm uninstall
npm uninstall 命令,可以卸载指定的包。
例如:npm uninstall moment
执行命令后,会把卸载的包,从 package.json 的 dependencies 中移除掉。

7、devDependencies 节点
如果某些包只在项目开发阶段中用到,在项目上线之后不会用到,建议把这些包记录到 devDependencies 节点中。
与之对应的,某些包在开发和上线都需要用到,则记录到 dependencies 节点中。
语法:
npm i 包名 -D                         //简写形式。安装指定的包,并记录到 DevDependencies 节点中
npm install 包名 --save-dev   //完整写法

其中,-D 可以写到包名的前面。

6.3 下载慢的问题

在国内下载外国资源会很慢,切换成国内的镜像服务器就变快了

1、查看当前的下包镜像源

npm config get registry

显示:https://registry.npmjs.org/   ,这是官方服务器

2、更换下包镜像源

例如:淘宝镜像源 npm config set registry=https://registry.npmmirror.com/

3、nrm 工具

源的管理工具,可以用来方便的切换 npm 源,https://www.npmjs.com/package/nrm
在 cmd 运行终端
通过 npm 将 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 制作一个包

发布包,首先创建一个文件夹,里面需要有:自己的 js 文件、说明文档 README.md 文件、还有在包里面创建 package.json 文件,里面配置有:
{
    "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 '&lt;'
            case '>':
                return '&gt;'
            case '"':
                return '&quot;'
            case '&':
                return '&amp;'
        }
    })
}
// 把所有 ><:& 都改成了转义字符,以免页面受影响
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、以上两步都失败了,则就失败,并报错模块缺失。

 

posted @ 2023-03-02 00:08  nekmea  阅读(23)  评论(0编辑  收藏  举报