03.06 随手记(AMD、CMD、CommonJS、ES6 Module的区别)

  • ***当前阶段的笔记 ***

「面向实习生阶段」https://www.aliyundrive.com/s/VTME123M4T9 提取码: 8s6v
点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。
1.CSS选择器有哪些?
1.id选择器( # myid)2.类选择器(.myclassname)3.标签选择器(div, h1, p)4.相邻选择器(h1 + p)5.子选择器(ul > li)6.后代选择器(li a)7.通配符选择器( * )8.属性选择器(a[rel = “external”])9.伪类选择器(a:hover, li:nth-child)
2.数组和对象的区别?
数组表示有序数据的集合,而对象表示无序数据的集合。如果数据的顺序很重要,就用数组,否则就用对象。
3.javascript的typeof返回哪些数据类型?
Object number function boolean underfind;

AMD、CMD、CommonJS、ES6 Module的区别

优点

  • 模块化开发中,通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数,并且可以按需加载。
  • 依赖自动加载,按需加载
  • 提高代码复用率,方便进行代码管理,使得代码管理更加清晰规范
  • 减少了命名冲突,消除全局变量。
  • 目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统

常见模块化规范

  • CommonJS 用于 node 端,是同步加载的
  • AMD 依赖于 requirejs,是异步加载的,是提前加载,立即加载
  • CMD 依赖于 seajs ,是异步加载,延后加载,就近加载,用时加载

1、CommonJS(Node.js)

CommonJS是服务器模块的规范,Node.js采用了这个规范。

根据 CommonJS 规范,一个单独的文件就是一个模块,每一个模块都是一个单独的作用域,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的。

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。

CommonJS 中,加载模块使用 require 方法。该方法读取一个文件并执行,最后返回文件内部的 exports 对象。

Node.js 主要用于服务器编程,加载的模块文件一般都已经存在本地硬盘,加载起来较快,不用考虑异步加载的方式,所以 CommonJS 的同步加载模块规范是比较适用的。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMDCMD 等解决方案。

CommonJS模块的特点如下。
● 所有代码都运行在模块作用域,不会污染全局作用域。
● 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
● 模块加载的顺序,按照其在代码中出现的顺序。

var x = 5;
var addX = function(value) {
  return value + x;
};

module.exports.x = x;
module.exports.addX = addX;


// 也可以改写为如下
module.exports = {
  x: x,
  addX: addX,
};


let math = require('./math.js');
console.log('math.x',math.x);
console.log('math.addX', math.addX(4));

2、ES6 模块

ES6模块和CommonJS区别

  • CommonJS 模块是运行时加载 ES6 Module 是编译时输出接口;
  • CommonJS 加载的是整个模块,将所有的接口全部加载进来 ES6 Module 可以单独加载其中的某个接口;
  • CommonJS 输出是值的拷贝 ES6 Module 输出的是值的引用,被输出模块的内部的改变会影响引用的改变;
  • CommonJS this指向当前模块 ES6 Module this指向undefined;

目前浏览器对ES6 Module兼容还不太好,我们平时在webpack中使用的export/import,会经过babel转换为CommonJS规范。

1)ES6的模块运行的机制也和CommonJS模块不同,ES6遇到模块加载命令import时不会去执行模块,只会生成一个动态只读引用,等到真的需要用到时,再去模块中取,只是一个动态引用,不会缓存结果值,模块里面的变量绑定其所在的模块。
2)ES6模块和CommonJS模块的循环加载机制也不同,CommonJS模块中一个模块就是一个脚本文件,require命令第一次加载就会运行整个脚本(加载时执行),并在内存中生成一个对象,以后需要这个模块时,就会到exports属性上取值,即使再次执行require命令,也不会再次执行该模块,而是到缓存中取值。对于循环加载,就输出已经执行的部分,未执行的部分并不会输出。ES6中模块是动态引用,需要开发者自己保证真正的取值时能够取到值。

如果当前的module文件非常大,可能造成堵塞可以如下操作

<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>

