为什么需要模块化
在开始说CommonJS之前,我们先回顾一下为什么需要模块化?
当前端工程到达一定规模后,就会出现下面的问题:
- 全局变量污染
- 依赖混乱
上面的问题,共同导致了代码文件难以细分
模块化就是为了解决上面两个问题出现的
模块化出现后,我们就可以把臃肿的代码细分到各个小文件中,便于后期维护管理
前端模块化标准
前端主要有两大模块化标准:
- CommonJS,简称CMJ,这是一个社区规范,出现的时间较早,目前仅node环境支持
- ES Module,简称ESM,这是随着ES6发布的官方模块化标准,目前浏览器和新版本node环境均支持
安装nodejs
前面提到CommonJS只支持node环境,所以我们需要安装nodejs
官网地址:nodejs.org/zh-cn/
浏览器
浏览器运行的是html页面,并加载页面中通过script元素引入的js
nodejs
nodejs直接运行某个js文件,该文件被称之为入口文件
nodejs遵循ECMAScript标准,但由于脱离了浏览器环境,因此:
- 你可以在nodejs中使用ECMAScript标准的任何语法或api,例如:循环、判断、数组、对象等
- 你不能在nodejs中使用浏览器的 web api,例如:dom对象、window对象、document对象等
由于大部分开发者是从浏览器端开发转向nodejs开发的,为了降低开发者的学习成本,nodejs中提供了一些和浏览器web api同样的对象或函数,例如:console、setTimeout、setInterval等
CommonJS
在nodejs中,由于有且仅有一个入口文件(启动文件),而开发一个应用肯定会涉及到多个文件配合,因此,nodejs对模块化的需求比浏览器端要大的多
由于nodejs刚刚发布的时候,前端没有统一的、官方的模块化规范,因此,它选择使用社区提供的CommonJS作为模块化规范
在学习CommonJS之前,首先认识两个重要的概念:模块的导出和模块的导入
模块的导出
要理解模块的导出,首先要理解模块的含义
什么是模块?
模块就是一个JS文件,它实现了一部分功能,并隐藏自己的内部实现,同时提供了一些接口供其他模块使用
模块有两个核心要素:隐藏和暴露
隐藏的,是自己内部的实现
暴露的,是希望外部使用的接口
任何一个正常的模块化标准,都应该默认隐藏模块中的所有实现,而通过一些语法或api调用来暴露接口
暴露接口的过程即模块的导出
模块的导入
当需要使用一个模块时,使用的是该模块暴露的部分(导出的部分),隐藏的部分是永远无法使用的。
当通过某种语法或api去使用一个模块时,这个过程叫做模块的导入
CommonJS规范
CommonJS使用exports
导出模块,require
导入模块
具体规范如下:
- 如果一个js文件中存在
exports
或require
,那么该js文件是一个模块 - 模块内的所有代码均为隐藏代码,包括全局变量、全局函数,这些全局的内容均不应该对全局变量造成任何污染
- 如果一个模块需要暴露一些api提供给外部使用,需要通过
exports
导出,exports
是一个空的对象,你可以为该对象添加任何需要导出的内容 - 如果一个模块需要导入其他模块,通过
require
实现,require
是一个函数,传入模块的路径即可返回该模块导出的整个内容
注意在用
require
函数导入的时候参数必须以"./"
或"../"
开头
nodejs对CommonJS的实现
为了实现CommonJS规范,nodejs对模块做出了以下处理
-
为了保证高效的执行,仅加载必要的模块。nodejs只有执行到
require
函数时才会加载并执行模块 -
为了隐藏模块中的代码,nodejs执行模块时,会将模块中的所有代码放置到一个函数中执行,以保证不污染全局变量。
(function(){ //模块中的代码 })()
-
为了保证顺利的导出模块内容,nodejs做了以下处理
- 在模块开始执行前,初始化一个值
module.exports = {}
module.exports
即模块的导出值- 为了方便开发者便捷的导出,nodejs在初始化完
module.exports
后,又声明了一个变量exports = module.exports
(function(module){ module.exports = {}; var exports = module.exports; //模块中的代码 return module.exports; })()
注意:上面的代码只是便于理解,实际CommonJS并不是由立即执行函数实现的,还要注意最后返回的是
module.exports
,如果我们对module.exports
重新赋值一个对象,那么exports
跟module.exports
就不指向同一个地方了。 - 在模块开始执行前,初始化一个值
-
为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果
原文链接:https://juejin.cn/post/7236689719077224506 作者:swyanga