1. 模块化的演进过程
stage1 文件划分
污染全局作用域
命名冲突问题
无法管理模块依赖关系
早期模块化完成靠约定
stage2 命名空间方式
每个模块包裹成为全局对象
减小命名冲突的可能
没有私有空间,仍然可以在外部进行访问或修改
无法管理模块依赖关系
stage3 IIFE(立即执行函数)
每个成员放在一个函数提供的私有作用域当中
对于需要暴露给外部的成员,通过挂载到全局对象来实现,实现私有变量
私有成员通过闭包去访问,外部无法使用
利用自执行函数的参数作为依赖声明去使用,使得每个模块的依赖关系更加明显
以上是早期在没有工具和规范的情况下对模块化的落地方式
2.模块化规范的出现
模块化标准+模块加载器
CommonJS规范:一个文件就是一个模块
每个模块都有单独的作用于
通过module.exports导出成员
通过require函数载入模块
CommonJS是以同步模式加载模块
\
早期使用AMD(异步模块定义规范),Require.js实现了AMD规范
AMD使用起来相对复杂,模块JS文件请求频繁
3.模块化标准规范
CommonJS in Node.js
ES Modules in Browers(新规范)
\
4.ES Modules基本特性
1.ESM 自动采用严格模式,忽略'use strict'
2.每个ES Module都是运行在单独有的私有作用域中
3.ESM是通过CORS的方式请求外部JS模块的,注意请求的地址要支持CORS
4.ESM的 script标签会延迟执行脚本(类似defer)
\
5. ES Modules 导入与导出
导出:
通过as进行导出成员的重命名
var name = 'foo11 module'
function hello(){
console.log('hello')
}
class Person {}
export { name,hello,Person }
export {
name as fooName
}
export default name//默认成员
将成员重命名为default,则该成员为默认成员。
导入:
import{fooName} from './module.js'
import foo from './module.js'//接收默认导出的成员,名称可以随便取
console.log(fooName)
6.导入导出中的注意事项
(1)export不是导出字面量对象,import也不是对对象的解构。都是一种固定的用法
(2)导出成员,导出的是对对象的引用。(指向被引用的地址)
(3)导入的成员是一个只读成员,不能修改(可用于定义常量模块)
import {} from 或者(直接import '')导入不需要控制的子功能模块
import * as mod from './module.js' 导出所有的成员到一个对象中
import只能出现在最顶层。
动态导入模块:import('./module.js').then(function(){
})
\
import title,{name,age} from *//title为默认成员
\
7.ES Modules in Broswer Polyfill兼容方案
将浏览器中不识别的ES module通过babel去转换,需要import的文件通过ajax去请求,请求回来的代码通过babel转换从而能够支持ES Module。
对于支持ES Module的浏览器,需要添加nomodule属性来阻止ES module的二次加载
具体操作:(只在开发阶段使用)
8.NodeJS中使用ES Module
node 8.5+就可以使用
1.将文件扩展名由js改为mjs
2.启动:node --experimental-modules index.mjs
9.ES Modules 与CommonJS交互
ES Modules中可以导入CommonJS模块
CommonJS模块始终只会导出一个默认成员
不能在CommonJS模块通过require载入 ES Modules模块
10. ES Modules 与CommonJS的差异
ESM中没有CommonJS中的那些模块全局成员
require、module和exports可以使用ES Modules中的import和export代替
__filename和__dirname需要转换,具体操作如下图
11.Nodejs新版本的进一步支持
node 12.10+可以通过项目的package.json中添加type字段("type":"module"),这样就不需要将文件扩展名改为mjs了,但想要正常执行CommonJS,需要把文件扩展名改为.cjs。
\
12.Babel兼容方案
babel是最主流的一款JS编译器,它可以将一些使用新特性的代码,编译成当前环境支持的代码。
babel是基于插件机制去实现的,通过插件去转换代码的特性。perset-env是一个插件的集合。
安装:yarn add @babel/node @babel/core @babel/perset-env --dev
使用:yarn babel-node index.js --persets=@babel/preset-env
也可以添加一个.babelrc的文件将presets添加到文件里
这样就可以这样使用了:yarn babel-node index.js
也可以单独安装preset中的一些插件来使用。