模块化的背景
早期 Javascript 程序很小,大多被用来执行独立的脚本任务,提供一定交互。现在有了运行大量 Javascript 脚本的复杂程序,还有一些被用在其他环境(例如 Node.js)。因此,有必要开始考虑提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。
Javascript中的模块需要解决的问题是: 如何将一段代码封装到一个有用的单元中,以及如何注册其函数/为模块导出值,如何引用其他代码单元。
Babel模块化相关插件
- babel-plugin-transform-modules-amd
- babel-plugin-transform-modules-commonjs
- babel-plugin-transform-modules-umd
传统方法
以Jquery为例,以前是如何定义代码段的?
// Jquery 示例
(function(window,undefined){
window.$ = window.JQuery;
var JQuery = function(selector){
}
})(window)
- 通过立即函数调用表达式(IIFE)定义
- 对依赖关系的引用是通过通过HTML脚本标记加载的全局变量名称完成的。
- 依赖关系的描述非常微弱:开发人员需要知道正确的依赖关系顺序。例如,包含Backbone的文件不能在jQuery标记之前。
- 它需要额外的工具才能将一组脚本标签替换为一个标签,以优化部署。
- 在大型项目上,这可能很难管理,尤其是当脚本开始以可能重叠和嵌套的方式具有许多依赖关系时。手写脚本标签的伸缩性不是很高,因此无法按需加载脚本。
CommonJS
CommonJS 原先叫做ServerJS,是js在服务端的规范,node使用的就是这种规范。根据CommonJS规范,一个单独的文件就是一个模块,require用来加载一个模块,exports用来向外部暴露该模块里的方法或属性,加载模块是同步的。
CommonJS规范
AMD (异步模块定义 Asynchronous Module Definition)
AMD规范制定了定义模块的规则,这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题)。
AMD规范只定义了一个函数 "define",它是全局变量。函数的描述为:
define(id?, dependencies?, factory);
id是模块的名字,dependencies是依赖模块数组,工厂方法 factory 为模块初始化要执行的函数或对象
- 通过调用define()来注册工厂函数,而不是立即执行它。
- 将依赖项作为字符串值数组传递,不要获取全局变量。
- 仅在加载并执行了所有依赖项后,才执行工厂函数。
- 将相关模块作为参数传递给工厂函数。
// 全局函数 define 应该实现绑定 amd 属性,防止与现有的定义了define函数但不遵从AMD编程接口的代码相冲突
define.amd = {};
// 判定当前页面是否有 AMD 模块加载器
if (typeof define === "function" && define.amd) {
// ...
}
requirejs
requireJS是基于AMD模块加载规范,使用回调函数来解决模块加载的问题。
requireJS是使用创建script元素,通过指定script元素的src属性来实现加载模块的。
CMD (通用模块定义 Common Module Definition)
CMD规范中,一个模块就是一个文件
define('hello', ['jquery'], function(require, exports, module) {
// require
// require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口
// require.async 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。callback 参数可选
// require.resolve 使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径
// exports
// exports 是一个对象,用来向外提供模块接口
// 也可以使用 return 直接向外提供接口
// exports 仅仅是 module.exports 的一个引用。在 factory 内部给 exports 重新赋值时,并不会改变 module.exports 的值。因此给 exports 赋值是无效的,不能用来更改模块接口
//module
// module 是一个对象,上面存储了与当前模块相关联的一些属性和方法
// module.uri String 根据模块系统的路径解析规则得到的模块绝对路径
// module.dependencies 是一个数组,表示当前模块的依赖
// module.exports 当前模块对外提供的接口
// module.exports 的赋值需要同步执行,不能放在回调函数里
});
// 判定当前页面是否有 CMD 模块加载器
if (typeof define === "function" && define.cmd) {
}
seajs
SeaJS是一个遵循 CMD 规范的模块加载器。
UMD(通用模块定义Universal Module Definition)
UMD提供了支持多种风格的通用模式,在兼容CommonJS和AMD规范的同时,还兼容全局引用的方式
// babel UT output code
(function (global, factory) {
if (typeof define === "function" && define.amd) {
// 判断是否支持AMD
define(["exports"], factory);
} else if (typeof exports !== "undefined") {
// 否支持 Node.js 模块格式
factory(exports);
} else {
var mod = {
exports: {}
};
factory(mod.exports);
global.input = mod.exports;
}
})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports.default = void 0;
var _default = {};
_exports.default = _default;
});
ES6 模块
- ES6 模块是通过静态分析编译时加载
- 不需要UMD模块格式,将来服务器和浏览器都会支持 ES6 模块格式。
- 将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。
- 不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。
ES6 模块
浏览器支持模块
最新的浏览器开始原生支持模块功能了,浏览器能够最优化加载模块,使它比使用库更有效率:使用库通常需要做额外的客户端处理。
- html 使用
type="module"
的 script 标签 - 原生JavaScript模块,使用
.mjs
扩展名,MIME-type
为javascript/esm
导入文件- 比较清晰,这可以指出哪些文件是模块,哪些是常规的 JavaScript。
- 这能保证你的模块可以被运行时环境和构建工具识别,比如 Node.js 和 Babel
- export import 语法和ES6一致
- import 模块文件的路径是相对于站点根目录的相对路径,或者使用点语法意味当前路径
- 通过本地加载Html 文件 (比如一个 file:// 路径的文件), 会遇到 CORS 错误,因为Javascript 模块安全性需要,要通过一个服务器来测试。
- 模块自动采用严格模式,不管你有没有在模块头部加上"use strict";
- 加载一个模块脚本时不需要使用 defer 属性,模块会自动延迟加载。
动态加载模块
将import()作为函数调用, 返回一个 promise, 模块对象为参数
import('/modules/myModule.mjs')
.then((module) => {
// Do something with the module.
});
参考
webpack 模块
webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:
- ES2015 import 语句
- CommonJS require() 语句
- AMD define 和 require 语句
- css/sass/less 文件中的 @import 语句。
- 样式(url(...))或 HTML 文件(
<img src=...>
)中的图片链接(image url)
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!