defer:等到整个页面正常渲染结束才会执行,
async:一旦下载完成,渲染引擎就会中断渲染,执行这个脚本以后再继续渲染。
如果有多个defer脚本,则会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

浏览器加载ES6模块时也使用script标签,但是要加入type=”module”属性。

<script type="module" src="foo.js"></script>

由于type属性设为module,所以浏览器知道这是一个ES6模块

<script type="module" src="foo.js"></script>
<!-- 等同于 -->
<script type="module" src="foo.js" defer></script>

CommonJS 输出值的拷贝

CommonJS 模块输出的是值的拷贝(类比于基本类型和引用类型的赋值操作)。对于基本类型,一旦输出,模块内部的变化影响不到这个值。对于引用类型,效果同引用类型的赋值操作。

// lib.js
var counter = 3;
var obj = {
    name: 'David'
};

function changeValue() {
    counter++;
    obj.name = 'Peter';
};

module.exports = {
    counter: counter,
    obj: obj,
    changeValue: changeValue,
};


// main.js
var mod = require('./lib');

console.log(mod.counter);  // 3
console.log(mod.obj.name);  //  'David'
mod.changeValue();
console.log(mod.counter);  // 3
console.log(mod.obj.name);  //  'Peter'

// Or
console.log(require('./lib').counter);  // 3
console.log(require('./lib').obj.name);  //  'Peter'
  • counter 是基本类型值,模块内部值的变化不影响输出的值变化。
  • obj 是引用类型值,模块内部值的变化影响输出的值变化。
  • 上述两点区别,类比于基本类型和引用类型的赋值操作。

也可以借助取值函数(getter),将 counter 转为引用类型值,效果如下。

在类的内部,可以使用 getset 关键字,对某个属性设置存执函数和取值函数,拦截该属性的存取行为。

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  get counter() {
    return counter
  },
  incCounter: incCounter,
};


// main.js
var mod = require('./lib');

console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 4

ES6 输出值的引用

ES6 模块是动态关联模块中的值,输出的是值得引用。原始值变了,import 加载的值也会跟着变。

ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析时,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。ES6 模块中,原始值变了,import 加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}

// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4

CommonJS 运行时加载 ES6静态编译

CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

这是因为,CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

ES6 模块是编译时输出接口,因此有如下2个特点

  • import 命令会被 JS 引擎静态分析,优先于模块内的其他内容执行
  • export 命令会有变量声明提升的效果

import 优先执行

在文件中的任何位置引入 import 模块都会被提前到文件顶部

// a.js
console.log('a.js')
import { foo } from './b';

// b.js
export let foo = 1;
console.log('b.js 先执行');

// 执行结果:
// b.js 先执行
// a.js

虽然 a 模块中 import 引入晚于 console.log('a'),但是它被 JS 引擎通过静态分析,提到模块执行的最前面,优于模块中的其他部分的执行。

export 命令变量提升效果

由于 importexport 是静态执行,所以 importexport 具有变量提升效果。即 importexport 命令在模块中的位置并不影响程序的输出。

// a.js
import { foo } from './b';
console.log('a.js');
export const bar = 1;
export const bar2 = () => {
  console.log('bar2');
}
export function bar3() {
  console.log('bar3');
}

// b.js
export let foo = 1;
import * as a from './a';
console.log(a);

// 执行结果:
// { bar: undefined, bar2: undefined, bar3: [Function: bar3] }
// a.js

a 模块引用了 b 模块,b 模块也引用了 a 模块,export 声明的变量也是优于模块其它内容的执行的。但具体对变量赋值需要等到执行到相应代码的时候。

ES6模块和CommonJS相同点

模块不会重复执行

重复引入某个相同的模块时,模块只会执行一次。

循环依赖

CommonJS 模块循环依赖

CommonJS 模块的重要特性是加载时执行,即脚本代码在 require 的时候,就会全部执行。一旦出现某个模块被“循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。

另外,由于 CommonJS 模块遇到循环加载时,返回的是当前已经执行的部分的值,而不是代码全部执行后的值,两者可能会有差异。所以,输入变量的时候,必须非常小心。

