Node.js学习第二天,模块化,npm与包

模块化的基本概念

什么是模块化

模块化是指解决一个复杂问题时 , 自顶向下 逐层把系统划分成若干模块的过程 , 对于整个系统来说,模块是可组合,分解和更换的单元

编程中的模块化

遵守固定的规则, 把一个大文件 拆分成独立并互相依赖的多个小模块

优点

  • 提高了代码的重用性
  • 提高了代码的可维护性
  • 可以实现按需加载

模块化规范

模块化规范就是对代码进行模块化拆分与组合时, 需要遵守的那些规则

例如

  • 使用什么样的语法格式来引用模块
  • 在模块中使用什么样的语法格式向外暴露成员

**模块化规范的好处: ** 大家都遵守同样的模块化规范写代码 , 降低了沟通的成本 ,极大方便了各个模块之间的相互调用

Node.js中的模块化

Node.js中模块的分类

Node.js中根据模块的来源不同 , 将模块分为了3大类

  • 内置模块 由Node.js官方提供的 , 例如 fs , path , http等,在安装Node.js时就已经将这些模块安装到本地了
  • 自定义模块 用户创建的每个.js文件, 都是自定义模块 ;
  • 第三方模块 第三方开发出来的模板 , 并非官方提供的内置模板 , 也不是用户创建的自定义模板,使用前需要先下载

加载模块

使用强大的require()方法,可以加载需要的内置模块,用户自定义模块, 第三方模块进行使用

// 1. 加载内置模块
const fs = require('fs')

//2. 加载用户自定义模块
// 给出本地路径
const custom = require('./custom.js')

//3.加载第三方模块
const moment = require('moment')

注意 : 当使用require()方法加载其他模块时, 会执行被加载模块中的代码

const custom = require('./myModule.js')
//在加载用户自定义模块时, 可以省略.js后缀名
//效果相同
const custom = require('./myModule')
console.log(custom)

Node.js中的模块作用域

什么是模块作用域

函数作用域 类似, 在自定义模块中定义的变量 , 方法等成员 , 只能在当前的模块中访问 , 这种模块级别的访问限制,叫做模块的作用域

模块作用域的好处

防止 了全局变量污染的问题

例如:

在浏览器中, 没有模块作用域的概念

我导入first.js 模块, 里面的username=“张三”;

然后我又导入second.js模块,里面有username=“李四”

这时候,我打印console.log(username) 结果为"李四" ,

first.js中的username被污染

向外共享模块作用域中的成员

module对象

在每一个.js自定义模块中都有一个module对象, 他存储了当前模块有关的信息

console.log(module)
/*
Module {
  id: '.',
  path: 'D:\\test\\node',
  exports: {},
  filename: 'D:\\test\\node\\myModule.js',
  loaded: false,
  children: [],
  paths: [
    'D:\\test\\node\\node_modules',
    'D:\\test\\node_modules',
    'D:\\node_modules'
  ]
}
*/

module.exports 对象

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

外界使用requir()方法导入自定义模块时, 得到的就是module.exports所指的对象

在一个自定义模块中, module.exports默认为空

// 当前文件就是用户自定义模块
const username = '张三'
function sayHello(){
    console.log(`大家好,我是${username}`)
}
const age = 20
// console.log(module)

// 向module.exports对象上挂载属性
module.exports.username='赵6'
module.exports.myfunction=function(){
    console.log('我是傻逼')
}
module.exports.sayHello=sayHello
module.exports.age=age

注意点 : 使用require() 方法导入模块时, 永远以module.exports指向的最后一个对象为准

exports对象

由于module.exports单词写起来比较复杂, 为了简化共享成员的代码,Node提供了exports对象,默认情况下 , exportsmodule.exports指向同一个对象 , 最终的共享结果 , 还是以module.exports指向的对象为准

只不过exports写起来更加方便

console.log(exports === module.exports)
//结果为true
const username = '张三'
exports.username = username
exports.sayHello = function(){
    console.log('大家好')
}

