在面试中只要说到模块化的问题,多多少少总会问到这些,umd、amd、cjs、esm,可能听过其中一个两个,或者都听说过。接下来我们先简单了解一下他们到底是什么,又有什么样的区别呢。

最开始的时候,Javascript是没有导入导出模块的这种方法,这就有一个比较头疼的问题,就是我们所有的代码都要写在一个文件里面,那可真的是又臭又长。有问题了,排查起来还特别的麻烦,定义个变量还总是出各种问题。后来,为了解决这些烦人的问题,各路大佬就推出了umd、amd、cjs、esm、cmd。

模块化的优点


  • 实现代码的可复用,具有独立的作用域,避免全局变量被污染

  • 便于代码编写和维护,提高开发效率

  • 实现按需加载,例如:npm就是一个巨大的模块化仓库

CommonJS(cjs)


CommonJS主要用于服务端,nodejs是默认使用cjs模块规范。每一个文件就是一个模块,有自己独立的作用域、变了、方法等,对其他的模块都不可见。模块可以多次加载,但是只在第一次时运行,然后运行结果就被缓存,后续加载则直接读取缓存结果。若想再次运行模块,则需要清楚缓存。模块的加载顺序是按照在代码中的出现的顺序进行加载的。
如果想要多次执行一个模块,可以导出一个方法,然后直接调用方法即可。特点


  1. 在执行模块代码之前,NodeJs会使用函数封装器将其封装为闭包形式的方法

  2. 可以通过 module.exports 导出模块内容,但是不能对exports有赋值操作

  3. CJS 是同步导入模块,通过 require(id) 引入模块、JSON、或本地文件

  4. 被引入的模块将被缓存到require.cache 对象中,如果删除该对象的某个模块会导致下次require的时候重新加载该模块

  5. CJS 不能在浏览器中工作,必须经过转换和打包。node.js 就是使用 commonJs 的模块规范,可以在 js 文件中直接使用

 // 定义模块 sum.js    
const num = 0;
function add(a, b) { return a + b; }
module.exports = {
//在这里写上需要向外暴露的函数、变量
  add: add,
  num: num
}
/** 必须加./路径,不加的话只会去node_modules文件找 **/
// 引用自定义的模块时,参数包含路径,可省略.js
const sum = require('./sum');
sum.add(2, 5);
// 引用核心模块时,不需要带路径
var http = require('http');
http.createService(...).listen(3000);

amd(Asynchronous Module Definition)


amd是Javascript的异步模块化定义方案,专门为前端而做的,代表库为:require.js。

  1. amd是异步导入

  2. 专为前端而设计

  3. 语法不如cjs直观

define(['dep1', 'dep2'], function (dep1, dep2) {   
  //Define the module value by returning a value.
  return function () {};
});
// "simplified CommonJS wrapping" https://requirejs.org/docs/whyamd.html
define(function (require) {
  var dep1 = require('dep1'),
  dep2 = require('dep2');
  return function () {};
});

RequireJS 是一个JavaScript模块加载器(文件和模块载入工具),使用RequireJS加载模块化脚本将提高代码的加载速度和质量它针对浏览器使用场景进行了优化,并且也可以应用到其他 JavaScript 环境中,例如 Node.js。

  1. 引入require.js时,我们会通过data-main引入入口文件;

  2. require.js获取到入口文件后,将文件以及对应的依赖通过script标签append到html上;

  3. 依赖是依次、同步append到html,但是script标签的加载却是异步的;

  4. 依赖加载完成后,会立即调用其回调执行函数;

  5. 入口文件监听到所有的依赖都加载完成后,再调用其回调函数(即回调函数factory)

<!-- project.html -->
<!DOCTYPE html>
<html>
<head>
<title>My Sample Project</title>
<script data-main="scripts/main" src="scripts/require.js"></script>
</head>
<body>
<h1>My Sample Project</h1>
</body>
</html>

// main.js
requirejs(["helper/util"], function(util) {
// you can do everything you want
});

cmd(Common Module Definition)


