编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

JS模块化详解(js模块化规范有哪些)

wxchong 2024-06-30 10:25:07 开源技术 13 ℃ 0 评论

CommonJS AMD CMD UMD ES6 Module

模块化开发的优点:

? 模块化开发中,通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数,并且可以按需加载。

? 依赖自动加载,按需加载。

? 提高代码复用率,方便进行代码的管理,使得代码管理更加清晰、规范。

? 减少了命名冲突,消除全局变量。

? 目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统

CommonJS

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

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

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

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

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));


AMD

? AMD = Asynchronous Module Definition,RequireJS异步模块定义。

? AMD 规范加载模块是异步的,并允许函数回调,不必等到所有模块都加载完成,后续操作可以正常执行。

? AMD 中,使用 require 获取依赖模块,使用 exports 导出 API

? 可异步加载,require([module], callback) 中 callback 为模块加载完成后的回调函


//规范 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;
}
}
});


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();
});

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

CMD

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

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

? AMD中只要模块作为依赖时,就会加载并初始化

? CMD中,模块作为依赖且被引用时才会初始化,否则只会加载。

? CMD 推崇依赖就近,AMD 推崇依赖前置。

? AMD 的 API 默认是一个当多个用,CMD 严格的区分推崇职责单一。例如,AMD 里 require 分全局的和局部的。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();
}
});

SeaJS

? 通过 exports 暴露接口。这意味着不需要命名空间了,更不需要全局变量。这是一种彻底的命名冲突解决方案。

? 通过 require 引入依赖。这可以让依赖内置,开发者只需关心当前模块的依赖,其他事情 Sea.js 都会自动处理好。对模块开发者来说,这是一种很好的 关注度分离,能让程序员更多地享受编码的乐趣。

? 通过 define 定义模块



//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推崇依赖就近,延迟执行。也就是说,只有用到时依赖模块才执行

UMD

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

? UMD 先判断是否支持 Node.js 的模块(exports)是否存在,存在则使用 Node.js 模块模式。再判断是否支持 AMD(define 是否存在),存在则使用 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 ...
});

ES6 Module

? ES6 Module是编译时输出接口;

? ES6 Module可以单独加载其中的某个接口;

? ES6 Module输出的是值的引用,被输出模块的内部的改变会影响引用的改变;

? ES6 Module this指向undefined;


//a.js
var name = 'lin';
var age = 13;
var job = 'ninja';

export { name, age, job};

//b.js
import { name, age, job} from './a.js';

console.log(name, age, job);// lin 13 ninja

//或者

//a2.js
export default function () {
console.log('default ');
}

//b2.js
import customName from './a2.js';
customName(); // 'default'

CMD(SeaJS)与AMD(RequireJS)区别

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

? AMD(RequireJS)中只要模块作为依赖时,就会加载并初始化。即尽早地执行(依赖)模块。相当于所有的require都被提前了,而且模块执行的顺序也不一定100%就是require书写顺序。

? CMD(SeaJS)中,模块作为依赖且被引用时才会初始化,否则只会加载。即只会在模块真正需要使用的时候才初始化。模块加载的顺序是严格按照require书写的顺序。

ES6 Module与CommonJS区别

? ES6 模块输出的是值的引用,输出接口动态绑定,而 CommonJS 输出的是值的拷贝。

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

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'


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

// 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); //


defer与async的区别

deferasync的区别是:

? defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;

? async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。

? 一句话,defer是“渲染完再执行”,async是“下载完就执行”。

? 如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表