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的方式加載

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