github地址:
一直以來對CommonJs/AMD/CMD/ES6的文件模塊加載一直懵懵懂懂。甚至有時會將CommonJs的exports和ES6的export.default搞混。趁着學習webpack,先搞懂這些模塊加載方式再說!!!
隨着前端的發展,我們日常開發的功能越來越復雜,文件越來越多。隨后前端社區衍生出了CommonJs/AMD/CMD/ES6的幾種模塊加載方式。
模塊加載方式
01: CommonJs
參考地址:阮一峰老師講解的CommonJs規范
每個文件都是一個單獨的模塊,有自己的作用域。在一個文件里面定義的變量、函數、類,都是私有的,對其他文件不可見。
CommonJS
規范規定,每個模塊內部,module
變量代表當前模塊。這個變量是一個對象,它的exports
屬性(即module.exports
)是對外的接口。加載某個模塊,其實是加載該模塊的module.exports
屬性。
CommonJs的特點
- 所有的代碼都運行在模塊作用域,不會污染全局作用域;
- 模塊可以多次加載,但是只會在第一次加載時運行一次,然后運行的結果就會被緩存了,以后再加載就直接讀取緩存結果。要想讓模塊繼續運行,必須清空緩存;
- 模塊加載順序,按照其在代碼中出現的順序;
- CommonJs加載模塊是同步的;
Module對象
我們在demo01中創建兩個js文件,名為c1.js
,main.js
c1.js
每個模塊內部都有一個module
對象,代表當前模塊。它有以下屬性:
module.id
模塊的識別符,通常是帶有絕對路徑的模塊文件名。module.filename
模塊的文件名,帶有絕對路徑。module.loaded
返回一個布爾值,表示模塊是否已經完成加載。module.parent
返回一個對象,表示調用該模塊的模塊。module.children
返回一個數組,表示該模塊要用到的其他模塊。module.exports
表示模塊對外輸出的值。
console.log(module);
在命令行中執行
node c1.js
輸出結果為
Module {
id: '.',
exports: {},
parent: null,
filename: '/Users/Desktop/demo01/c1.js',
loaded: false,
children: [],
paths:
[] }
其中主要關注的是module.exports
這個屬性,它表示當前模塊對外輸出的接口,其他文件加載該模塊,實際上就是讀取module.exports變量。
注意:一下都在node環境中執行,也就是使用命令行node xxx.js
來執行的因為CommonJs是在node環境中運行的。
創建一個c2.js
文件
c2.js
創建一個對象c2
,通過module.exports
把該對象暴露,在main.js
中,使用require
進行接收。
let c2 = {
num: 5,
sum: function(a,b){
return a+ b
},
person: {
name: 'wbin',
age: '25'
}
}
module.exports = c2;
main.js
let c2 = require('./c2');
console.log(c2); // { num: 5,sum: [Function: sum],person: { name: 'wbin', age: '25' } }
如果要暴露具體內容
創建c3.js
c3.js
let c3 = {
num: 5,
sum: function(a,b){
return a+ b
},
person: {
name: 'wbin',
age: '25'
}
}
module.exports.num = c3.num;
module.exports.person = c3.person;
main.js
let {num, person} = require('./c3');
console.log(num, person); // 5 { name: 'wbin', age: '25' }
在c3.js
中,我暴露出c3
對象的兩個屬性,num
和person
,在main.js
中使用了ES6的對象擴展來接收對應的值。
module.exports
和exports
(這點只需要了解即可,在開發過程中,建議使用module.exports
)
為了方便,Node
為每個模塊提供一個exports
變量,指向module.exports
。這等同在每個模塊頭部,有一行這樣的命令
var exports = module.exports;
但是需要注意的是,不能直接將exports
變量指向一個值,這種行為相當於切斷了exports
和module.exports
的關系
在demo01中創建c4.js
c4.js
let c4 = {
num: 5,
sum: function(a,b){
return a+ b
},
person: {
name: 'wbin',
age: '25'
}
}
exports = c4;
main.js
let c4 = require('./c4');
console.log(c4); // {}
以上輸出結果是無效的,是一個空對象。
正確的寫法是
c4.js
let c4 = {
num: 5,
sum: function(a,b){
return a+ b
},
person: {
name: 'wbin',
age: '25'
}
}
exports.c4 = c4;
為了防止這種不必要的錯誤,建議使用module.exports
02: ES6
參考地址:《ES6入門》第22和23章
ES6模塊的設計是盡可能的靜態化,使得編輯時就能確定模塊之間的依賴關系,以及輸入和輸出變量。而CommonJs和AMD則是在運行時才能實現以上結果。
例如CommonJs模塊就是一個對象,輸入時必須查找對象屬性,而ES6模塊則可以暴露出任何變量、函數等。
所以說ES6模塊的加載方式是“編譯時“加載或者是靜態加載。
ES6模塊功能主要由兩個命令構成:export和import。export用來規定模塊的對外接口,import用來輸入其他模塊提供的功能。
demo02e1.js
可以使用export暴露變量
var firstName = 'w';
var lastName = 'bin';
var year = 1993;
export {firstName, lastName ,year};
也可以暴露fun
export function mul(a, b){
return a * b;
}
一般情況下,export輸出的變量就是本來的名字,但是可以使用as進行重命名。進行重命名之后我們可以給某個變量(可能是fun)這些進行多次輸出。
function add(a, b){
return a + b;
}
function reduce(a, b){
return a - b;
}
export {
add as sum,
reduce as mius,
reduce as jian
}
需要注意的是:ES6模塊的import/export
目前不支持在node環境中直接使用,可以使用webpack打包之后在瀏覽器中查看效果
使用import來加載某個模塊
e2.js
export let name = 'wbin';
export let age = 26;
main.js
import {name, age} from './e2';
console.log(name, age);
import命令接收一個大括號{},里面指定要從其他模塊加載的變量名。需要注意的是加載的變量名必須和export輸出的變量名一致。但是我們可以在improt中給該名稱重新命名。
import {name as wbin, age} from './e2';
console.log(wbin, age);
有時我們需要整體加載所需要的模塊,可以使用*號來加載
circle.js
export function area(radius) {
return (Math.PI * radius * radius);
}
export function circumference(radius){
return 2 * Math.PI * radius;
}
main.js
// 整體引入
import * as circle from './circle';
console.log(circle.area(2),circle.circumference(2));
默認輸出 export default
e3.js
export default function(){
return '123'
}
main.js
import name from './e3';
console.log(name()); // 123
注意:使用默認輸出時,import不使用{},使用正常輸出時,import需要使用{}!!!
03: AMD
AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義"。它采用異步方式加載模塊,模塊的加載不影響它后面語句的運行。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之后,這個回調函數才會運行。
AMD也采用require()語句加載模塊,它要求兩個參數:
require([module], callback);
在demo03文件夾中創建幾個文件 index.html,main.js,sum.js,all.js以及簡單的webpack配置 webpack.config.js
webpack.config.js
module.exports = {
entry: {
bundle: './main.js'
},
output: {
filename: '[name].js'
},
mode: 'development'
}
sum.js
define(function(){
return {
sum: function(a, b){
return a + b;
}
}
})
main.js
require(['./sum'],function(sum){
console.log(sum.sum(1,2));
})