为了最大化利用ES6规范不引入无关代码从而减小打包体积的优势,越来越多的模块支持同时将自己的模块发布成commonJs和ES6规范的的文件,在package.json
中的"module"
字段配置ES6模块入口文件路径,现在像webpack,rollup都已经支持该字段,在打包过程中不再引入package.json
中"main"
字段对应的文件,而是引入"module"
文件对应的文件(如果包中的package.json中有module字段的话)
所以,我们先明确下面两点:
package.json
中"main"
指向的文件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,两种规范的文件能够互相加载吗?
假如有个commonJs规范的模块:
// aaa.js
exports.kkk = "hello c"
exports.kkm = "world c"
有三种方法通过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'
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const aaa = require('aaa');
console.log(aaa) // { kkk: 'hello c', kkm: 'world c' }
这种方法其实算不上时通过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();
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
的方式加载