模塊設計的思想是儘可能靜態化,使得編譯的時候就能夠肯定模塊的一來關係,以及輸入和輸出的變量。
CommonJS和AMD都只能在運行時肯定這些東西,commonJS模塊就是對象,輸入時須要查找對象屬性javascript
// CommonJS模塊 let { stat, exists, readFile } = require('fs'); // 等同於 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile;
nodeJS 中模塊化使用的就是CommonJS的規範,實質就是總體加載fs模塊,生成fs_對象,在對象上讀取屬性和方法,
這種加載方式是「運行時加載」html
ES6 模塊不是對象,而是經過export命令顯式指定輸出的代碼,再經過import命令輸入。
// ES6模塊 import { stat, exists, readFile } from 'fs';
上面代碼的實質是從fs模塊加載 3 個方法,其餘方法不加載。這種加載稱爲「編譯時加載」或者靜態加載,即 ES6 能夠在編譯時就完成模塊加載,效率要比 CommonJS 模塊的加載方式高。java
一個模塊就是一個獨立的文件。該文件內部的全部變量,外部沒法獲取。若是你但願外部可以讀取模塊內部的某個變量,就必須使用export關鍵字輸出該變量。node
// profile.js export var firstName = 'Michael'; export var lastName = 'Jackson'; export var year = 1958; // profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year};
export的語法,對外導出接口,在接口名與模塊內部變量之間,創建了一一對應的關係。es6
// 寫法一 export var m = 1; // 寫法二 var m = 1; export {m}; // 寫法三 var n = 1; export {n as m};
注意,import命令具備提高效果,會提高到整個模塊的頭部,首先執行。json
目前階段,經過 Babel 轉碼,CommonJS 模塊的require命令和 ES6 模塊的import命令,能夠寫在同一個模塊裏面,可是最好不要這樣作。由於import在靜態解析階段執行,因此它是一個模塊之中最先執行的。下面的代碼可能不會獲得預期結果。數組
require('core-js/modules/es6.symbol'); require('core-js/modules/es6.promise'); import React from 'React';
export default命令用於指定模塊的默認輸出。顯然,一個模塊只能有一個默認輸出,所以export default命令只能使用一次。因此,import命令後面纔不用加大括號,由於只可能惟一對應export default命令。promise
本質上,export default就是輸出一個叫作default的變量或方法,而後系統容許你爲它取任意名字。因此,下面的寫法是有效的。緩存
// modules.js function add(x, y) { return x * y; } export {add as default}; // 等同於 // export default add; // app.js import { default as foo } from 'modules'; // 等同於 // import foo from 'modules'; // 正確 export var a = 1; // 正確 var a = 1; export default a; // 錯誤 export default var a = 1;
每一個文件就是一個模塊,有本身的做用域。在一個文件裏面定義的變量、函數、類,都是私有的,對其餘文件不可見。
CommonJS規範規定,每一個模塊內部,module變量表明當前模塊。這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,實際上是加載該模塊的module.exports屬性。app
var x = 5; var addX = function (value) { return value + x; }; module.exports.x = x; module.exports.addX = addX; // 使用 var example = require('./example.js'); console.log(example.x); // 5 console.log(example.addX(1)); // 6
export var foo = 'bar'; setTimeout(() => foo = 'baz', 500);
ES6 模塊化上面代碼輸出變量foo,值爲bar,500 毫秒以後變成baz。
這一點與 CommonJS 規範徹底不一樣。CommonJS 模塊輸出的是值的緩存,不存在動態更新
Node內部提供一個Module構建函數。全部模塊都是Module的實例
module.id 模塊的識別符,一般是帶有絕對路徑的模塊文件名。
module.filename 模塊的文件名,帶有絕對路徑。
module.loaded 返回一個布爾值,表示模塊是否已經完成加載。
module.parent 返回一個對象,表示調用該模塊的模塊。
module.children 返回一個數組,表示該模塊要用到的其餘模塊。
module.exports 表示模塊對外輸出的值。
module.exports屬性表示當前模塊對外輸出的接口,其餘文件加載該模塊,實際上就是讀取module.exports變量。
爲了方便,Node爲每一個模塊提供一個exports變量,指向module.exports。這等同在每一個模塊頭部,有一行這樣的命令。
var exports = module.exports;
形成的結果是,在對外輸出模塊接口時,能夠向exports對象添加方法。
exports.area = function (r) { return Math.PI * r * r; }; exports.circumference = function (r) { return 2 * Math.PI * r; };
注意,不能直接將exports變量指向一個值,由於這樣等於切斷了exports與module.exports的聯繫。
exports.hello = function() { return 'hello'; }; module.exports = 'Hello world';
面代碼中,hello函數是沒法對外輸出的,由於module.exports被從新賦值了。
這意味着,若是一個模塊的對外接口,就是一個單一的值,不能使用exports輸出,只能使用module.exports輸出。
和node中模塊加載規則一致
一般,咱們會把相關的文件會放在一個目錄裏面,便於組織。這時,最好爲該目錄設置一個入口文件,讓require方法能夠經過這個入口文件,加載整個目錄。
在目錄中放置一個package.json文件,而且將入口文件寫入main字段。下面是一個例子。
// package.json { "name" : "some-library", "main" : "./lib/some-library.js" }
require發現參數字符串指向一個目錄之後,會自動查看該目錄的package.json文件,而後加載main字段指定的入口文件。若是package.json文件沒有main字段,或者根本就沒有package.json文件,則會加載該目錄下的index.js文件或index.node文件。