模块化开发
模块化只是一种思想
模块化演变过程
- Stage 1 - 文件划分方式
- 将功能与数据放置到不同的文件当中
- 约定每个文件都是一个独立的模块
- 缺点:
- 每个模块都在全局作用域下工作,污染全局作用域,容易出现命名冲突
- 无法管理模块之间的依赖关系
- 模块内的成员可以被其他模块随意修改
- 完全依靠约定,当项目变得庞大以后,难以维护
- Stage 2 - 命名空间方式
- 每个模块只暴露一个全局对象
- 所有成员与方法都挂载在这个全局对象下
- 解决了全局作用域污染与命名冲突的问题
- 缺点:
- 依然无法管理模块之间的依赖关系
- 模块的内的成员依然可以被修改
- Stage 3 - IIFE(立即执行函数)方式
- 使用立即执行函数,为模块创建私有空间
- 对于需要对外暴露的内容,可以挂载到window对象上
- 解决了模块内成员可以被随意修改的问题
- Stage 4 - 模块化规范出现
- CommonJS规范 - NodeJS模块化规范
- 一个文件就是一个模块
- 每个模块有单独的作用域
- 通过module.exports导出成员
- 通过require函数加载模块
- 同步模式加载模块(在浏览器环境效率不高)
- AMD(Asynchronous Module Definition)- 异步模块定义规范
- require.js实现了AMD规范,同时是一个功能强大的模块加载器
- 约定使用define函数定义一个模块,接收三个参数
- 第一个参数:模块名称
- 第二个参数:声明依赖数组
- 第三个参数:提供私有空间的函数,通过return向外部导出成员
- 使用require函数加载模块
- 绝大多数第三方库都支持AMD规范
- AMD模块使用起来相对复杂
- 模块JS请求相对频繁
- CMD(Common Module Definition)- 通用模块定义规范
- 由淘宝前端团队推出的sea.js定义并实现了CMD规范
- 类似CommonJS规范
- 通过require加载依赖
- 通过module.exports或exports对外暴露成员
- 被require.js兼容
- CommonJS规范 - NodeJS模块化规范
模块化标准规范
ES Module - ES2015中定义,并在语言层面实现的模块化规范
模块化的最佳实践,NodeJS环境下使用CommonJS规范,浏览器环境下使用ES Module
ES Module特性
- 自动采用严格模式
- 运行在独立私有作用域中
- 通过CORS方式请求外部JS模块
- 即script被标识为type=module方式引入外部资源时,会受到浏览器同源策略限制
- script加载外部JS时,会延迟执行脚本,等同于使用defer属性,不影响页面渲染,执行顺序不按引用顺序
ES Module的导入导出
-
通过export导出
-
常见用法
export const a = 1 export const fn = () => {} export default 123 export { a, fn as b } export t from './module.js'
-
注意事项
-
export { obj }
与export default { obj }
-
export { obj }
export后面跟的不是字面量对象,而是固定的语法 -
export default { obj }
export default后面跟的是一个值,这里{ obj }
表示字面量对象
-
- export导出的是成员的引用,而不是成员值的拷贝,这与CommonJS不同
-
-
-
通过import导入
-
常见用法
import { a, b, default as n } from './es-module.js'
-
注意事项
-
import { obj } from xxx
import后面的括号不是解构写法,而是固定的语法 - import导入的对象是只读的,不允许修改
- import导入文件路径不可省略(相对路径或者绝对路径)
- 不支持表达式方式,如果需要动态加载模块,可以使用import()函数,该函数返回一个Promise对象
-
-
ES Module浏览器环境polyfill
browser-es-module-loader
- 工作原理:
- 读取ES Module脚本
- 交给babel进行转换
- 执行转换后的代码
- 注意事项
- 在支持ES Module的浏览器环境下,会导致重复执行
- 可以使用script的nomodule属性,只会在不支持ES Module的浏览器环境下加载脚本
ES Module in Node.js
Node.js中目前可以使用ES Module语法加载或者导出模块,属于实验特性
-
要在node环境下使用ES Module
JS文件需要使用.mjs结尾(新版本node在package.json中标识type: module时,可以直接使用.js文件后缀,此时想使用CommonJS模块,需使用.cjs结尾)
-
执行时,需要添加
--experimental-modules
参数开启实验特性// 注意,必须先跟--experimental-modules参数,再接文件名 node --experimental-modules import.mjs
-
使用import加载其他模块
- 第三方模块:无法使用提取成员的方式
- 系统内置模块:官方做了兼容性处理,可以正常提取成员
- CommonJS模块:导出一个对象,因此无法使用提取成员的方式加载模块,可以使用加载default默认对象的方式
CommonJS模块无法直接加载ES Module
-
ES Module与CommonJS模块的差异
- ES Module下无法访问CommonJS内置成员及方法
require、module、exports、__filename、__dirname
- ES Module下无法访问CommonJS内置成员及方法