模塊標准: CommonJS、AMD、CMD、ES6 Module
CommonJS是Node.js原生支持的模塊標准. 使用module.exports和require()
函數.
AMD和CMD比較相似, AMD的實現有require.js, CMD的實現有Sea.js.
ES6 Module引入import
和export
兩個關鍵字, 是Webpack推薦的模塊標准.
export 與 import
export
首先module.exports = ...
和export
關鍵字不能混用, 在Wbpack中優先使用export
關鍵字.
export
對一個Module對象進行操作, 而不是普通的js對象, 最后module.exports
賦值為該對象以導出.
使用ES6語法導出的是一個Module對象, 根據default字段, 有"默認導入"和"具名導出"兩種說法.
let value = 88;
let value2 = 66;
function printValue() {
console.log(value);
}
// 導出"默認"變量作為一個{}對象
export default {
value2: value, // 導出變量的同時重命名
printValue,
};
// export default value; // 也可以將"默認"變量導出為一個變量或函數, 但不能多次導出"默認"變量
// export default value2 = value; // 重命名
// 具名導出
export { value as value2, value2 as value }; // 導出多個非默認變量, 同時重命名別名
// export { value: value2, value2: value }; // 語法錯誤
export var foo = printValue; // 導出單個非默認變量, 同時重命名
console.log({ foo }); // 現在 foo 是合法變量
import
import會判斷對應模塊入口文件(通常是/index.js
)中module.exports
是普通js對象還是Module
對象, 從而實現兼容:
如果是
Module
對象, 則有默認導出和其它導出(具名導出)之分
如果是普通js對象, 那么module.exports就是默認導出
import * as A from './test'; // 等價於C, 這種語法通常在使用import關鍵字引用瀏覽器和服務器通用模塊時使用, 因為這些模塊不導出Module對象
import B from './test'; // 默認導入
const C = require('./test'); // Node.js原始require語法, 可理解為偽代碼: C = test.module.exports;
console.log({ A });
console.log({ B });
console.log({ C });
// 更新
import { default as D } from './test'; // 等價於B
高階用法
集線器
可以將一個js文件作為路由集線器, 導入其它js的非默認同時導出:
export { DefaultLoadingManager, LoadingManager } from './loaders/LoadingManager.js';
我們將之前的文件命名為test2.js
, 在test1.js
中路由:
export { default } from './test2'; // 導出default
export { foo } from './test2'; // 導出非默認變量foo
export * from './test2'; // 導出全部非默認變量. 也就是說導出不會包含含default字段
// 也就是說, 導出test2全部可導成員需要以下兩步
export * from './test2';
export { default } from './test2';
// 有時我們需要導入默認導出, 又需要導入普通的CommonJS導出, 可以這樣寫:
import React, { Component } from 'react';
// 那么之前的導出全部成員可以這么寫嗎? (疑問)
export *, { default } from './test2';
// 導入這樣寫可以嗎? (疑問) 不可以!!!
import * as React, { Component } from 'react';
構建通用包時, 建議不使用ES6模塊
以下四種React的引入是一樣的,顯然react包沒有使用ES6模塊:
import ReactA, { createElement as e, default as ReactB } from 'react';
import * as ReactC from 'react';
const ReactD = require('react');
console.log(ReactA === ReactB); // true
console.log(ReactA === ReactC);
console.log(ReactA === ReactD);
import一個目錄
當我們導入一個目錄時, 會嘗試執行該目錄下的index.js
文件. 並且目錄優先於同名的js文件, 除非導入時顯式指定.js
后綴.
在Windows下文件名不區分大小寫, 所以Index.js
也可以編譯, 但是在Linux下就完蛋了...!這也算是一個坑吧
異步import
function(string loader_and_file): Promise
動態加載模塊。調用 import()
之處被視為分割點,意思着被請求的模塊和它引用的所有子模塊,會分割到一個單獨的 chunk 中。
異步import, 模塊的處理過程將作為一個分割點單獨打包為獨立模塊, 再通過ajax請求, 請求路徑是相對於引用腳本的URL, 所以對於單頁應用來說沒有問題.
// "."表示當前js文件, 使用raw-loader異步加載當前js文件的文本內容
import('!raw-loader!.').then(({ default: text }) => {
this.setState({ code: text });
});
非入口點文件
通過異步import打包后的文件稱為非入口點文件, 文件名可以通過Webpack配置中的config.output.chunkFilename確定:
output: {
filename: '[name].js',
path: DIR_DIST,
chunkFilename: 'async/[id].js', // 此選項確定非入口塊文件的名稱
},
Magic Comments可以通過js注釋的方式控制每一個異步import.
官方文檔
https://webpack.docschina.org/api/module-methods/
異步import及其魔法注釋
https://webpack.js.org/api/module-methods/#magic-comments