模块化之CommonJs、AMD、CMD和ES6模块化
模块是将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起。块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
模块化有两个重要的概念:模块的导出和模块的导入
-
模块的导出:暴露接口的过程即模块的导出
-
模块的导入:当通过某种语法或API去使用一个模块时,这个过程叫做模块的导入
1、CommonJS
因为CommonJs是node服务提出的模块化规范,所以我们首先需要安装node环境
安装nodeJs
官网地址:https://nodejs.org/zh-cn/
安装完成后在dos
窗口中输入node
命令验证
使用nodeJs
在node环境中运行index.js文件
node index.js
nodejs直接运行某个js文件,该文件被称之为入口文件
为什么要有CommonJS?
在nodejs中,由于有且仅有一个入口文件(启动文件),而开发一个应用肯定会涉及到多个文件配合,因此,nodejs对模块化的需求比浏览器端要大的多。由于nodejs刚刚发布的时候,前端没有统一的、官方的模块化规范,因此,它选择使用社区提供的CommonJS作为模块化规范。
CommonJS使用
CommonJS使用exports
导出模块,导出的是一个对象,require
导入模块
具体规范如下:
- 如果一个JS文件中存在
exports
或require
,该JS文件是一个模块 - 模块内的所有代码均为隐藏代码,包括全局变量、全局函数,这些全局的内容均不应该对全局变量造成任何污染
- 如果一个模块需要暴露一些API提供给外部使用,需要通过
exports
导出,exports
是一个空的对象,你可以为该对象添加任何需要导出的内容
//utils.js
function getNumber(){
return 1
}
exports = {
getNumber:getNumber //导出getNumber方法
}
//等同于
exports.getNumber = function(){
return 1; //导出一个方法叫getNumber
}
//等同于
function getNumber(){
return 1
}
module.exports = {getNUmber}
- 如果一个模块需要导入其他模块,通过
require
实现,require
是一个函数,传入模块的路径即可返回该模块导出的整个内容
//index.js
const util = require("./utils.js")
//等同于
const {getNumber} = require("./utils.js")
//等同于
console.log(require("./utils.js").getNumber())
注意:必须加路径./
或../
不可省略,否则会报错!
nodejs对CommonJS的实现
为了实现CommonJS规范,nodejs对模块做出了以下处理
-
为了保证高效的执行,仅加载必要的模块。nodejs只有执行到
require
函数时才会加载并执行模块 -
为了隐藏模块中的代码,nodejs执行模块时,会将模块中的所有代码放置到一个函数中执行,以保证不污染全局变量。(node笔记中解释)
(function(){ //模块中的代码 })()
-
为了保证顺利的导出模块内容,nodejs做了以下处理
- 在模块开始执行前,初始化一个值
module.exports = {}
-
module.exports
即模块的导出值 - 为了方便开发者便捷的导出,nodejs在初始化完
module.exports
后,又声明了一个变量exports = module.exports
(function(module){ module.exports = {}; var exports = module.exports; //模块中的代码 return module.exports; })()
- 在模块开始执行前,初始化一个值
-
为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果
CommonJS的工作原理
当使用require(模块路径)
导入一个模块时,node会做以下两件事情(不考虑模块缓存):
- 通过模块路径找到本机文件,并读取文件内容
- 将文件中的代码放入到一个函数环境中执行,并将执行后module.exports的值作为require函数的返回结果
正是这两个步骤,使得CommonJS在node端可以良好的被支持
可以认为,CommonJS是同步的,必须要等到加载完文件并执行完代码后才能继续向后执行
2、AMD
当浏览器遇到CommonJS
当想要把CommonJS放到浏览器端时,就遇到了一些挑战
- 浏览器要加载JS文件,需要远程从服务器读取,而网络传输的效率远远低于node环境中读取本地文件的效率。由于CommonJS是同步的,这会极大的降低运行性能
- 如果需要读取JS文件内容并把它放入到一个环境中执行,需要浏览器厂商的支持,可是浏览器厂商不愿意提供支持,最大的原因是CommonJS属于社区标准,并非官方标准
因为浏览器无法支持模块化。出现了AMD和CMD规范,有效的解决了浏览器模块化的问题。
AMD(Asynchronous Module Definition):异步模块加载机制
require.js实现了AMD规范
在AMD中,导入和导出模块的代码,都必须放置在define函数中
define([依赖的模块列表], function(模块名称列表){
//模块内部的代码
return 导出的内容
})
在AMD中,入口文件需要特殊指明,用data-amin
属性标记
<body>
<script data-main="./index.js" src="./require"></script>
</body>
AMD使用
//utils.js
define(function(){
let data = []
//...模块内部代码 进行一系列
return {
name:"导出的内容",
data:data
}
})
//index.js
define(['utils'], function(utils) {
//等utils模块加载完成后执行自己的模块,参数utils是utils模块导出的内容
return {
name:"index模块的内容"
}
});
如果多个文件都用到了同一个模块,模块只会读取一次,执行一次,会加入缓存中
后来,require也实现了CMD规范
//utils2.js
define(function(require, exprots,module) {
const utils = require("./utils.js")
console.log("utils模块代码",utils)
module.exprots = "导出utils2模块的内容"
});
注意:可以省略./
,但最好不要省略
3、CMD
CMD(Common Module Definition):公共模块定义规范
sea.js实现了CMD规范
在CMD中,导入和导出模块的代码,都必须放置在define函数中,内部实现类似CommonJs
define(function(require, exports, module){
//模块内部的代码
})
CMD简单使用
define(function(require, exprots,module) {
const utils1 = require("./utils1.js")
const utils2 = require("./utils2.js")
module.exprots = "导出该模块的内容"
});
通过异步方式导入模块
define(function(require, exprots,module) {
require.async("./utils1.js",function(utils1){
console.log(utils1)
})
require.async("./utils2.js",function(utils2){
console.log(utils2)
})
module.exprots = "导出a模块的内容"
});
4、ES6模块化
ECMA组织参考了众多社区模块化标准,终于在2015年,随着ES6发布了官方的模块化标准,后成为ES6模块化
模块化声明方式有两种
- 依赖延迟声明 ,如:commonjs
- 优点:某些时候可以提高效率
- 缺点:无法在一开始确定模块依赖关系(比较模糊)
- 依赖预声明 ,如: cmd,amd,es6模块
- 优点:在一开始可以确定模块依赖关系
- 缺点:某些时候效率较低
ES6模块化具有以下的特点
- 使用依赖预声明的方式导入模块
- 灵活的多种导入导出方式
- 规范的路径表示法:所有路径必须以./或…/开头
模块的引入
注意:这一部分非模块化标准
目前,浏览器使用以下方式引入一个ES6模块文件
<script src="入口文件" type="module">
ES6导入和导出
1、基本导入和导出
基本导出可以有多个,每个必须有名称。export 声明表达式
// 基本导出,声明语句
export const name = "lkx" //导出name = "llx"
export function test(){
console.log("导出函数test")
}
其他写法export {具名符号}
const age = 10
const sex = "男"
export {
age,
sex
}
//将age变量的名称作为导出的名称,将age的值,作为导出的值
// 等同于
export const age = 10
export const sex = "男"
也可以给导出变量起别名
const name = 10
export {
name as name1
}
由于基本导出必须具有名称,所以要求导出内容必须跟上声明表达式或具名符号
由于使用的是依赖预加载,因此,导入任何其他模块,导入代码必须放置到所有代码之前
(不在开头也可以,浏览器预编译时进行import导入提升)不是解构赋值
对于基本导出,如果要进行导入,使用下面的代码
import {导入的符号列表} from "模块路径"
import {name,test} from "./until.js"
console.log(name)
console.log(test())
注意以下细节:
-
导入时使用的符号是常量,不可修改
-
导入时,可以通过关键字
as
对导入的符号进行重命名
import {name,test as t} from "./until.js"
console.log(t())
- 可以使用*号导入所有的基本导出,形成一个对象
import * as obj from "./b.js"
//会把所有导出的内容形成一个对象,必须使用as重命名
- 多个文件导入同一个模块,会加入缓存,不会重复运行
import "./init.js"
//这个导入语句,仅运行文件,不会导入模块,如果有缓存,不运行
2、默认导入和导出
每个模块,除了允许有多个基本导出之外,还允许有一个默认导出
默认导出类似于CommonJS中的module.exports
,由于只有一个,因此无需具名
具体的语法是
export default 默认导出的数据
//或
export {默认导出的数据 as default}
基本使用
export default {
name:name,
age:18
}
需要想要导入一个模块的默认导出,需要使用下面的语法
import 接收变量名 from "模块路径"
由于默认导入时变量名是自行定义的,因此没有别名一说
import data from "./utils.js"
console.log(data)
基本导入和默认导入混合使用:
如果希望同时导入某个模块的默认导出和基本导出,可以使用下面的语法:
import 接收默认导出的变量, {接收基本导出的变量} from "模块路径"
export const sex = "fale"
export default {
name:name,
age:18
}
import data,{sex} from "./utils.js"
console.log(data,sex)
注:如果使用*号,会将所有基本导出和默认导出聚合到一个对象中,默认导出会作为属性default存在
//b.js
export const sex = "fale"
export default {
name:name,
age:18
}
import * as data from "./b.js"
console.log(data)
注意:
当导出一个内容时,尽量保证该内容是不可变的(大部分情况都是如此)。因为,虽然导入后,无法更改导入内容,但是在导入的模块内部却有可能发生更改,这将导致一些无法预料的事情发生
导入的模块是常量
import { name } from "./b.js"
name = 10 //报错
如果我们只是想执行模块中的一些代码,而不需要导入它的任何内容,可以使用无绑定的导入:
import "模块路径"
有的时候,我们可能需要用一个模块封装多个模块,然后有选择的将多个模块的内容分别导出,可以使用下面的语法轻松完成
export {绑定的标识符} from "模块路径"
推荐阅读
-
JavaScript模块化-CommonJS、AMD、CMD、UMD、ES6
-
前端模块化:CommonJS,CMD,AMD,ES6
-
前端模块化小总结—commonJs,AMD,CMD, ES6 的Module
-
javascript模块化之CommonJS、AMD、CMD、UMD、ES6
-
javascript模块化之CommonJS、AMD、CMD、UMD、ES6
-
JavaScript里面的模块化4种方式(CommonJS,AMD,CMD,es6Module)
-
模块化module/CommonJS/Browserify/AMD/RequireJS/CMD/SeaJS/ES6_Babel
-
前端模块化:AMD, CMD, CommonJS, ES6 Module
-
前端模块化详解(CommonJS、AMD、CMD、ES Module)
-
模块化之CommonJs、AMD、CMD和ES6模块化