本文已参与「新人创作礼」活动,一起开启掘金创作之路。
JS语言刚开始只是为了处理一些页面上的交互效果,后来逐渐形成一门复杂的语言,在实现的功能越来越复杂,项目越来越大时,也开始像其他语言一样分文件开发,将每一个文件定义为一个module,在需要使用的模块引入这个module以引用这个模块暴露出来的接口,并且不让其污染全局作用域
常用的模块化规范有: CommonJS、AMD、CMD、ESM
一、CommonJS
CommonJS是最早出现被广泛应用的标准化模块化规范,是node完成初始,开发npm包管理工具时所用的模块化规范。
1.特点
》模块内所有代码都是在模块作用域,不会污染全局作用域
》CommonJS起初就是为了node服务的,他是同步的
》require 读入并执行一个JavaScript文件,返回该模块的exports对象。如果没有发现指定模块,会报错,如果第一次进来,会降exports存入缓存,然后第二次进入后只需要返回缓存中的export即可
》exports 用来向外暴露 API,exports 只能是一个 object 对象,暴漏的 API 须作为该对象的属性。
2.使用
```javascript // exports 规定exports为一个对象 module.exports = { key: value } module.exports.x = value export.x = value
// require 如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径 require('moment') require('./math.js') ```
二、AMD
CommonJS是同步的,只有需要引入的模块加载完成才能执行后面的代码,一般都是服务端使用,如果浏览器端使用,需要借助Browserify将项目打包。而AMD是基于异步开发模式开发的,允许异步完成后调用一个回调函数。
1. 定义和使用
```javascript // 定义暴露接口 define(function() { // do something })
// 定义没有依赖的模块 define('math',['exports'],function (exports) { exports.getObs = function() { return Math.abs(-3); } // OR:直接返回 // function getObs() { // return Math.abs(-3); // } // return { // getObs, // }; });
// require
require(['math'], function(math){ math.getObs() }) ```
2.require.js
require是遵循amd规范的一个模块化工具,下面结合require.js来使用一下amd规范
》首先require需要下载,官网或者github上都可以(官网地址: https://requirejs.org/docs/release/2.3.6/minified/require.js)
》然后把require.js放入一个文件夹下(amd/js/require.js),并在入口文件中引入
amd/index.html html <!DOCTYPE html> <html> <head> <title>Modular Demo</title> <script src="js/require.js" data-main="main"></script> </head> <body></body> </html>
》 然后定义模块
javascript // amd/modules/math define('math',['exports'],function (exports) { exports.getObs = function() { return Math.abs(-3); } });
》 在main中引入模块
require.config({ baseUrl: "", // 默认在根目录下 paths: { math: "./modules/math", }, }); require(["math"], function (math) { alert(math.getObs()); })
效果:
二、CMD
CMD也是基于异步服务于浏览器端的一个模块化规范,它结合了ComminJs和AMD的特点,并开发了自己的模块化工具语言SeaJs
1.定义和使用
```javascript //定义没有依赖的模块,exports,module三个是默认传入 define(function(exports, module){ exports.xxx = value // Or // module.exports = value })
//定义有依赖的模块 define(function(require, exports, module){ //同步引入依赖 var module2 = require('./module2') //异步引入依赖 require.async('./module3', function (m3) {}) //暴露模块 exports.xxx = value })
// 引用 define(function (require) { var m1 = require('./module1') console.log(m1.xxx) })
```
2.SeaJS
》首先SeaJS需要下载,https://github.com/seajs/seajs
》引入到index.js
html <!DOCTYPE html> <html> <head> <title>Modular Demo</title> <script type="text/javascript" src="js/sea.js"></script> <script type="text/javascript"> seajs.use("./main"); </script> </head> <body></body> </html>
》定义模块
javascript define(function (require, exports, module) { module.exports = { getRandom: () => { return Math.random(); } } })
》在main.js中引用模块
javascript define(function (require) { var math = require("./modules/math.js"); console.log(math.getRandom()); });
效果
三 ESM
1.定义和使用
```javascript // export export { a: '', b: function() {} } export default { a: '' } export const a = 123; export const {a, b} = Obj
// 引入import 必须放在代码的最前面,因为ESM是不编译的 import A from "./a.js" import { a,b } from "./a.js" ```
2、ESM和CMD的区别
1.CMD因为输出的export对象是运行完成后才输出接口,而EMD是引用接口,所以编译时就输出接口
2.CMD是有缓存的,模块内数据更新,不会更新到引用的模块,还是使用缓存中的值。而EMD是没有缓存的,他是直接引用了模块中的变量和方法,当有数据改变时,引用的模块也会更新。
参考文章: https://juejin.cn/post/6844903744518389768#heading-32