common.js node环境中内置的模块化规范 优点 一个文件就是一个模块 每个文件都有独立的作用域 用法 module.exports 导出成员 可以用exports代替 但是exports不能导出对象 通过require函数导入模块 原理 common.js规范实际就是将js模块当成一个函数,内置了几个参数,包括require,module等五个参数, module.exports指向的是一个对象,而exports也指向了同一个对象,所以可以代替module.exports,当exports导出一个新对象的时候,他就失效了,因为模块导入的过程就是执行那个模块对应的函数的过程,并将module.exports指向的对象返回出来。 局限性 commonJs规范是以同步的方式加载模块,node 环境下会首先加载所有模块然后使用 不适合浏览器的执行环境
AMD 异步模块定义规范,require.js实现了AMD规范 导出模块 通过define函数来定义模块,该函数接受三个参数 1:模块名称 2:依赖的模块,可选项 数组 3:一个函数 为模块提供私有作用域 函数的参数与依赖的模块一一对应 导入模块 require 该方法接受两个参数 1:所加载的模块 2:模块加载之后的执行方法 方法的参数与加载的模块一一对应
define('a',['jQuery','/c'],function($,angular){
const user = { name: 123}
return {
jquery:$,
user,
}
}
require([./a,'Angular'],function(a,angular){
})
缺点: AMD规范使用相对复杂,而且每次执行require时会生成script标签去请求对应模块,对于同一个模块可能会被多次加载,影响页面性能
CMD 通用模块定义规范 sea.js实现了CMD规范 特点
- 一个模块是一个文件
- 不应该在模块内引入新的自由变量
- 异步执行
- 通过define来定义模块和引入模块, define接受一个函数作为参数,该函数有三个参数
- require 导入模块
- exports 导出模块
- module 模块的描述对象 module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。
// math.js
define(function(require, exports, module) {
exports.add = function() {
var sum = 0, i = 0, args = arguments, l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};
});
// increment.js
define(function(require,exports,module){
var add = require('math').add
exports.increment = function(val){
return add(val,1)
}
})
// program.js
define(function(require, exports, module) {
var inc = require('increment').increment;
var a = 1;
inc(a); // 2
module.id == "program";
});
实践: Node中使用CommonJs规范,web中使用ES Module
ESModule ESM是ES6中的定义的模块系统
特点:
- ESM默认采用严格模式,忽略'use strict' ,this为undefined
<script type="modue">
console.log(this) // undefined
</script>
- 每个ESM都具有单独的做那个作用域 不能直接访问
- ESM 是通过 CORS 方式请求外部 js 的。CORS不能通过文件的形式请求,只能通过http的方式请求 所以有时候会存在跨域
- ESM是延迟执行的,类似于给script标签添加了defer属性 等到页面加载完毕在执行 使用 Html中给script标签添加type="module"来定义
import 导入模块 要求: from后面的文件路径必须包含完整的文件名 可以是相对路径,也可以是绝对路径,又或者是模块所在位置的完整url 不能省略后缀或者index.js 不引用模块中的成员,而是直接记载模块时候,
import {} from '路径'
import '路径'
导入模块中所有成员
import * as 别名 from '路径'
import只能在顶层作用域中使用 不能在if,for等作用域中使用 动态导入
import(modulePath).then(mod => {
console.log(mod);
})
import(modulePath).then({mod拆开来写} => {
console.log(mod);
})
同时导入命名成员和默认成员
import { default as name, Person, hello } from './module.js'
import name, { Person, hello } from './module.js'
模块导出
1. 直接导出变量、函数、类等
export var name = 'Json'
export function hello() {
console.log('hello');
}
export class Person {
constructor(name) {
this.name = name
}
}
2. export 导出一个对象,将变量作为一个对象的属性导出
export {
name, hello, Person
}
3. 导出时重命名,通过 as 来对导出的变量进行重命名,导入时只能使用重命名之后的变量名
// 导出
export {
name as userName, hello, Person
}
// 导入
import { name } from './module.js' // 报错 应该是userName
如果将变量重命名为 default , 则该变量将作为模块默认导出成员。在 import 导入时,直接使用任意名字来存储模块导出的变量。
4.如果将变量重命名为 default , 则该变量将作为模块默认导出成员。在 import 导入时,直接使用任意名字来存储模块导出的变量。
export {
name as default, hello, Person
}
import personName from './module.js'
console.log(personName);
5. 通过 export default 变量名 可以将变量作为模块的默认导出来导出。在导入时的用法和 4 一致。
export default name
注意事项 导入导出的写法是固定的语法,而不是对象的解构 比如导出一个对象 导入的之后不能通过结构的形式来获取 其中一个属性 导入导出的是变量的引用,如果在使用了导入的变量之后修改变量,引用的变量的值将改变 导入模块的时候,模块的成员是只读的,不能修改 直接导出导入的成员
export { Person, hello } from './module.js'
直接导出默认成员时,需要通过 as 进行重命名
export { default as name, Person, hello } from './module.js'
esmodule在低版本浏览器的兼容 ESM 浏览器环境 polyfill。通过引入browser-es-module-loader 来兼容不支持 ESM 的浏览器环境。 1.可以通过unpkg.com/moduleName来…
<script nomodule type="text/javascript" src="https://unpkg.com/browse/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script>
<script nomodule type="text/javascript" src="https://unpkg.com/browse/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>
- script 标签的 nomodule 属性,在不支持ESM的浏览器中才会运行其中的代码。可以利用这个属性来兼容对ESM的支持
<script nomodule>
alert(1234)
</script>
node环境下使用ESM
- 需要将后缀名改为.mjs
- 使用node命令执行.mjs文件时,需要添加--experimental-modules参数 因为还处于试验阶段 3.可以使用 import {} from ‘’ 的形式导入内置模块的成员,因为内置模块对ESM进行了兼容
// import fs from 'fs'
import { writeFileSync } from 'fs'
writeFileSync('.test.txt', 'Hello Node 123')
4.不能使用 import {} from ‘’ 的方式导入第三方模块,因为第三方模块导出的是默认成员(主要看node第三方模块是否添加了ESM支持)
import { camelCase } from 'lodash'
console.log(camelCase('ES Module')); 报错 不支持esm
CommJS和ESM在Node环境下的交互 1.ESM 可以导入 CommonJs 的模块 2.CommonJs 模块不能 导入 ESM 的模块 3.CommonJs 模块只会默认导出
ESM和CommonJs 在 Node 中差异:主要ESM是对于node内置模块中的5个变量不支持
// 加载模块的函数
console.log(require);
// 模块对象
console.log(module);
// 导出对象别名
console.log(exports);
// 当前文件的绝对路径
console.log(__filename);
// 当前文件所在目录
console.log(__dirname);
在 ESM 中使用相关功能
import { fileURLToPath } from 'url'
import { dirname } from 'path'
// 当前文件的url
// console.log(import.meta.url);
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
console.log(__filename);
console.log(__dirname);
在node中设置默认使用ESM规范执行js
- package.json文件中的type设置为 module
{
"type": "module",
"dependencies": {
"lodash": "^4.17.20"
}
}
2 .如果要使用CommonJs规范,则需要将后缀改为.cjs
使用babel让node低版本环境兼容ESM
使用preset-env转换 preset-env是一个插件集合 安装插件@babel/node @bable/core @babel/preset-env 配置babel,在.babelrc文件中添加配置 "presets": ["@babel/preset-env"] 运行命令 yarn babel-node index.js
使用插件@babel/plugin-transform-modules-commonjs转换 模块转换的专用插件 安装插件@babel/node @bable/core @babel/plugin-transform-modules-commonjs 配置babel,在.babelrc文件中添加配置 "plugins": ["@babel/plugin-transform-modules-commonjs"] 运行命令 yarn babel-node index.js