cmd通用模块定义,amd的优化版,代表库:sea.js

  1. AMD是依赖前置,提前加载依赖;CMD依赖后置,使用时才加载

  2. 所有代码都运行在模块作用域中,不会污染全局变量;

  3. 模块会被异步加载;

  4. 模块加载完成后,不会执行其回调函数,而是等到主函数运行且需要的执行依赖的时候才运行依赖函数(依赖后置、按需加载)

(对于seajs想了解的同学可以自己去官网了解,因为小妖也不太熟悉)

umd(Universal Module Definition)


一般可以把cjs转为UMD,用于浏览器运行时使用。是一种通用的写法,是在amd和cjs两个流行而不统一的规范情况下,才催生出umd来统一规范的,umd前后端均可通用,UMD 更像是一种配置多个模块系统的模式,当使用 Rollup/Webpack 之类的打包器时,UMD 通常用作备用模块。

// CommonJS侧重服务器,而AMD侧重于浏览器,两者的模块不能共享/*  UMD的思想很简单   判断是AMD则使用AMD方式  是commonJS则使用CommonJS方式  都不是则将模块公开给全局(window或global) */ (function (root, factory) {   if (typeof define === "function" && define.amd) {       // amd方式       define(["jquery", "underscore"], factory);   } else if (typeof exports === "object") {       // cjs方式       module.exports = factory(require("jquery"), require("underscore"));   } else {       // 公开暴露给全局       root.Requester = factory(root.$, root._);   }}(this, function ($, _) {    // 属性    var property = Math.property;    // 方法    function a() { };                   // 私有方法,因为它没被返回    function b() { return a() };        // 公共方法,因为被返回了    function c(x, y) { return x + y };  // 公共方法,因为被返回了    // 暴露公共方法    return {        ip: PI,        b: b,        c: c    }}));

esm(ES Module)


ESM 代表 ES 模块,es6原生支持的。这是 Javascript 提出的实现一个标准模块系统的方案。

  1. 大多现代浏览器都支持

  2. 异步加载和简单语法

  3. 因为是静态module结构,支持打包工具去除无用代码

// 在html中调用
<script type="module">
import {func1} from 'my-lib';
func1();
</script>

// 静态导入:导入本地的文件、库或者远程模块
import { createStore } from "https://unpkg.com/redux@4.0.5/es/redux.mjs";
import * as myModule from './util.js';

// 动态导入:ES模块实际上是JavaScript对象:我们可以解构它们的属性以及调用它们的任何公开方法
btn.addEventListener("click", () => {
  // loads named export
  import("./util.js")
  .then(({ funcA }) => {
    funcA(); });
});

const loadUtil = () => import("./util.js");
// 返回的是一个 promise。所以也可以使用可以使用 `async/await`
btn.addEventListener("click", () => {
  loadUtil().then(module => {
    module.funcA();
    module.default();
})})
// 使用 async/await 的写法
btn.addEventListener("click", async () => {
  const utilsModule = await loadUtil();
  utilsModule.funcA();
  utilsModule.default();
})

最后

  • 由于 esm 具有简单的语法,异步特性和可摇树性,因此它是最好的模块化方案

  • umd 随处可见,通常在 esm 不起作用的情况下用作备用

  • cjs 是同步的,适合后端

  • amd 是异步的,适合前端(cmd是amd的优化)

最最后:欢迎关注大家我的公众号