var a = require('a'); // 安全的写法 导入整体,保证module已经执行完成
var foo = require('a').foo; // 危险的写法

exports.good = function (arg) {
  return a.foo('good', arg); // 使用的是 a.foo 的最新值
};

exports.bad = function (arg) {
  return foo('bad', arg); // 使用的是一个部分加载时的值
};

上面代码中,如果发生循环加载,require('a').foo 的值很可能后面会被改写,改用 require('a') 会更保险一点。

**Demo **

// a.js
console.log('a starting');
exports.done = false;
const b = require('./b');
console.log('in a, b.done =', b.done);
exports.done = true;
console.log('a done');

// b.js
console.log('b starting');
exports.done = false;
const a = require('./a');
console.log('in b, a.done =', a.done);
exports.done = true;
console.log('b done');

// node a.js
// 执行结果:
// a starting
// b starting
// in b, a.done = false
// b done
// in a, b.done = true
// a done

从上面的执行过程中,可以看到,在 CommonJS 规范中,当遇到 require() 语句时,会执行 require 模块中的代码,并缓存执行的结果,当下次再次加载时不会重复执行,而是直接取缓存的结果。正因为此,出现循环依赖时才不会出现无限循环调用的情况。

ES6 模块循环依赖

跟 CommonJS 模块一样,ES6 不会再去执行重复加载的模块,又由于 ES6 动态输出绑定的特性,能保证 ES6 在任何时候都能获取其它模块当前的最新值。

动态 import()

ES6 模块在编译时就会静态分析,优先于模块内的其他内容执行,所以导致了我们无法写出像下面这样的代码

if(some condition) {
  import a from './a';
}else {
  import b from './b';
}

// or 
import a from (str + 'b');

因为编译时静态分析,导致了我们无法在条件语句或者拼接字符串模块,因为这些都是需要在运行时才能确定的结果在 ES6 模块是不被允许的,所以 动态引入import() 应运而生。

import() 允许你在运行时动态地引入 ES6 模块,想到这,你可能也想起了 require.ensure 这个语法,但是它们的用途却截然不同的。

require.ensure 的出现是 webpack 的产物,它是因为浏览器需要一种异步的机制可以用来异步加载模块,从而减少初始的加载文件的体积,所以如果在服务端的话, require.ensure 就无用武之地了,因为服务端不存在异步加载模块的情况,模块同步进行加载就可以满足使用场景了。 CommonJS 模块可以在运行时确认模块加载。

import() 则不同,它主要是为了解决 ES6 模块无法在运行时确定模块的引用关系,所以需要引入 import()

先来看下它的用法

  • 动态的 import() 提供一个基于 PromiseAPI
  • 动态的 import() 可以在脚本的任何地方使用 import() 接受字符串文字,可以根据需要构造说明符
// a.js
const str = './b';
const flag = true;
if(flag) {
  import('./b').then(({foo}) => {
    console.log(foo);
  })
}
import(str).then(({foo}) => {
  console.log(foo);
})

// b.js
export const foo = 'foo';

// babel-node a.js
// 执行结果
// foo
// foo

当然,如果在浏览器端的 import() 的用途就会变得更广泛,比如 按需异步加载模块,那么就和 require.ensure 功能类似了。

因为是基于 Promise 的,所以如果你想要同时加载多个模块的话,可以是 Promise.all 进行并行异步加载。

Promise.all([
  import('./a.js'),
  import('./b.js'),
  import('./c.js'),
]).then(([a, {default: b}, {c}]) => {
    console.log('a.js is loaded dynamically');
    console.log('b.js is loaded dynamically');
    console.log('c.js is loaded dynamically');
});

还有 Promise.race 方法,它检查哪个 Promise 被首先 resolvedreject。我们可以使用 import() 来检查哪个 CDN 速度更快:

const CDNs = [
  {
    name: 'jQuery.com',
    url: 'https://code.jquery.com/jquery-3.1.1.min.js'
  },
  {
    name: 'googleapis.com',
    url: 'https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js'
  }
];

