ES6和commonJs在webpack下的混用
为了最大化利用ES6规范不引入无关代码从而减小打包体积的优势,越来越多的模块支持同时将自己的模块发布成commonJs和ES6规范的的文件,在
所以,我们先明确下面两点:
- 在运行时引入模块,引入的是
package.json 中"main" 指向的文件 - 在webpack打包或者webpack-dev-server的时候,引入的是
package.json 中"module" 指向的文件
为了方便测试,我们先在node工程的"node_modules"文件夹下自己创建一个名为
1 2 3 4 5 | |-- node_modules |--|-- aaa |--|--|-- package.json |--|--|-- aaa.js |--|--|-- aaa.module.js |
1 2 3 4 5 | // package.json { "main": "aaa.js", "module": "aaa.module.js" } |
示例
最近在看Three.js,发现这个包就同时支持require和import,而且该包根目录的package.json中有指明module字段,module字段就指向该包的es6模块js文件,我看了下该包的两个入口文件
1 2 3 4 | // aaa.ja exports.kkk = "hello c" exports.kkm = "world c" |
1 2 3 4 5 6 7 | // aaa.module.js var kkk = "hello m" var kkm = "world m" export { kkk, kkm } |
1 2 3 4 5 6 7 | // 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规范的模块:
1 2 3 4 | // aaa.js exports.kkk = "hello c" exports.kkm = "world c" |
有三种方法通过import引入
1,直接import
1 2 | import aaa from 'aaa' console.log(aaa) // { kkk: 'hello c', kkm: 'world c' } |
注意:
1 2 | import {kkk} from 'aaa' console.log(kkk) // 报错:SyntaxError: The requested module 'aaa' does not provide an export named 'kkk' |
2,使用Node.js 内置的module.createRequire()
1 2 3 4 5 | 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的规则
1 2 3 4 5 6 7 | import {bbb} from 'bbb' var b = new bbb(); // babel转化后 "use strict"; var _bbb = require("bbb"); var b = new _bbb.bbb(); |
1 2 3 4 5 6 7 8 | 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"](); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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的模块试试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // 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; |
1 2 3 4 5 6 7 8 9 10 11 | // 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 的方式加载