侧边栏壁纸
博主头像
M酷博主等级

一帆风顺 ⛵️⛵️⛵️

  • 累计撰写 45 篇文章
  • 累计创建 40 个标签
  • 累计收到 464 条评论

目 录CONTENT

文章目录

JS模块化的理解

M酷
2021-01-08 / 0 评论 / 0 点赞 / 2,408 阅读 / 2,770 字 / 正在检测是否收录...
广告 广告

什么是模块化?

模块化是一种处理复杂系统分解为更好的可管理模块的方式。
所谓的模块化开发就是封装细节,提供使用接口,彼此之间互不影响,每个模块都是实现某一特定的功能。
模块化开发的基础就是函数。
模块化开发使用代码耦合度降低,模块化的意义在于最大化的设计重用,以最少的模块、零部件,更快速的满足更多的个性化需求。

优点:

  1. 避免命名冲突
  2. 提高可复用性
  3. 支持按需加载
  4. 提高可维护性
  5. 提高可调试性
  6. 提高可测试性

缺点:

大量使用带来性能损耗

  1. 系统分层,调用链会很长
  2. 模块间发送消息会很耗性能

为什么需要模块化?

在 WEB 开发的早期,为了团队协作和代码维护的方 便,许多开发者会选择将 JavaScript 代码分开写在不同 的文件里面,然后通过多个 script 标签来加载它们。

<script src="./a.js"></script> 
<script src="./b.js">
</script> <script src="./c.js"></script>

虽然每个代码块处在不同的文件中,但最终所有 JS 变量 还是会处在同一个全局作用域下,这时候就需要额外注意由于作用域变量提升所带来的问题。

<script>
  // a.js
  var num = 1;
  setTimeout(() => console.log(num), 1000);
</script>
<script>
  // b.js
  var num = 2; 
</script>

模块化规范

1、AMD规范(Asynchronous Module Definition)
RequireJS遵循的是AMD规范。AMD规范是异步加载,依赖前置,特点是准备充分,但加载会较慢。使用define()定义模块,require()加载模块,有缓存,用于浏览器端。

2、CMD规范(Common Module Definition)
SeaJS遵循的是CMD规范。CMD规范也会异步加载,不同的是CMD依赖就近,特点是首次加载很快。CMD规范也是使用define()定义模块,require()加载模块,用于浏览器端,但和AMD规范思想不同,写法也不同。

3、CommonJS规范
CommonJS规范的特点是加载模块顺序按照词法解析的顺序加载,是同步加载的(服务端主要本地读取文件),使用require加载模块,module.exports导出模块,有缓存,用于Nodejs服务端,module可省略,但不推荐。

4、ESModule规范
ESModule的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系。使用import和export来导入导出,ES6还提供了一个default,用来提供默认的export。特点是加载模块存储的是值的引用,所以全局只有一份,加载模块也是异步的。
ES6的module吸收了CommoneJS和AMD两者的优点,兼容两标准的规范,浏览器和服务端均可使用。

5、UMD规范(Universal Module Definition)
UMD是一种 CommonJS/AMD/CMD 的兼容写法。它是为了让模块同时兼容CommonJS/AMD/CMD 规范而出现的,多被一些需要同时支持浏览器端和服务端引用的第三方库所使用。通过 define.amd/define.cmd/module 等判断当前支持什么方式,都不行就挂载到 window 全局对象上面去。

(function (root, factory) {
    if (typeof define === 'function' && (define.amd || define.cmd)) {
        //AMD,CMD
        define(['b'], function(b){
          return (root.returnExportsGlobal = factory(b))
        });
    } else if (typeof module === 'object' && module.exports) {
        //Node, CommonJS之类的
        module.exports = factory(require('b'));
    } else {
        //公开暴露给全局对象
        root.returnExports = factory(root.b);
    }
}(this, function (b) {
  return {};
}));

后模块化时代

通过前面的分析我们可以看出来,使用ESModule的模块明显更符合 JS 开发的历史进程,因为任何一个支持 JS 的环境,随着对应解释器的升级,最终一定会支持 ESModule 的标准。但是,WEB 端受制于用户使用的浏览器版本,我们并不能随心所欲的随时使用 JS 的最新特性。

为了能让我们的新代码也运行在用户的老浏览器中,社区涌现出了越来越多的工具,它们能静态将高版本规范的代码编译为低版本规范的代码,最为大家所熟知的就是babel

对于模块化相关的 import 和 export 关键字,babel 最终会将它编译为包含 require 和 exports 的 CommonJS 规范。这就造成了另一个问题,这样带有模块化关键词的模块,编译之后还是没办法直接运行在浏览器中,因为浏览器端并不能运行 CommonJS 的模块。为了能在 WEB 端直接使用 CommonJS 规范的模块,除了编译之外,我们还需要一个步骤叫做打包(bundle)。

所以打包工具比如 webpack / rollup ,编译工具 babel 它们之间的区别和作用就很清楚了。

  • 打包工具主要处理的是JS不同版本间模块化的区别编译工具主要处理的是 JS 版本间语义的问题
  • 如果使用了 ESModule : 必须使用 webpack 和 babel, 如果是 AMD 或 CommonJS : 只用 webpack即可

参考资料

JavaScript温故之AMD/CMD/UMD/CommonJS/ES6Module
The difference between module.exports and exports

0
广告 广告

评论区