console.log(`------`);
console.log(`jQuery is: ${window.jQuery}`);

Promise.race([
  import(CDNs[0].url).then(()=>console.log(CDNs[0].name, 'loaded')),
  import(CDNs[1].url).then(()=>console.log(CDNs[1].name, 'loaded'))
]).then(()=> {
  console.log(`jQuery version: ${window.jQuery.fn.jquery}`);
});

当然,如果你觉得这样写还不够优雅,也可以结合 async/await 语法糖来使用。

async function main() {
  const myModule = await import('./myModule.js');
  const {export1, export2} = await import('./myModule.js');
  const [module1, module2, module3] =
    await Promise.all([
      import('./module1.js'),
      import('./module2.js'),
      import('./module3.js'),
    ]);
}

动态 import() 为我们提供了以异步方式使用 ES 模块的额外功能。

根据我们的需求动态或有条件地加载它们,这使我们能够更快,更好地创建更多优势应用程序。

3、AMD (RequireJS) 异步模块定义(前置加载)

基于commonJS规范的nodeJS出来以后,服务端的模块概念已经形成,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境

var math = require('math');
math.add(2, 3);

第二行math.add(2, 3),在第一行require(‘math’)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的。

这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。

因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

通过define来定义一个模块,使用require可以导入定义的模块。define可以传入三个参数,分别是字符串-模块名、数组-依赖模块、函数-回调函数。数组中声明需要加载的模块,可以是模块名、js文件路径。

RequireJS的特点

对于依赖的模块,AMD推崇依赖前置,提前执行。也就是说,在define方法里传入的依赖模块(数组),会在一开始就下载并执行。

AMD规范:

前置加载,所有前置模块异步加载结束后,才进行调用callback。require.js实现了这个规范

define(['./a', './b'], function(a, b) { 
// 依赖必须一开始就写好    
a.doSomething()    
// 此处略去 100 行    
b.doSomething()    
// ...})

缓存:所有缓存的模块保存在require.cache之中,如果想删除模块的缓存,可以像下面这样写。

// 删除指定模块的缓存
delete require.cache[moduleName];
// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {
  delete require.cache[key];
})

注意,缓存是根据绝对路径识别模块的,如果同样的模块名,但是保存在不同的路径,require命令还是会重新加载该模块。

require.main:
require方法有一个main属性,可以用来判断模块是直接执行,还是被调用执行。直接执行的时候(node module.js),require.main属性指向模块本身。require.main === module // true。调用执行的时候(通过require加载该脚本执行),上面的表达式返回false。

require的内部处理流程
require命令是CommonJS规范之中,用来加载其他模块的命令。它其实不是一个全局命令,而是指向当前模块的module.require命令,而后者又调用Node的内部命令Module.

Module._load = function(request, parent, isMain) {
  // 1. 检查 Module._cache,是否缓存之中有指定模块
  // 2. 如果缓存之中没有,就创建一个新的Module实例
  // 3. 将它保存到缓存
  // 4. 使用 module.load() 加载指定的模块文件,读取文件内容之后,使用 module.compile() 执行文件代码
  // 5. 如果加载/解析过程报错,就从缓存删除该模块
  // 6. 返回该模块的 module.exports
};

上面的第4步,采用module.compile()执行指定模块的脚本,逻辑如下。

Module.prototype._compile = function(content, filename) {
  // 1. 生成一个require函数,指向module.require
  // 2. 加载其他辅助方法到require
  // 3. 将文件内容放到一个函数之中,该函数可调用 require
  // 4. 执行该函数
};

上面的第1步和第2步,require函数及其辅助方法主要如下。
● require(): 加载外部模块
● require.resolve():将模块名解析到一个绝对路径
● require.main:指向主模块
● require.cache:指向所有缓存的模块
● require.extensions:根据文件的后缀名,调用不同的执行函数
一旦require函数准备完毕,整个所要加载的脚本内容,就被放到一个新的函数之中,这样可以避免污染全局环境。该函数的参数包括require、module、exports,以及其他一些参数。

(function (exports, require, module, __filename, __dirname) {
  // YOUR CODE INJECTED HERE!
});

