首頁 > web前端 > js教程 > JavaScript模組化程式設計規範之CommonJS、AMD、CMD、ES6

JavaScript模組化程式設計規範之CommonJS、AMD、CMD、ES6

WBOY
發布: 2022-03-10 13:53:18
轉載
1986 人瀏覽過

本篇文章為大家帶來了關於javascript中的相關知識,其中主要介紹了模組化程式設計規範,CommonJS、AMD、CMD以及ES6的相關問題,希望對大家有幫助。

JavaScript模組化程式設計規範之CommonJS、AMD、CMD、ES6

相關推薦:javascript學習教學

#一、前言

AMD、 CMD、CommonJsES5中提供的模組化程式設計方案,import/exportES6新增的模組化程式設計方案。

那麼,究竟什麼是AMD、CMD、CommonJs?他們之間又存在什麼差別呢?專案開發應該選用哪一種模組化程式設計規範,又是如何使用?本篇部落格文章將一一解答以上疑問。

二、AMD-非同步模組定義

AMD是」Asynchronous Module Definition」的縮寫,即」非同步模組定義」。它採用非同步方式載入模組,模組的載入不影響它後面語句的運作。

這裡異步指的是不堵塞瀏覽器其他任務(dom構建,css渲染等),而加載內部是同步的(加載完模組後立即執行回調)。

RequireJS:是一個AMD框架,可以非同步載入JS文件,依照模組載入方法,透過define()函數定義,第一個參數是數組,裡面定義一些需要依賴的包,第二個參數是一個回調函數,透過變數來引用模組裡面的方法,最後透過return來輸出。

AMDRequireJS在推廣過程中對模組定義的規範化產出,它是一個概念,RequireJS是對這個概念的實現,就好比JavaScript語言是對ECMAScript規範的實作。 AMD是一個組織,RequireJS是在這個組織下自訂的一套腳本語言。

不同於CommonJS,它要求兩個參數:

require([module], callback);
登入後複製

第一個參數[module],是數組,裡面的成員是要載入的模組,callback是載入完成後的回呼函數。如果將上述的程式碼改成AMD方式:

require(['math'], function(math) {
  math.add(2, 3);})
登入後複製

其中,回呼函數中參數對應數組中的成員(模組)。

requireJS載入模組,採用的是AMD規格。也就是說,模組必須按照AMD規定的方式來寫。

具體來說,就是模組書寫必須使用特定的define()函數來定義。如果一個模組不依賴其他模組,那麼可以直接寫在define()函數之中。

define(id, dependencies, factory);
登入後複製
  • id:模組的名字,如果沒有提供該參數,模組的名字應該預設為模組載入器請求的指定腳本名字;
  • dependencies:模組的依賴,已被模組定義的模組標識的陣列字面量。依賴參數是可選的,如果忽略此參數,它應該預設為 ["require", "exports", "module"]。然而,如果工廠方法的長度屬性小於3,載入器會選擇以函數的長度屬性指定的參數個數呼叫工廠方法。
  • factory:模組的工廠函數,模組初始化要執行的函數或物件。如果為函數,它應該只被執行一次。如果是對象,此對象應該為模組的輸出值。

假定現在有一個math.js文件,定義了一個math模組。那麼,math.js書寫方式如下:

// math.jsdefine(function() {
  var add = function(x, y) {
    return x + y;
  }

  return  {
    add: add  }})
登入後複製

載入方法如下:

// main.jsrequire(['math'], function(math) {
  alert(math.add(1, 1));})
登入後複製

如果math模組也依賴其他模組,寫法如下:

// math.jsdefine(['dependenceModule'], function(dependenceModule) {
    // ...})
登入後複製

require()函數載入math模組的時候,就會先載入dependenceModule模組。當有多個依賴時,就將所有的依賴都寫在define()函數第一個參數數組中,所以說AMD是依賴前置的。這不同於CMD規範,它是依賴就近的。
CMD

三、CMD-同步模組定義

CMDCommon Module Definition通用模組定義,是SeaJS在推廣過程中對模組定義的規範化產出,是一個同步模組定義,是SeaJS的一個標準,SeaJSCMD概念的一個實現,SeaJS是淘寶團隊玉伯提供的一個模組開發的js框架。 CMD規格是國內發展出來的,就像AMD有個requireJSCMD有個瀏覽器的實作SeaJS SeaJS要解決的問題和requireJS一樣,只不過在模組定義方式和模組載入(可以說運行、解析)時機上有所不同。

CMD 通过define()定义,没有依赖前置,通过require加载jQuery插件,CMD是依赖就近,在什么地方使用到插件就在什么地方require该插件,即用即返,这是一个同步的概念。

CMD 规范中,一个模块就是一个文件。代码的书写格式如下:

define(function(require, exports, module) {
  // 模块代码});
登入後複製

其中,

  • require是可以把其他模块导入进来的一个参数;
  • exports是可以把模块内的一些属性和方法导出的;
  • module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。

AMD是依赖关系前置,在定义模块的时候就要声明其依赖的模块;
CMD是按需加载依赖就近,只有在用到某个模块的时候再去require,示例代码如下:

// CMDdefine(function(require, exports, module) {
  var a = require('./a')
  a.doSomething()
  // 此处略去 100 行
  var b = require('./b') // 依赖可以就近书写
  b.doSomething()
  // ... })// AMD 默认推荐的是define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
  a.doSomething()
  // 此处略去 100 行
  b.doSomething()
  ...})
登入後複製