exports和module.exports使用误区

exports.username='张三'
module.exports ={
    gender: 'boy',
    age : 22
}

//在另一个模块中,导入该模块
const m = require('')
console.log(m)
//结果为
{gender: 'boy' , age:22}

原理: 原来exportsmodule.exports指向同一个对象 , 然后又在内存中开辟了一个新的对象, 让module.exports对象指向这个新对象 , 由于require()导入的最终结果以module.exports为准, 所以就会出现这种情况

image-20220426120723396

image-20220426121127924

注意 : 为了防止混乱, 建议大家不要在同一个模块中同时使用exportsmodule.exports

CommonJS 规范

Node.js 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间如何相互依赖。

CommonJS规定

  • 每个模块内部 , module变量 代表当前模块
  • module 变量是一个对象 , 他的exports 属性(即 module.exports) 是对外接口
  • 加载整个模块 , 其实就是加载该模块的module.exports属性 , require()用于加载模块

npm与包

Node.js中的第三方模块 又叫做

不同于Node.js中的内置模块与自定义模块 , 包由第三方个人或者团队开发出来, 免费提供给所有人使用

注意: Node.js中的包都是免费且开源的 , 不需要付费即可免费下载使用

如何下载包

国外有一家 IT 公司,叫做 npm, Inc. 这家公司旗下有一个非常著名的网站: https://www.npmjs.com/ ,它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包.

npm, Inc. 公司提供了一个地址为 https://registry.npmjs.org/ 的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包。

注意:

  • 从 https://www.npmjs.com/ 网站上搜索自己所需要的包
  • 从 https://registry.npmjs.org/ 服务器上下载自己需要的包

npm, Inc. 公司提供了一个包管理工具,我们可以使用这个包管理工具,从 https://registry.npmjs.org/ 服务器把需要的包下载到本地使用。

这个包管理工具的名字叫做 Node Package Manager(简称 npm 包管理工具),这个包管理工具随着 Node.js 的安装包一起被安装到了用户的电脑上。

大家可以在终端中执行 npm -v 命令,来查看自己电脑上所安装的 npm 包管理工具的版本号:

npm -v

当我们需要安装某个包时,执行这条命令即可

npm install 包名;
可以简写为
npm i 包名

当然我们也可以安装指定版本的包

npm i 包名@版本号
例如
npm i moment@2.22.2

初次下载

初次下载完包之后, 在项目文件夹下多了一个叫做node_modules的文件夹和package-lock.json的配置文件

  • node_modules文件夹用来存放已安装项目中的包 , require()导入第三方包时, 就是从这个目录中查找并加载包
  • package-lock.json配置文件用来记录node_modules目录下的每一个包的下载信息,例如包的名字 , 版本号 ,下载地址等

注意 : 我们不要手动修改node_modulespackage-lock.json文件中的代码 , npm包管理器会自动去维护他们

包管理配置文件

npm规定 , 在项目根目录中 , 必须提供一个package.json的包管理配置文件 , 用来记录与项目有关的一些配置文件, 例如

  • 项目的名称 , 版本号 , 描述等
  • 项目中都用到了那些包
  • 哪些包只在开发期间用
  • 哪些包在开发和部署时都需要用到

多人协作问题

例如 , 我们需要多人协作时 ,通常将项目上传到Github上,但是有一个问题:

整个项目体积30M , 第三方包体积是 28M , 源代码只有2M

第三方包的体积过大 , 不方便上传和下载

解决方案就是: 共享时剔除node_modules文件夹 , 别人下载源代码后 , 只需要在本地安装包即可

记录项目中安装了哪些包

我们剔除了node_modules文件夹 , 怎么知道项目中用到了哪些包呢?

在项目根目录下,创建一个叫做package.json的配置文件 , 即可用来记录项目中用到了哪些包

快速创建pack.json