Module._compile方法是同步执行的,所以Module._load要等它执行完成,才会向用户返回module.exports的值。

  • AMD = Asynchronous Module Definition,即 异步模块定义
  • AMD 规范加载模块是异步的,并允许函数回调,不必等到所有模块都加载完成,后续操作可以正常执行。
  • AMD 中,使用 require 获取依赖模块,使用 exports 导出 API
//规范 API
define(id?, dependencies?, factory);
define.amd = {};


// 定义无依赖的模块
define({
    add: function(x,y){
        return x + y;
    }
});


// 定义有依赖的模块
define(["alpha"], function(alpha){
    return {
        verb: function(){
            return alpha.verb() + 1;
        }
    }
});

异步加载和回调

require([module], callback)callback 为模块加载完成后的回调函数

//加载 math模块,完成之后执行回调函数
require(['math'], function(math) {
 math.add(2, 3);
});

RequireJS

RequireJS 是一个前端模块化管理的工具库,遵循 AMD 规范,RequireJS 是对 AMD 规范的阐述。

RequireJS 基本思想为,通过一个函数来将所有所需的或者所依赖的模块装载进来,然后返回一个新的函数(模块)。后续所有的关于新模块的业务代码都在这个函数内部操作。

RequireJS 要求每个模块均放在独立的文件之中,并使用 define 定义模块,使用 require 方法调用模块。

按照是否有依赖其他模块情况,可以分为 独立模块非独立模块

  • 独立模块,不依赖其他模块,直接定义
define({
    method1: function(){},
    method2: function(){}
});

//等价于
define(function() {
    return {
        method1: function(){},
        method2: function(){}
    }
});
  • 非独立模块,依赖其他模块
define([ 'module1', 'module2' ], function(m1, m2) {
    ...
});

//等价于
define(function(require) {
    var m1 = require('module1');
    var m2 = require('module2');
    ...
});
  • require 方法调用模块
require(['foo', 'bar'], function(foo, bar) {
    foo.func();
    bar.func();
});

4、CMD (SeaJS)(就近加载,延迟执行)

可同步可异步

就近加载,在需要用到依赖的时候才申明,可同步可异步,Sea.js实现了这个规范,Sea.js 遇到依赖后只会去下载 JS 文件,并不会执行,而是等到所有被依赖的 JS 脚本都下载完以后,才从头开始执行主逻辑。因此被依赖模块的执行顺序和书写顺序完全一致。

define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    // 此处略去 100 行
    var b = require('./b') // 依赖可以就近书写
    b.doSomething()
    // ...
})
//a.js
/*
* define 接受 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串,
* factory 为对象、字符串时,表示模块的接口就是该对象、字符串。
* define 也可以接受两个以上参数。字符串 id 表示模块标识,数组 deps 是模块依赖.
*/
define(function(require, exports, module) {
  var $ = require('jquery');
  exports.setColor = function() {
    $('body').css('color','#333');
  };
});

//b.js
//数组中声明需要加载的模块,可以是模块名、js文件路径
seajs.use(['a'], function(a) {
  $('#el').click(a.setColor);
});

对于依赖的模块,CMD推崇依赖就近,延迟执行。也就是说,只有到require时依赖模块才执行。AMD 推崇依赖前置。

img

CMD = Common Module Definition,即 通用模块定义CMDSeaJS 在推广过程中对模块定义的规范化产出。

CMD规范和AMD类似,都主要运行于浏览器端,写法上看起来也很类似。主要是区别在于 模块初始化时机

  • AMD中只要模块作为依赖时,就会加载并初始化
  • CMD中,模块作为依赖且被引用时才会初始化,否则只会加载。
  • CMD 推崇依赖就近,AMD 推崇依赖前置。
  • AMDAPI 默认是一个当多个用,CMD 严格的区分推崇职责单一。例如,AMDrequire 分全局的和局部的。CMD里面没有全局的 require,提供 seajs.use() 来实现模块系统的加载启动。CMD 里每个 API 都简单纯粹。
