Node.js——模块化(模块的基本概念、模块化的规范、包与NPM)
目录
模块的基本概念
模块的定义
模块化的优点
Node.js 模块的分类
模块的加载
模块的作用域
向外共享模块作用域内的成员
模块化的规范
CommonJS
AMD
CMD
ES6 Module
包与NPM
包的定义
包
NPM 包管理器
模块的基本概念
模块的定义
- 什么是模块?
模块通常是指编程语言所提供的代码组织机制,利用此机制可将程序拆解为独立的、可分解,可更换的代码单元。
- 什么是模块化?
模块化是一种设计思想,利用模块化可以把一个非常复杂的系统结构细化到具体的功能点,每个功能点看作一个模块,然后通过某种规则把这些小的模块组合到一起,构成模块化系统。
模块化的优点
- 提高了代码复用性
- 提高了代码可维护性
- 实现了代码按需加载
Node.js 模块的分类
Node.js 根据模块的来源不同, 将模块分为了3大类:
- 内置模块 或者叫 核心(原生)模块: 由Node.js官方提供(如fs、http、net), 拥有最高的加载优先级.
- 自定义模块:用户创建的js文件都是自定义模块.
- 第三方模块: 由第三方开发, 并不是官方提供的内置模块, 使用前需要提前下载.
模块的加载
- 使用require()方法加载模块, 可以加载内置模块,自定义模块,第三方模块.
例如:
//加载 内置模块
const fs = require('fs');
//加载 自定义模块
const coconut = require('./coconut.js'); //路径可以省略文件的后缀名js
//加载 第三方模块
const timestamp = require('time-stamp');
- 使用require()方法加载其它模块时,会执行加载模块中的代码.
coconut.js
主文件
运行得到结果
模块的作用域
和函数作用域类似, 在自定义模块中定义的变量, 方法等成员, 只能在当前模块内被访问, 这种模块级别的访问权限叫做模块作用域.
向外共享模块作用域内的成员
module对象
在每个.js 自定义模块中都有一个module对象,里面存储了当前模块有关的信息. 例如:
- 在自定义模块中, 可以使用module.exports对象将模块内的成员导出, 让外界使用.
- 外界用require()方法导入自定义模块时, 得到的便是module.exports所指向的对象.
注意:
- 使用require()方法导入模块时, 导入的结果,永远以module.exports指向的对象为准.
- 由于module.exports单词写起来不够简练, 为了简化代码, Node提供了exports对象. 默认情况下, exports和module.exports 指向的为同一个对象. 最终共享的结果, 还是以module.exports 为主. (二者可能会因为指针地址指向的问题而产生错误)
使用require()方法导入模块时, 导入的结果,永远以module.exports指向的对象为准. 2.为了防止混乱, 建议不要在同一个模块中同时使用exports和module.exports.
模块化的规范
我们看JavaScript三种模块规范:
- CommonJS:主要用于规范服务器端的编程,Node.js就是参照该规范实现
- AMD:Asynchronous Module Definition 异步模块定义,使用异步方式加载模块
- CMD:Common Module Definition 通用模块定义,与AMD类似,但是在模块定义的方式和加载的时机上有所不同
CommonJS
Node.js 遵循了CommonJS模块化规范, CommonJS规定了模块的特性和各模块之间如何相互依赖.
CommonJS规定:
- 每个模块内部, module 变量代表了当前模块.
- module 变量为一个对象,它的exports属性 (即module.exports) 为对外接口.
- 加载模块就是加载模块的module.exports属性. require()方法用于加载模块.
加载模块是同步的,只有加载完成后才能执行后面的操作,也就是当要用到该模块了,现加载现用,不仅加载速度慢,而且还会导致性能、可用性、调试和跨域访问等问题。
Node.js主要用于服务器编程,加载的模块文件一般都存在本地硬盘,加载起来比较快,不用考虑异步加载的方式,因此,CommonJS规范比较适用。然而,这并不适合在浏览器环境,同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD CMD解决方案。
AMD
AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
这里介绍用require.js实现AMD规范的模块化:
- 在html文件导入require.js
- 用define()定义模块
- 用require()加载模块。
define()函数
语法:
define([module-name?], [array-of-dependencies?], [module-factory-or-object]);
- module-name:字符串,模块名称(可选). 如果省略第一个参数,这样就定义了一个匿名模块,这时候模块文件的文件名就是模块标识。
- array-of-dependencies: 是我们要载入的依赖模块(可选),使用相对路径。注意是数组格式
- module-factory-or-object: 工厂方法或者一个对象(必选).
当define函数执行时,它首先会异步的去调用第二个参数中列出的依赖模块,并且还要当做形参传到factory中,对于依赖的模块提前执行,依赖前置。
也就是:需要先加载完毕所有的依赖模块, 然后才能运行你定义的模块.
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。 如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中.
require()函数
require()语句加载模块,但是不同于CommonJS,它要求两个参数.
语法: require([module], callback);
- 第一个参数[module],是一个数组,里面的成员为字符串类型, 也就是要加载的模块名;
- 第二个参数callback,则是加载成功之后的回调函数。注意要有参数来接收模块共享的成员.
CMD
CMD (Common Module Definition), 是seajs推崇的规范,CMD则是依赖就近,用的时候再require.
CMD与AMD一样,也是采用特定的define()函数来定义,用require()方式来引用模块
ES6 Module
ES6标准发布后,module成为标准,标准使用是以export指令导出接口,以import引入模块.
但是在我们一贯的node模块中,我们依然采用的是CommonJS规范,使用require引入模块,使用module.exports导出接口。
在 Node.js 环境中默认使用的是 CommonJS 规范。需要使用 require 语句进行导入。import 是 ES6 中的模块化写法,CommonJS 模块与 ES6 模块不兼容.
包与NPM
包的定义
Node.js中的包
包是包含js文件和其他附加信息的整体,某种意义上来说,包是模块的集合, 也就是我们说的第三方模块.
模块是按照CommonJS规范写的js文件。
包
NPM官网 https://www.npmjs.com/ 是全球最大的包共享平台. 从这个网站上可以查找到你想要的包.
同时通过 https://registry.npmjs.org/ 服务器共享所有的包. 也就是我们能从这个地址下载包.
因为国内官网无法访问, 所以可以使用国内的阿里云镜像. https://npmmirror.com/
NPM 包管理器
Node Packaged Manager (NPM) 是管理包的工具. 这个包管理工具随着Node.js的安装一起安装到了用户的电脑.
可以通过在终端中执行npm -v 命令来查看自己电脑的NPM版本.
END