简单聊一聊Javascript中的模块化的更多相关文章

  1. JavaScript 中的模块化

    JavaScript 中的模块化 最早的基于立即执行函数,闭包的模块化 const MountClickModule = function(){  let num = 0;  const handle ...

  2. 简单分析JavaScript中的面向对象

    初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...

  3. 简单说 JavaScript中的tostring( ) 与 valueOf( )方法

    说明 所有的对象都继承有toString() 和 valueOf() 方法,对象到字符串,对象到数字的转换,会通过调用待转换对象的这两个方法中的一个来完成. 解释 toString( )方法的作用是: ...

  4. 简单谈谈JavaScript中的this

    是夜,想着考量下小黄毛近期的JavaScript进阶如何了,鉴于近期一直在接触Vue 2.0,索性就围绕this编写了个代码片段, 给其一个测量,毕竟写js的程序员都知道,JavaScript的函数调 ...

  5. 简单理解Javascript中的call 和 apply

    javascript中面向对像的能力是后来加进来的, 为了兼容性, 所以整出了很多奇特的东西, function Animal(){ this.name = "Animal"; t ...

  6. 简单理解javascript中的原型对象,实现对之间共享属性和行为

    javascript中提供了构造函数.可以方便的创建对象. 典型的构造函数例如以下: function Person(name, age) { this.name = name; this.age = ...

  7. 简单说 JavaScript中的事件委托(下)

    说明 上次我们说了一些,关于 JavaScript中事件委托的 基础知识,这次我们继续来看. 解释 先来一段代码 <!doctype html> <html lang="e ...

  8. JavaScript中的模块化之AMD和CMD

    前言: 为什么我们需要模块化开发,模块化开发的好处有哪些? 首先我们先说一下非模块化的开发方式带来的弊端. 非模块化开发中会导致一些问题的出现,变量和函数命名可能相同,会造成变量污染和冲突,并且出错时 ...

  9. 聊一聊JavaScript中的事件循环

    一.概念:事件循环 JavaScript是单线程的 1.整片 script 整体代码(第一个宏任务)放到执行栈中,执行之后,会触发很多方法 这些方法只能一个个的顺序执行,不能并发 2.这些要执行的方法 ...

随机推荐

  1. Arduino+ESP32 之 驱动GC9A01圆形LCD(二),移植LVGL,跑示例程序,显示自制图片

    在前文Arduino+ESP32 之 驱动GC9A01圆形LCD(一), 我们已经移植好了arduino GFX库, 该库的示例程序内,还有LVGL的示例程序哦. arduino环境下移植lvgl是很 ...

  2. 人工智能与智能系统2-> 机器人学2 | 时间与运动

    <Robotics, Vision and Control>学习到第三章,我才发现这本书是有配套视频的,第二章看的好辛苦,很多地方生硬理解了一下,现在打算把视频再好好看一看,作为补充,也会 ...

  3. Linq to SQL各种参考

    原文:https://www.cnblogs.com/lyj/archive/2008/01/23/1049686.htmlhttps://www.cnblogs.com/lyj/archive/20 ...

  4. SpringBoot 自定义配置

    有时候需要自己定义一些配置,比如SpringBoot没有提供Druid连接池的配置,需要我们自己写配置. 以在springboot中使用Druid为例. 依赖 <dependency> & ...

  5. Spring学习二:Spring Bean 定义

    Bean 定义 被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的.bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象.这些 bean 是 ...

  6. JFrame 的层次结构 及 背景设置说明

    感谢原文:https://blog.csdn.net/qq_32006373/article/details/49659129 一.JFrame 的层次结构 我们通过两个图来说明一下 JFrame 的 ...

  7. undefined index: php中提示Undefined ...

    我们经常接收表单POST过来的数据时报Undefined index错误,如下:$act=$_POST['action'];用以上代码总是提示Notice: Undefined index: act ...

  8. NSLog输出格式及随机数

    NSLog输出格式及随机数 %@ 对象 %d, %i 整数 (%i和%d无差别,%i是老式写法,%d是新式写法而已.) %u 无符整形 %f 浮点/双字 %x, %X 二进制整数 %o 八进制整数 % ...

  9. https校验问题

    一般会报SSL问题:解决办法参考 http://blog.csdn.net/a506681571/article/details/78284589 # 设置未经允许验证的SSL方法,只需运行一次便可 ...

  10. sublime与python交互

    点击菜单栏中的工具 -> 编译系统,勾选Python即可 创建hello.py文件,Ctrl+S保存文件,Ctrl+B执行文件,结果如下图   3.sublime运行python文件的交互环境设 ...