四、CommonJS 规范

CommonJS规范是通过module.exports定义的,在前端浏览器里面并不支持module.exports,通过node.js后端使用。Nodejs端使用CommonJS规范,前端浏览器一般使用AMDCMDES6等定义模块化开发规范。

CommonJS的终极目标是提供一个类似PythonRubyJava的标准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用就可以运行在不同的JavaScript解释器和不同的主机环境中。

在兼容CommonJS的系统中,你可以使用JavaScript开发以下程序:

  1. 服务器端JavaScript应用程序;
  2. 命令行工具;
  3. 图形界面应用程序;
  4. 混合应用程序(如,Titanium或Adobe AIR);

2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志"Javascript模块化编程"正式诞生。NodeJSCommonJS规范的实现,webpack 也是以CommonJS的形式来书写。

node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

var math = require('math');
登入後複製

然后,就可以调用模块提供的方法:

var math = require('math');math.add(2,3); // 5
登入後複製

CommonJS定义的模块分为:模块引用(require)模块定义(exports)模块标识(module)
其中,

  • require()用来引入外部模块;
  • exports对象用于导出当前模块的方法或变量,唯一的导出口;
  • module对象就代表模块本身。

虽说NodeJS遵循CommonJS的规范,但是相比也是做了一些取舍,添了一些新东西的。

NPM作为Node包管理器,同样遵循CommonJS规范。

下面讲讲commonJS的原理以及简易实现:

1、原理
浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境变量。

module
exports
require
global
登入後複製

只要能够提供这四个变量,浏览器就能加载 CommonJS 模块。

下面是一个简单的示例。

var module = {
  exports: {}};(function(module, exports) {
  exports.multiply = function (n) { return n * 1000 };
  }(module, module.exports))var f = module.exports.multiply;
  f(5) // 5000
登入後複製

上面代码向一个立即执行函数提供 module 和 exports 两个外部变量,模块就放在这个立即执行函数里面。模块的输出值放在 module.exports 之中,这样就实现了模块的加载。

2、Browserify 的实现
Browserify 是目前最常用的 CommonJS 格式转换工具。

请看一个例子,main.js 模块加载 foo.js 模块。

// foo.jsmodule.exports = function(x) {
  console.log(x);};// main.jsvar foo = require("./foo");foo("Hi");
登入後複製

使用下面的命令,就能将main.js转为浏览器可用的格式。

$ browserify main.js > compiled.js
登入後複製

其中,Browserify到底做了什么?安装一下browser-unpack,就清楚了。

$ npm install browser-unpack -g
登入後複製

然后,将前面生成的compile.js解包。

$ browser-unpack < compiled.js
登入後複製
[
  {
    "id":1,
    "source":"module.exports = function(x) {\n  console.log(x);\n};",
    "deps":{}
  },
  {
    "id":2,
    "source":"var foo = require(\"./foo\");\nfoo(\"Hi\");",
    "deps":{"./foo":1},
    "entry":true
  }]
登入後複製

可以看到,browerify 将所有模块放入一个数组,id 属性是模块的编号,source 属性是模块的源码,deps 属性是模块的依赖。

因为 main.js 里面加载了 foo.js,所以 deps 属性就指定 ./foo 对应1号模块。执行的时候,浏览器遇到 require(&#39;./foo&#39;) 语句,就自动执行1号模块的 source 属性,并将执行后的 module.exports 属性值输出。

五、ES6

有关es6模块特性,强烈推荐阮一峰老师的:ECMAScript 6 入门 - Module 的语法专栏。

要说 ES6 模块特性,那么就先说说 ES6 模块跟 CommonJS 模块的不同之处。

  • ES6 模块输出的是值的引用,输出接口动态绑定,而 CommonJS 输出的是值的拷贝;
  • ES6 模块编译时执行,而 CommonJS 模块总是在运行时加载。

CommonJS 模块输出的是值的拷贝(原始值的拷贝),也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

// a.jsvar b = require(&#39;./b&#39;);console.log(b.foo);setTimeout(() => {
  console.log(b.foo);
  console.log(require('./b').foo);}, 1000);// b.jslet foo = 1;setTimeout(() => {
  foo = 2;}, 500);module.exports = {
  foo: foo,};// 执行:node a.js// 执行结果:// 1// 1// 1
登入後複製

上面代码说明,b 模块加载以后,它的内部 foo 变化就影响不到输出的 exports.foo 了。这是因为 foo 是一个原始类型的值,会被缓存。所以如果你想要在 CommonJS 中动态获取模块中的值,那么就需要借助于函数延时执行的特性。

// a.jsvar b = require('./b');console.log(b.foo);setTimeout(() => {
  console.log(b.foo);
  console.log(require('./b').foo);}, 1000);// b.jsmodule.exports.foo = 1;   // 同 exports.foo = 1 setTimeout(() => {
  module.exports.foo = 2;}, 500);// 执行:node a.js// 执行结果:// 1// 2// 2
登入後複製

所以我们可以总结一下:

  • CommonJS 模块重复引入的模块并不会重复执行,再次获取模块直接获得暴露的module.exports 对象。
  • 如果你需要处处获取到模块内的最新值的话,也可以每次更新数据的时候每次都要去更新 module.exports 上的值
  • 如果暴露的 module.exports 的属性是个对象,那就不存在这个问题了。

相关推荐:javascript视频教程

以上是JavaScript模組化程式設計規範之CommonJS、AMD、CMD、ES6的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:csdn.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板