ES6和commonJs模块化规范的混用

ES6和commonJs在webpack下的混用

为了最大化利用ES6规范不引入无关代码从而减小打包体积的优势,越来越多的模块支持同时将自己的模块发布成commonJs和ES6规范的的文件,在package.json中的"module"字段配置ES6模块入口文件路径,现在像webpack,rollup都已经支持该字段,在打包过程中不再引入package.json"main"字段对应的文件,而是引入"module"文件对应的文件(如果包中的package.json中有module字段的话)

所以,我们先明确下面两点:

  1. 在运行时引入模块,引入的是package.json"main"指向的文件
  2. 在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.jsthree.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的方式加载

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章