前言
我們都知道,瀏覽器是無法識別commonjs規範的模組和es6 module的。將這些規範的模組轉化為瀏覽器認識的語句就是webpack做的最基本事情,webpack 本身維護了一套模組系統,這套模組系統相容了所有前端歷史程式下的模組規範,包括 amd commonjs es6 等。當然babel也具有將es6模組轉化的能力(parcel我不想提你),但是由於webpack 具有tree-shaking的功能,比起babel來更加具有優勢。所以一般babel配置裡都會禁止掉babel的module功能。(["es2015", {"modules": false}])。
commonjs規範
專案結構:
- app.js(entry):
var c = require('./c')
console.log(c)
複製程式碼
- c.js(entry):
let c1 = 'c1'
let c2 = 'c2'
module.exports = {
c1,
c2,
}
複製程式碼
打包結果:
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["app"] = factory();
else
root["app"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return (function(modules) { // webpackBootstrap
// The module cache
var installedModules = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = 0);
})
/************************************************************************/
([
/* 0 */
(function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
}),
/* 1 */
(function(module, exports, __webpack_require__) {
"use strict";
var c = __webpack_require__(2);
console.log(c);
module.exports = {
a: '我是a'
};
}),
/* 2 */
(function(module, exports) {
var c1 = 'c1';
var c2 = 'c2';
module.exports = {
c1: c1,
c2: c2
};
})
]);
});
複製程式碼
解析:
打包生成的是個立即執行函式,簡化來寫就是
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["app"] = factory();
else
root["app"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {解析完的模組部分})
複製程式碼
可以看到模組部分被作為factory引數傳入了webpackUniversalModuleDefinition中,如果檢測到module.exports有定義,那麼模組賦值給module.exports;如果檢測到amd的模組系統有定義,賦值給define的模組系統;最後如果上述模組系統都未檢測到,賦值給webpack.output.library定義的全域性變數。瀏覽器可以通過window.app拿到解析好的模組。
下面看模組解析部分
factory:
function(){
return (function(modules){
解析模組的方法
})([function(){模組1},function(){模組1},...])
}
複製程式碼
factory方法最後return出去的就是webpack entry的js:app.js暴露的值。
解析模組的方法:
var installedModules = {};
function __webpack_require__(moduleId) {
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}
__webpack_require__.m = modules;
__webpack_require__.c = installedModules;
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
__webpack_require__.p = "";
return __webpack_require__(__webpack_require__.s = 0);
複製程式碼
-
定義了installedModules ,這個變數被用來快取已載入的模組。
-
定義了__webpack_require__ 這個函式,函式引數為模組的id。這個函式用來實現模組的require。
-
webpack_require 函式首先會檢查是否快取了已載入的模組,如果有則直接返回快取模組的exports。
-
如果沒有快取,也就是第一次載入,則首先初始化模組,並將模組進行快取。初始化->
{ i: moduleId, l: false, exports: {} } 複製程式碼
-
然後呼叫模組函式,也就是前面webpack對我們的模組的包裝函式,將module、module.exports和__webpack_require__作為引數傳入。注意這裡做了一個動態繫結,將模組函式的呼叫物件繫結為module.exports,這是為了保證在模組中的this指向當前模組。
-
呼叫完成後,模組標記為已載入。
-
返回模組exports的內容。
-
利用前面定義的__webpack_require__ 函式,require第0個模組,也就是入口模組。
https://segmentfault.com/a/1190000010349749
再看編號為 0 的模組
function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
}
複製程式碼
直接讓expoprts = webpack_require(1),此時
installedModules[0] = {i: 1, l: true, exports: __webpack_require__(1)}
複製程式碼
再看編號為 1 的模組
function(module, exports, __webpack_require__) {
"use strict";
var c = __webpack_require__(2);
console.log(c);
module.exports = {
a: '我是a'
}
}
複製程式碼
直接讓module.exports = {a: '我是a'},此時:
installedModules[1] = {i: 1, l: true, exports: {a:'我是a'}}
複製程式碼
再看編號為 2 的模組
function(module, exports) {
var c1 = 'c1';
var c2 = 'c2';
module.exports = {
c1: c1,
c2: c2
};
}
複製程式碼
直接讓module.exports = {c1: 'c1',c2: 'c2'},因為2模組沒有require其他模組,因此沒有接收到__webpack_require__。此時:
installedModules[2] = {i: 2, l: true, exports: {c1: 'c1',c2: 'c2'}}
複製程式碼
結束,現在installedModules的結果是
{
0: {i: 0, l: true, exports: {a:'我是a'}}
1: {i: 1, l: true, exports: {a:'我是a'}}
2: {i: 2, l: true, exports: {c1: 'c1',c2: 'c2'}}
}
複製程式碼
factory函式要return的是return __webpack_require__(__webpack_require__.s = 0);
因此入口模組:app.js的返回結果是{a:'我是a'},同時window.app = {a:'我是a'}
結論:webpack使用自定義的__webpack_require__函式實現了commonjs require的功能,並且使用installedModules變數儲存了module.exports的模組輸出。完成了對commonjs模組的轉化。
es6 module
專案結構:
- app.js(entry):
import c,{c1,c2} from './c';
console.log(c,c1,c2)
export default '我是a';
export let a = '我是aa';
複製程式碼
- c.js:
import b from './b'
console.log(b)
export let c1 = '我是c111'
export let c2 = '我是c222'
export default '我是c';
複製程式碼
- b.js:
export default 'bbb';
複製程式碼
打包結果:
與commonjs部分基本相同,只有模組部分解析的不同
模組0與上述相同。 下面看編號為 1 的模組(app.js)
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
__webpack_require__.d(__webpack_exports__, "aaaa", function() { return aaaa; });
var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(2);
console.log(__WEBPACK_IMPORTED_MODULE_0__c__["c" /* default */], __WEBPACK_IMPORTED_MODULE_0__c__["a" /* c1 */], __WEBPACK_IMPORTED_MODULE_0__c__["b" /* c2 */]);
__webpack_exports__["default"] = ('我是a');
var aaaa = '我是aaaa';
複製程式碼
結論:
-
因為app.js是es6的模組,所以webpack對該模組增加了__esModule屬性(true)。
-
由於es6模組有export default的功能,因此webpack把本模組暴露出的default屬性賦給了module.exports的default屬性。
-
注意:只有該模組是入口模組,並且是es6模組時,該模組export default的值才會被轉為module.exports的default屬性。
-
export 暴露的變數被轉化成了
__webpack_require__.d(__webpack_exports__, "aaaa", function() { return aaaa; });
,可以發現export暴露的變數名aaaa被原本的輸出了。 -
import語句被轉成了
var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(2);
,可以看出import c,{c1,c2}的語句webpack沒有處理,而是直接通過新變數__WEBPACK_IMPORTED_MODULE_0__c__接收了c模組的exports物件。所有用到c,c1,c2的地方都到 __WEBPACK_IMPORTED_MODULE_0__c__上取。
此時:
installedModules[1] = {
i: 1,
l: true,
exports: {
default:'我是a',
aaaa:"我是aaaa",
__esModule:true
}
}
複製程式碼
再看模組2(c.js):
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.d(__webpack_exports__, "a", function() { return c1; });
__webpack_require__.d(__webpack_exports__, "b", function() { return c2; });
var __WEBPACK_IMPORTED_MODULE_0__b__ = __webpack_require__(3);
console.log(__WEBPACK_IMPORTED_MODULE_0__b__["a" /* default */]);
var c1 = '我是c111';
var c2 = '我是c222';
__webpack_exports__["c"] = ('我是c');
}
複製程式碼
結論:
-
export 暴露的變數被轉化成了
__webpack_require__.d(__webpack_exports__, "a", function() { return c1; });
,可以發現與入口的es6模組不同的是:export暴露的變數名被隨機改變了。default被轉為了c, c1被轉為了a,c2被轉為了b。因此當入口模組(app.js)import了本模組並使用default,c1,c2屬性時,相應的被webpack對應改為了c,a,b(見模組1呼叫的地方) -
與入口模組不同的是,本模組的export default被轉化成了
__webpack_exports__["c"] = ('我是c');
,並沒有被轉化為default屬性,而是同樣被轉成了一個隨機屬性名。
此時:
installedModules[2] = {
i: 2,
l: true,
exports: {
a:"我是c111",
b:"我是c222",
c: "我是c"
}
}
複製程式碼
再看模組3
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_exports__["a"] = ('bbb');
}
複製程式碼
結論:
模組3的export default被轉化成了__webpack_exports__["a"] = ('bbb'); default被隨機改為了a,然後呼叫到地方同樣要使用 ['a']取值。
綜上,installedModules:
{
0: {i: 0, l: true, exports: {a:'我是a',_esModule:true}}
1: {i: 1, l: true, exports: {a:'我是a',_esModule:true}}
2: {i: 2, l: true, exports: {a:"我是c111", b:"我是c222",c: "我是c"}}
3: {i: 3, l: true, exports: {a:"bbb"}}
}
複製程式碼
結論:
-
入口模組如果為es6模組的話,會被新增__esModule ,值為true,表明這是一個es模組。而被入口的es6模組引用的其他es6模組不會被新增__esModule屬性。
-
es6模組作為入口模組時,export出去的default屬性和其他屬性名都會被原樣保留。default屬性通過
__webpack_exports__["default"] = ...
的方式匯出,其他屬性通過__webpack_require__.d(__webpack_exports__, "aaaa", function() { return aaaa; });
方式匯出。 -
es6模組不是入口模組而是被其他es6模組引用時,export出去的default屬性和其他屬性名都會被隨機賦予新的屬性名稱,例如
export default '我是c';
轉為了__webpack_exports__["c"] = ('我是c');
。default屬性同樣通過__webpack_exports__["c"] = ...
的方式匯出,其他屬性同樣通過__webpack_require__.d(__webpack_exports__, "b", function() { return c2; });
的方式匯出。 -
es6模組的import語法被轉化成了
var __WEBPACK_IMPORTED_MODULE_0__b__ = __webpack_require__(3);
。
commonjs模組與es6模組混用
情景一:es6引用commonjs
專案結構:
- app.js(entry):
import c,{c1,c2} from './c';
console.log(c,c1,c2)
export default '我是a';
export let aaaa = '我是aaaa';
複製程式碼
- c.js:
let c1 = 'c1'
let c2 = 'c2'
module.exports = {
c1,
c2,
}
複製程式碼
打包結果:
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["app"] = factory();
else
root["app"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return (function(modules) { // webpackBootstrap
var installedModules = {};
function __webpack_require__(moduleId) {
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}
__webpack_require__.m = modules;
__webpack_require__.c = installedModules;
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
__webpack_require__.p = "";
return __webpack_require__(__webpack_require__.s = 0);
})
/************************************************************************/
([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "aaaa", function() { return aaaa; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(2);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__c___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__c__);
console.log(__WEBPACK_IMPORTED_MODULE_0__c___default.a, __WEBPACK_IMPORTED_MODULE_0__c__["c1"], __WEBPACK_IMPORTED_MODULE_0__c__["c2"]);
/* harmony default export */ __webpack_exports__["default"] = ('我是a');
var aaaa = '我是aaaa';
/***/ }),
/* 2 */
/***/ (function(module, exports) {
var c1 = 'c1';
var c2 = 'c2';
module.exports = {
c1: c1,
c2: c2
};
/***/ })
/******/ ]);
});
複製程式碼
入口模組(app.js)為es6模組,引用的模組(c.js)是commonjs模組。
模組0依舊不變,看模組1(app.js)的打包情況
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
__webpack_require__.d(__webpack_exports__, "aaaa", function() { return aaaa; });
var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(2);
var __WEBPACK_IMPORTED_MODULE_0__c___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__c__);
console.log(__WEBPACK_IMPORTED_MODULE_0__c___default.a, __WEBPACK_IMPORTED_MODULE_0__c__["c1"], __WEBPACK_IMPORTED_MODULE_0__c__["c2"]);
__webpack_exports__["default"] = ('我是a');
var aaaa = '我是aaaa';
}
複製程式碼
可以發現模組1引用commonjs模組的地方打包結果發生了改變。
var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(2);
// 與import es6模組相比增加了以下部分
var __WEBPACK_IMPORTED_MODULE_0__c___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__c__);
複製程式碼
當發現引用模組是commonjs模組時,在呼叫__webpack_require__()之後,還會呼叫__webpack_require__.n,
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
複製程式碼
本方法的作用是:如果傳入模組是es6模組轉化成的commonjs模組,即__esModule=true,那麼返回的是該模組的default屬性的值,如果傳入的模組原來就是commonjs模組,返回模組本身,並且令該模組的a屬性 = 模組本身。結果就是生成了兩個變數__WEBPACK_IMPORTED_MODULE_0__c__和__WEBPACK_IMPORTED_MODULE_0__c___default。
__WEBPACK_IMPORTED_MODULE_0__c__={
c1,
c2,
}
__WEBPACK_IMPORTED_MODULE_0__c___default = function getModuleExports(){return module}
__WEBPACK_IMPORTED_MODULE_0__c___default.a = module
複製程式碼
結論:
- es6模組引用commonjs模組時,因為import name from '..'想取的是模組的default屬性,而commonjs模組沒有暴露default的方法,所以webpack將整個模組作為了default屬性的值輸出。
再看模組2:
function(module, __webpack_exports__) {
"use strict";
var c1 = 'c1';
var c2 = 'c2';
module.exports = {
c1: c1,
c2: c2
};
}
複製程式碼
原樣輸出
綜上:
-
es6呼叫commonjs模組,import 預設值的情況會特殊處理
-
被引用的commonjs模組會原樣輸出。
情景二:commonjs呼叫es6模組
專案結構:
- app.js(entry):
var b = require('./b')
var c = require('./c')
console.log(b.default)
console.log(c)
module.exports = {
a:'我是a'
}
複製程式碼
- c.js:
import b from './b'
console.log(b)
export let c1 = '我是c111'
export let c2 = '我是c222'
export default '我是c';
複製程式碼
打包結果:
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["app"] = factory();
else
root["app"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return (function(modules) { // webpackBootstrap
// The module cache
var installedModules = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = 0);
})
/************************************************************************/
([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var b = __webpack_require__(2);
var c = __webpack_require__(3);
console.log(b.default);
console.log(c);
module.exports = {
a: '我是a'
};
/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
__webpack_exports__["default"] = ('bbb');
/***/ }),
/* 3 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
__webpack_require__.d(__webpack_exports__, "c1", function() { return c1; });
__webpack_require__.d(__webpack_exports__, "c2", function() { return c2; });
var __WEBPACK_IMPORTED_MODULE_0__b__ = __webpack_require__(0);
console.log(__WEBPACK_IMPORTED_MODULE_0__b__["default"]);
var c1 = '我是c111';
var c2 = '我是c222';
/***/ })
/******/ ]);
});
複製程式碼
入口模組(app.js)為commonjs模組,引用的模組(b.js,c.js)是es6模組。
模組0依舊不變,看模組1(app.js)的打包情況
function(module, exports, __webpack_require__) {
"use strict";
var b = __webpack_require__(2);
var c = __webpack_require__(3);
console.log(b.default);
console.log(c);
module.exports = {
a: '我是a'
}
}
複製程式碼
僅僅是使用__webpack_require__方法替代了原來的require方法
再看模組2(b.js)和模組3(c.js)
b.js
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
__webpack_exports__["default"] = ('bbb');
}
複製程式碼
c.js
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
__webpack_require__.d(__webpack_exports__, "c1", function() { return c1; });
__webpack_require__.d(__webpack_exports__, "c2", function() { return c2; });
var c1 = '我是c111';
var c2 = '我是c222';
__webpack_exports__["default"] = ('我是c');
}
複製程式碼
結論:
-
可以發現commonjs模組引用es6模組,被引用的es6模組會被增加__esModule屬性(true)
-
export語法被轉為了
__webpack_require__.d(__webpack_exports__, "c1", function() { return c1; });
。 -
export default語句被轉為了
__webpack_exports__["default"] = ('我是c');
-
使用import b from './b'呼叫es6模組,如果需要呼叫b的預設值,需要用
__WEBPACK_IMPORTED_MODULE_0__b__["default"];
-
使用import b from './b'呼叫commonjs模組,需要以下方式:
var __WEBPACK_IMPORTED_MODULE_0__b__ = __webpack_require__(0); var __WEBPACK_IMPORTED_MODULE_0__b___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__b__); console.log(__WEBPACK_IMPORTED_MODULE_0__b___default.a); 複製程式碼
綜上:
-
commonjs模組被打包時,require部分會被__webpack_require__函式替代,其他部分原樣輸出。
-
commonjs模組被import時,由於commonjs模組沒有暴露預設值的功能,所以import預設值的語法會被解析成:
// import c from './c'被解析成: var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(2); var __WEBPACK_IMPORTED_MODULE_0__c___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__c__); // 使用c時被解析成: console.log(__WEBPACK_IMPORTED_MODULE_0__c___default.a) 複製程式碼
-
即使在es6模組中使用require()引用,不管require的是es6模組還是commonjs模組,都只會被簡單的解析成
var c = __webpack_require__(2);
。可以認為commonjs規範的require會被__webpack_require__直接替換。 -
es6模組被require時(不論require語句出現在commonjs還是es6的模組裡),es6模組都會被新增__esModule=true。es6模組被import時則都不會新增__esModule屬性。es6模組被當做入口模組時,也會被新增__esModule=true,可以認為做為入口模組時的命運就是會被打包成commonjs模組暴露出去,此時就需要一個變數來標識我以前是es6模組,只不過被強行變成了commonjs模組,一經打包完成,本模組再被引用時將不會觸發tree-shaking功能。
-
es6模組被直接import時,會觸發webpack的tree-shaking功能,可以認為webpack只有對es6模組進行靜態解析後才能呼叫tree-shaking。
-
es6模組被打包時,export語句被解析成:
__webpack_require__.d(__webpack_exports__, "a", function() { return c1; }); 複製程式碼
export default語句被解析成:
__webpack_exports__["c"] = ('我是c'); 複製程式碼
當然所有的屬性名都是被隨機賦予了新的名稱,一般是按a,b,c,d...的順序。
例外情況:如果es6模組被當做入口模組,export和export default語句暴露的屬性名會保留,例如:
__webpack_require__.d(__webpack_exports__, "aaaa", function() { return aaaa; }); __webpack_exports__["default"] = ('我是a'); 複製程式碼
-
es6模組被import時,import語句會被解析成:
var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(2); 複製程式碼
不會出現__webpack_require__.n的使用。
此時呼叫es6模組暴露出的所有屬性都通過__WEBPACK_IMPORTED_MODULE_0__c__['隨機屬性名']的方式,例如:
console.log(__WEBPACK_IMPORTED_MODULE_0__c__["c" /* default */]) 複製程式碼
-
es6模組被require時,簡單的多:
var c = __webpack_require__(2); 複製程式碼
呼叫任何屬性都通過c直接呼叫。
簡述:
import->__webpack_require__ 和 __webpack_require__.n((引用commonjs模組時出現))
require->__webpack_require__
export->__webpack_require__.d
export default->__webpack_exports__[".."]
module.exports->不變
複製程式碼
webpack模組化原理-ES module import、require、export、module.exports 混合使用詳解
根據以上思路,如果實現了檔案按路徑載入,就能寫出一個簡單的模組化工具了。