npm包管理工具提供了一个快捷命令 , 可以在执行命令所处的目录中 , 快速创建package.json这个包管理配置文件
初始化一个node项目

在执行命令所处的根目录中, 快速新建 package.json文件
npm init -y

注意

  • 上述命令只能在英文目录下成功运行 , 所以项目文件夹的名称 一定要使用英文命令 ,不要使用中文,不能出现空格
  • 运行npm install 包 安装包时, npm包管理工具会自动包的名称版本号 , 记录到package.json

dependencies节点

package.json文件中 , 有一个dependencies节点 , 专门用来记录npm install命令安装了哪些包

image-20220426183221504

一次性安装所有的包

当我们拿到一个剔除了node_modules的项目后 , 需要先把所有的包下载到文件中 ,才能将项目运行起来否则会报错

image-20220426183331101

可以通过命令把这个项目中要用到的包一次性安装下来

npm install
或 简写
npm i

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

卸载包

可以运行指定的命令 , 来卸载指定的包

npm uninstall 包名
例如
npm uninstall moment

注意 : npm uninstall 命令执行成功后 , 会把卸载的包 ,从package.json中的dependencies中移除掉

devDependencies节点

如果某些包只在项目开发阶段会用到 , 在项目上线后不会用到 , 则建议把这些包记录到devDependencies节点中 , 例如 webpack工具 , 与之对应的 , 如果某些包在项目开发和上线后都需要用到 , 则建议把这些包记录到dependencies节点中

可以执行如下命令 , 将包记录到devDependencies节点中

npm install 包名 --save-dev
或简写为
npm i 包名 -D

包下载慢的问题

在使用npm包管理工具下载的时候 , 默认从国外的的 https://registry.npmjs.org/ 服务器进行下载,所以会很慢

我们可以修改npm的下载路径 , 使其从国内的镜像服务器上下载 , 这样就解决了包下载慢的问题

我们可以从淘宝NPM镜像服务器 上下载 , 淘宝的镜像服务器每隔一段时间会自动把npm服务器的包同步到自己的服务器上 , 对国内用于提供下载服务

//查看当前的下载包路径
npm config get registry
//默认为https://registry.npmjs.org/
//将包下载路径切换为淘宝镜像服务器
npm config set registry=https://registry.npm.taobao.org/
//检查镜像源是否修改成成功
npm config get registry

nrm

当我们使用上面的命令来切换npm下载镜像源 时 , 非常麻烦 , 我们可以使用一个小工具nrm

//将nrm 安装为全局可用的工具
npm i nrm -g
//查看所有的镜像源
nrm ls
//将下载包的镜像源切换为tabo
nrm use taobao

image-20220426190523893

包的分类

使用npm包管理工具下载的包 , 共分为 两大类

  • 项目包
  • 全局包

项目包

那些被安装到项目的node_modules目录中的包 ,都是项目包

项目包又分为两类

  • 开发依赖包 , 被记录到devDependencies节点中的包 , 只在开发期间会用到
  • 核心依赖包 , 被记录到dependencies节点中的包 , 在开发和项目上线之后都会用到
npm i 包名 -D 安装开发依赖包
npm i 包名  核心依赖包

全局包

在执行npm install 命令时 , 如果提供了-g 参数 ,则会把包安装为全局包

全局包会被安装到C:\Users\用户目录\AppData\Roaming\npm\node_modules目录下

npm i 包名 -g 全局安装的包
npm uninstall 包名 -g   卸载全局包

注意

  • 只有工具性质的包 , 才有全局安装的必要性 ,因为他们提供了好用的终端命令
  • 判断某个包是否需要全局安装才能使用 , 可以参考官方提供的使用说明

i5ting_toc

推荐一个好用的小工具

i5ting_toc是一个可以把md文档转换为html页面的小工具

//将i5ting_toc安装为全局包
npm install -g i5ting_toc
//调用i5ting_toc , 轻松实现 md 转 html 的功能
i5ting_toc -f 要转换的md文件路径 -o

包的规范