//AMD
define(['./a','./b'], function (a, b) {
 
    //依赖一开始就写好
    a.test();
    b.test();
});
 
//CMD
define(function (requie, exports, module) {
     
    //依赖可以就近书写
    var a = require('./a');
    a.test();
     
    ...
    //软依赖
    if (status) {
        var b = requie('./b');
        b.test();
    }
});



//定义没有依赖的模块
define(function(require, exports, module){
  exports.xxx = value
  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')
  var m4 = require('./module4')
  m1.show()
  m4.show()
})

Sea.js

使用Sea.js,在书写文件时,需要遵守CMD(Common Module Definition)模块定义规范。一个文件就是一个模块。

用法

  • 通过 exports 暴露接口。这意味着不需要命名空间了,更不需要全局变量。这是一种彻底的命名冲突解决方案。
  • 通过 require 引入依赖。这可以让依赖内置,开发者只需关心当前模块的依赖,其他事情 Sea.js 都会自动处理好。对模块开发者来说,这是一种很好的 关注度分离,能让程序员更多地享受编码的乐趣。
  • 通过 define 定义模块,更多详情参考SeasJS | 极客学院

示例

例如,对于下述util.js代码

var org = {};
org.CoolSite = {};
org.CoolSite.Utils = {};

org.CoolSite.Utils.each = function (arr) {
  // 实现代码
};

org.CoolSite.Utils.log = function (str) {
  // 实现代码
};

可以采用SeaJS重写为

define(function(require, exports) {
  exports.each = function (arr) {
    // 实现代码
  };

  exports.log = function (str) {
    // 实现代码
  };
});

通过 exports 就可以向外提供接口。通过 require('./util.js') 就可以拿到 util.js 中通过 exports 暴露的接口。这里的 require 可以认为是 Sea.js 给 JavaScript 语言增加的一个语法关键字,通过 require 可以获取其他模块提供的接口。

define(function(require, exports) {
  var util = require('./util.js');
  exports.init = function() {
    // 实现代码
  };
});

SeaJS与RequireJS区别

二者区别主要表现在模块初始化时机

  • AMD(RequireJS)中只要模块作为依赖时,就会加载并初始化。即尽早地执行(依赖)模块。相当于所有的require都被提前了,而且模块执行的顺序也不一定100%就是require书写顺序。
  • CMD(SeaJS)中,模块作为依赖且被引用时才会初始化,否则只会加载。即只会在模块真正需要使用的时候才初始化。模块加载的顺序是严格按照require书写的顺序。

从规范上来说,AMD 更加简单且严谨,适用性更广,而在RequireJS强力的推动下,在国外几乎成了事实上的异步模块标准,各大类库也相继支持AMD规范。

但从SeaJS与CMD来说,也做了很多不错东西:1、相对自然的依赖声明风格 2、小而美的内部实现 3、贴心的外围功能设计 4、更好的中文社区支持。

5、UMD

  • UMD = Universal Module Definition,即通用模块定义。UMDAMDCommonJS的糅合。

AMD 模块以浏览器第一的原则发展,异步加载模块。
CommonJS 模块以服务器第一原则发展,选择同步加载。它的模块无需包装(unwrapped modules)。
这迫使人们又想出另一个更通用的模式 UMD(Universal Module Definition),实现跨平台的解决方案。

  • UMD 先判断是否支持 Node.js 的模块(exports)是否存在,存在则使用 Node.js 模块模式。再判断是否支持 AMDdefine 是否存在),存在则使用 AMD 方式加载模块。
(function (window, factory) {
    if (typeof exports === 'object') {
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        define(factory);
    } else {
        window.eventUtil = factory();
    }
})(this, function () {
    //module ...
});

webpack中加载3种模块 | 语法

Webpack允许使用不同的模块类型,但是底层必须使用同一种实现。所有的模块可以直接在盒外运行。

  • ES6 模块
import MyModule from './MyModule.js';
  • CommonJS(Require)
var MyModule = require('./MyModule.js');
  • AMD
define(['./MyModule.js'], function (MyModule) {
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值