ES6和commonJs在webpack下的混用
爲了最大化利用ES6規範不引入無關代碼從而減小打包體積的優勢,越來越多的模塊支持同時將自己的模塊發佈成commonJs和ES6規範的的文件,在package.json
中的"module"
字段配置ES6模塊入口文件路徑,現在像webpack,rollup都已經支持該字段,在打包過程中不再引入package.json
中"main"
字段對應的文件,而是引入"module"
文件對應的文件(如果包中的package.json中有module字段的話)
所以,我們先明確下面兩點:
- 在運行時引入模塊,引入的是
package.json
中"main"
指向的文件 - 在webpack打包或者webpack-dev-server的時候,引入的是
package.json
中"module"
指向的文件
爲了方便測試,我們先在node工程的"node_modules"文件夾下自己創建一個名爲aaa
的模塊,文件結構如下:
|-- node_modules
|--|-- aaa
|--|--|-- package.json
|--|--|-- aaa.js
|--|--|-- aaa.module.js
// package.json
{
"main": "aaa.js",
"module": "aaa.module.js"
}
示例
最近在看Three.js,發現這個包就同時支持require和import,而且該包根目錄的package.json中有指明module字段,module字段就指向該包的es6模塊js文件,我看了下該包的兩個入口文件three.js
和three.module.js
,我這裏簡單模擬一下:
// aaa.ja
exports.kkk = "hello c"
exports.kkm = "world c"
// aaa.module.js
var kkk = "hello m"
var kkm = "world m"
export {
kkk, kkm
}
// index.js
// import * as aaa from 'aaa' // 通過webpack打包時,這種加載方式也行
var aaa = require('aaa')
console.log(aaa.kkk) // 直接運行輸出"hello c",webpack打包後輸出"hello m"
console.log(aaa.kkm) // 直接運行輸出"world c",webpack打包後輸出"world m"
通過以上方式,index.js在直接運行時,和被webpack打包後,輸出的信息不相同,可見運行時和webpack引用的文件時不同的。
webpack具體是如何實現兩者間的混用的,筆者就無恥的略過了(筆者自己也不知道咯),不過從webpack打包出的文件可以看點端倪,應該是webpack識別出了CommonJs和ES6的導入導出部分的代碼,然後轉換成了統一的函數,這樣就能實現兩種規範的混用了
那麼拋開webpack,兩種規範的文件能夠互相加載嗎?
import如何加載commonJs模塊
假如有個commonJs規範的模塊:
// aaa.js
exports.kkk = "hello c"
exports.kkm = "world c"
有三種方法通過import引入
1,直接import
import aaa from 'aaa'
console.log(aaa) // { kkk: 'hello c', kkm: 'world c' }
注意: import
命令加載 commonJS 模塊,只能整體加載,不能只加載單一的輸出項
import {kkk} from 'aaa'
console.log(kkk) // 報錯:SyntaxError: The requested module 'aaa' does not provide an export named 'kkk'
2,使用Node.js 內置的module.createRequire()
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const aaa = require('aaa');
console.log(aaa) // { kkk: 'hello c', kkm: 'world c' }
3,使用babel-loader將import轉成require
這種方法其實算不上時通過import引入的,因爲它的原理是將import轉成了require,因爲我們在開發項目過程中,爲了更好啊兼容性,免不了是要使用babel-loader的,所以我們這裏來小小嚐試一下babel-loader將import轉成require的規則
import {bbb} from 'bbb'
var b = new bbb();
// babel轉化後
"use strict";
var _bbb = require("bbb");
var b = new _bbb.bbb();
import aaa from 'aaa'
var a = new aaa();
// babel轉化後
"use strict";
var _aaa = _interopRequireDefault(require("aaa"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
var a = new _aaa["default"]();
import * as ccc from 'ccc'
var c = new ccc();
// babel轉化後
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var ccc = _interopRequireWildcard(require("ccc"));
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
var c = new ccc();
require如何加載ES6模塊
CommonJS 的require命令不能直接加載 ES6 模塊,那麼我們可以將es6的模塊通過babel-loader轉成commonJs的模塊試試
// b.js
var aa = "kkm"
var bb = "kkk"
export {
aa, bb
}
// babel轉化後
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.bb = exports.aa = void 0;
var aa = "kkm";
exports.aa = aa;
var bb = "kkk";
exports.bb = bb;
// b.js
export default 'kkk'
// babel轉化後
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _default = 'kkk';
exports["default"] = _default;
注意,這種通過export default導出接口的文件,我們在require時爲了方便,可以通過
require(./b.js).default
的方式加載