webpack模塊化實現探究

前言

咱們都知道,瀏覽器是沒法識別commonjs規範的模塊和es6 module的。將這些規範的模塊轉化爲瀏覽器認識的語句就是webpack作的最基本事情,webpack 自己維護了一套模塊系統,這套模塊系統兼容了全部前端歷史進程下的模塊規範,包括 amd commonjs es6 等。固然babel也具備將es6模塊轉化的能力(parcel我不想提你),可是因爲webpack 具備tree-shaking的功能,比起babel來更加具備優點。因此通常babel配置裏都會禁止掉babel的module功能。(["es2015", {"modules": false}])。前端

commonjs規範

項目結構:

  1. app.js(entry):
var c = require('./c')
console.log(c)
複製代碼
  1. 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
};

 })
 ]);
});
複製代碼

解析:

打包生成的是個當即執行函數,簡化來寫就是webpack

(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拿到解析好的模塊。git

下面看模塊解析部分es6

factory:github

function(){
    return (function(modules){
        解析模塊的方法
    })([function(){模塊1},function(){模塊1},...])
}
複製代碼

factory方法最後return出去的就是webpack entry的js:app.js暴露的值。web

解析模塊的方法:segmentfault

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);
複製代碼
  1. 定義了installedModules ,這個變量被用來緩存已加載的模塊。瀏覽器

  2. 定義了__webpack_require__ 這個函數,函數參數爲模塊的id。這個函數用來實現模塊的require。緩存

  3. webpack_require 函數首先會檢查是否緩存了已加載的模塊,若是有則直接返回緩存模塊的exports。bash

  4. 若是沒有緩存,也就是第一次加載,則首先初始化模塊,並將模塊進行緩存。初始化->

    {
    	i: moduleId,
    	l: false,
    	exports: {}
    }
    複製代碼
  5. 而後調用模塊函數,也就是前面webpack對咱們的模塊的包裝函數,將module、module.exports和__webpack_require__做爲參數傳入。注意這裏作了一個動態綁定,將模塊函數的調用對象綁定爲module.exports,這是爲了保證在模塊中的this指向當前模塊。

  6. 調用完成後,模塊標記爲已加載。

  7. 返回模塊exports的內容。

  8. 利用前面定義的__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模塊的轉化。

webpack模塊化原理-commonjs

es6 module

項目結構:

  1. app.js(entry):
import c,{c1,c2} from './c';

console.log(c,c1,c2)

export default '我是a';
export let a = '我是aa';
複製代碼
  1. c.js:
import b from './b'
console.log(b)

export let c1 = '我是c111'
export let c2 = '我是c222'

export default '我是c';
複製代碼
  1. 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';
複製代碼

結論:

  1. 由於app.js是es6的模塊,因此webpack對該模塊增長了__esModule屬性(true)。

  2. 因爲es6模塊有export default的功能,所以webpack把本模塊暴露出的default屬性賦給了module.exports的default屬性。

  3. 注意:只有該模塊是入口模塊,而且是es6模塊時,該模塊export default的值纔會被轉爲module.exports的default屬性。

  4. export 暴露的變量被轉化成了__webpack_require__.d(__webpack_exports__, "aaaa", function() { return aaaa; });,能夠發現export暴露的變量名aaaa被本來的輸出了。

  5. 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');
}
複製代碼

結論:

  1. 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調用的地方)

  2. 與入口模塊不一樣的是,本模塊的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"}}
}
複製代碼

結論:

  1. 入口模塊若是爲es6模塊的話,會被添加__esModule ,值爲true,代表這是一個es模塊。而被入口的es6模塊引用的其餘es6模塊不會被添加__esModule屬性。

  2. es6模塊做爲入口模塊時,export出去的default屬性和其餘屬性名都會被原樣保留。default屬性經過__webpack_exports__["default"] = ...的方式導出,其餘屬性經過__webpack_require__.d(__webpack_exports__, "aaaa", function() { return aaaa; });方式導出。

  3. es6模塊不是入口模塊而是被其餘es6模塊引用時,export出去的default屬性和其餘屬性名都會被隨機賦予新的屬性名稱,例如export default '我是c';轉爲了__webpack_exports__["c"] = ('我是c');。default屬性一樣經過__webpack_exports__["c"] = ...的方式導出,其餘屬性一樣經過__webpack_require__.d(__webpack_exports__, "b", function() { return c2; });的方式導出。

  4. es6模塊的import語法被轉化成了var __WEBPACK_IMPORTED_MODULE_0__b__ = __webpack_require__(3);

commonjs模塊與es6模塊混用

情景一:es6引用commonjs

項目結構:

  1. app.js(entry):
import c,{c1,c2} from './c';
console.log(c,c1,c2)

export default '我是a';
export let aaaa = '我是aaaa';
複製代碼
  1. 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
複製代碼

結論:

  1. 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
    };
}
複製代碼

原樣輸出

綜上:

  1. es6調用commonjs模塊,import 默認值的狀況會特殊處理

  2. 被引用的commonjs模塊會原樣輸出。

情景二:commonjs調用es6模塊

項目結構:

  1. app.js(entry):
var b = require('./b')
var c = require('./c')
console.log(b.default)
console.log(c)
module.exports = {
	a:'我是a'
}
複製代碼
  1. 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');
}
複製代碼

結論:

  1. 能夠發現commonjs模塊引用es6模塊,被引用的es6模塊會被增長__esModule屬性(true)

  2. export語法被轉爲了__webpack_require__.d(__webpack_exports__, "c1", function() { return c1; });

  3. export default語句被轉爲了__webpack_exports__["default"] = ('我是c');

  4. 使用import b from './b'調用es6模塊,若是須要調用b的默認值,須要用__WEBPACK_IMPORTED_MODULE_0__b__["default"];

  5. 使用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);
    複製代碼

綜上:

  1. commonjs模塊被打包時,require部分會被__webpack_require__函數替代,其餘部分原樣輸出。

  2. 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)
    複製代碼
  3. 即便在es6模塊中使用require()引用,無論require的是es6模塊仍是commonjs模塊,都只會被簡單的解析成var c = __webpack_require__(2);。能夠認爲commonjs規範的require會被__webpack_require__直接替換。

  4. es6模塊被require時(不論require語句出如今commonjs仍是es6的模塊裏),es6模塊都會被添加__esModule=true。es6模塊被import時則都不會添加__esModule屬性。es6模塊被當作入口模塊時,也會被添加__esModule=true,能夠認爲作爲入口模塊時的命運就是會被打包成commonjs模塊暴露出去,此時就須要一個變量來標識我之前是es6模塊,只不過被強行變成了commonjs模塊,一經打包完成,本模塊再被引用時將不會觸發tree-shaking功能。

  5. es6模塊被直接import時,會觸發webpack的tree-shaking功能,能夠認爲webpack只有對es6模塊進行靜態解析後才能調用tree-shaking。

  6. 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');
    複製代碼
  7. 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 */])
    複製代碼
  8. 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 混合使用詳解

項目代碼

根據以上思路,若是實現了文件按路徑加載,就能寫出一個簡單的模塊化工具了。

相關文章
相關標籤/搜索