深入了解一下包的结构

一个规范 的包, 他的组成结构,必须符合以下3点要求

  • 包必须以单独的目录 而存在
  • 包的顶级目录下要必须包含package.json这个包管理配置文件
  • package.json中必须包含name , version , main 这三个属性 ,分别代表包的名字,版本号 ,包的入口
  • main属性用来指明包的入口文件, 即require()需要引入的真正对象

模块加载机制

优先从缓存中加载

模块在第一次加载后会被缓存 , 这也就意味着多次调用require()引入统一模块不会导致模块内的代码被执行多次

注意 : 不论是内置模块 , 用户自定义模块 , 还是第三方模块 ,他们都会优先从缓存中加载 , 从而提供模块的加载效率

内置模块的加载机制

内置模块是由Node.js官方提供的模块 , 内置模块的加载优先级最高

例如在node_moudles目录下有一个我自己创建的 fs模块
我现在引入 fs模块
require('fs')
实际引入的是系统内置模块的fs

自定义模块的加载机制

使用require() 加载自定义模块时 , 必须指定./../开头的 路径标识符 , 如果没有指定./../这样的路径标识符 , 则运行时会把他当做内置模块第三方模块进行加载

我们知道 , 在导入自定义模块时, 是可以省略.js后缀名的 , 其实真正的原理是这样的

  1. 按照确切的文件名进行加载
  2. 如果上一步 行不通 , 则 补全 .js拓展名进行加载
  3. 如果上一步行不通 , 则补全.json拓展名进行加载
  4. 如果上一步行不通 , 则补全.node拓展名进行加载
  5. 最终还是不行 , 则加载失败 , 报错

第三方模块的加载机制

如果传递给require()的不是一个内置模块 , 也不是./../开头的标识符, 则系统会判定为第三方模块 , 尝试从 /node_modules文件夹中加载第三方模块

如果在当前项目的node_modules文件夹中没有找到该模块 , 则开始逐层向上寻找 , 直到文件系统的根目录

image-20220427185033245

目录作为模块

当我们给require() 传递的标识符是一个目录时 , 系统也会去加载 , 有三种加载方式

  • 在被加载目录下查找一个叫做package.json的文件 , 并寻找main属性 , 作为require()加载的入口
  • 如果在目录中没有package.json文件, 或者main入口不存在或无法解析 , 则Node.js将会视图加载目录下的index.js文件
  • 如果上两步都失败了 , 则Node.js会报错

开发属于自己的包

  1. 新建一个项目文件夹 , 作为包的根目录

  2. 在包的根目录下 , 创建如下三个文件

    • package.json 包管理配置文件
    • index.js 包的入口文件
    • README.md 包的说明文档
  3. 初始化package.json配置文件 , 必须包含以下几个属性,不能少

    image-20220427185615214

  4. 编写README.md包的说明文档, 包括: 包的安装方式 , 导入方式 , 包内的方法和属性的使用说明 ,开源协议

发布包

  1. 注册npm账号

  2. 在终端登录npm账号(在运行npm login命令之前, 必须保证包的下载地址为官方 , 可以使用nrm修改下载地址,可以看前面的修改下载地址的文字)

    //执行
    npm login
    //然后依次输入账户名和密码即可
    
  3. cd到包的根目录下 , 运行npm publish命令 , 即可发布到npm官网上 (注意: 包名不能雷同 , 可以去官网去搜索有没有同名的包)

  4. 发布包的时候要慎重 , 尽量不要去发布没有意义的包

删除已发布的包

在终端命令行登录后

运行命令npm unpublish 包名 --force , 即可删除已发布的包

注意:

  • npm unpublish命令只能删除72小时以内发布的包
  • npm unpublish删除的包 , 在24小时内不允许再次发布

声明

此篇文章根据 黑马程序员Node.js教学视频及相关课件 整理而来

posted @ 2022-11-13 22:27  秋天Code  阅读(41)  评论(0)    收藏  举报