Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack 打包入口文件分析 #47

Open
xiaoxiaosaohuo opened this issue Dec 17, 2018 · 0 comments
Open

webpack 打包入口文件分析 #47

xiaoxiaosaohuo opened this issue Dec 17, 2018 · 0 comments

Comments

@xiaoxiaosaohuo
Copy link
Owner

xiaoxiaosaohuo commented Dec 17, 2018

普通模块

webpack.config.js

入口

entry: {
    app: "./src/index.js"
  },

index.js

import b from './b';
let a = b+1;

b.js

const b = '我是b';
export const c = 'sb';
export default b;

删除注释后的app.js代码如下,这就是打包后代码

 (function(modules) { 
 	// 缓存模块,modules就是我们声明的模块
 	var installedModules = {};
 	// webpack的模块导入函数
 	function __webpack_require__(moduleId) {
 		// 检查模块是否安装过
 		if(installedModules[moduleId]) {
 			return installedModules[moduleId].exports;
 		}
 		// 没有安装过就创建新模块存入缓存
 		var module = installedModules[moduleId] = {
 			i: moduleId,
 			l: false,
 			exports: {}
 		};

 		// 执行模块,传入module,module.exports,require
 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

 		// 标记模块已经加载过
 		module.l = true;

 		// 返回模块的内容
 		return module.exports;
 	}

 	//  保存传入的模块数组
 	__webpack_require__.m = modules;

 	// 保存安装过的模块
 	__webpack_require__.c = installedModules;

 	// 在module.exports对象上定义导出属性的getter
 	__webpack_require__.d = function(exports, name, getter) {
 		if(!__webpack_require__.o(exports, name)) {
 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
 		}
 	};

 	// 在export 对象上定义__esModule
 	__webpack_require__.r = function(exports) {
 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
 		}
 		Object.defineProperty(exports, '__esModule', { value: true });
 	};

 	// 创建一个假的命名空间
 	// mode & 1: value is a module id, require it
 	// mode & 2: merge all properties of value into the ns
 	// mode & 4: return value when already ns object
 	// mode & 8|1: behave like require
 	__webpack_require__.t = function(value, mode) {
 		if(mode & 1) value = __webpack_require__(value);
 		if(mode & 8) return value;
 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
 		var ns = Object.create(null);
 		__webpack_require__.r(ns);
 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
 		return ns;
 	};

 	// 模块默认导出
 	__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 配置中的 publicPath,用于加载被分割出去的异步代码
 	__webpack_require__.p = "";


 	// 加载入口文件
 	return __webpack_require__(__webpack_require__.s = "./src/index.js");
 })
 ({

 "./src/b.js":

(function (module, __webpack_exports__, __webpack_require__) {

    "use strict";
    eval("__webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, \"c\", function() { return c; });
    const b = '我是b';
    const c = 'sb';
    __webpack_exports__[\"default\"] = (b);

}),

 "./src/index.js":
(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b */ \"./src/b.js\");\n\nlet a = _b__WEBPACK_IMPORTED_MODULE_0__[\"default\"]+1;\n\n\n//# sourceURL=webpack:///./src/index.js?");

})

 });

可以看出在整体上这是一个IIFE函数

(function(modules){})({moduleid:fucntion(){}});

webpack首先会加载入口模块,从入口模块再去加载其他的模块。

经过webpack封装后的index.js如下

__webpack_require__.r(__webpack_exports__);
 var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( "./src/b.js");

let a = _b__WEBPACK_IMPORTED_MODULE_0__["default"] + 1;

可以看到首先执行__webpack_require__.r 在exports上定义__esModule属性,然后导入其他依赖的模块.

再来看b.js

__webpack_require__.r(__webpack_exports__);
 __webpack_require__.d(__webpack_exports__, "c", function() { return c; });
const b = '我是b';
const c = 'sb';
 __webpack_exports__["default"] = (b);

对于非默认导出的属性,会调用__webpack_require__.d方法在exports定义其getter,对于默认导出的属性直接赋值给exports.defalut。

异步模块

将a.js改为异步加载

import b from './b';
import("./a")
    .then((res) => {
        console.log(res)
    })
let a = b+1;

dist目录会多出一个0.js的文件,这个就是加载的a.js,同时入口文件也有一些变化。

先看index.js

__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b */ "./src/b.js");

__webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./a */ "./src/a.js"))
    .then((res) => {
        console.log
    })
let a = _b__WEBPACK_IMPORTED_MODULE_0__["default"]+1;

import方法被转成了__webpack_require__.e,这个方法会传入一个chunkId(本例子中是0),去加载对应的chunk,然后返回一个promise,当模块加载完成后再调用__webpack_require__去安装模块。

0.js

(window["webpackJsonp"] = window["webpackJsonp"] || [])
.push([
[0],//自身的chunkid
{
//自身依赖的其他模块
 "./src/a.js":

  (function(module, __webpack_exports__, __webpack_require__) {

    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\nconst split = 'split';\n/* harmony default export */ __webpack_exports__[\"default\"] = (split);\n\n\n//# sourceURL=webpack:///./src/a.js?");

  })

}]);

webpackJsonp是全局的,push方法其实webpackJsonpCallback方法

在app.js中将push方法被赋值为webpackJsonpCallback

var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback;
 (function(modules) { // webpackBootstrap
 	// install a JSONP callback for chunk loading
     	function webpackJsonpCallback(data) {
 		var chunkIds = data[0]; 
 		var moreModules = data[1];


 		// add "moreModules" to the modules object,
 		// then flag all "chunkIds" as loaded and fire callback
 		var moduleId, chunkId, i = 0, resolves = [];
 		for(;i < chunkIds.length; i++) {
 			chunkId = chunkIds[i];
 			if(installedChunks[chunkId]) {
 				resolves.push(installedChunks[chunkId][0]);
 			}
 			//标记为0,表示已经安装过了
 			installedChunks[chunkId] = 0;
 		}
 		//将模块存入modules
 		for(moduleId in moreModules) {
 			if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 			
 				modules[moduleId] = moreModules[moduleId];
 			}
 		}
 		if(parentJsonpFunction) parentJsonpFunction(data);
        //执行回调,__webpack_require__.e的promise回调,调用__webpack_require__加载对应的模块
 		while(resolves.length) {
 			resolves.shift()();
 		}

 	};


 	// 缓存模块的对象
 	var installedModules = {};

 	// 缓存chunk的对象
 	// undefined = chunk not loaded, null = chunk preloaded/prefetched
 	// Promise = chunk loading, 0 = chunk loaded
 	var installedChunks = {
 		"app": 0
 	};



 	// 拼接异步加载chunk的路径
 	function jsonpScriptSrc(chunkId) {
 		return __webpack_require__.p + "" + ({}[chunkId]||chunkId) + ".bundle.js"
 	}

 	// The require function
 	function __webpack_require__(moduleId) {

 		略
 	}

 
 	// 加载chunk函数
 	__webpack_require__.e = function requireEnsure(chunkId) {
 		var promises = [];


 		// JSONP chunk loading for javascript

 		var installedChunkData = installedChunks[chunkId];
 		if(installedChunkData !== 0) { // 0表示已经安装过了

 			//installedChunkData是个数组,其内容为[resolve,reject,promise]
 			if(installedChunkData) {
 			//如果存在,表明正在加载	promises.push(installedChunkData[2]);
 			} else {
 				// 创建promise,并加入缓存
 				var promise = new Promise(function(resolve, reject) {
 					installedChunkData = installedChunks[chunkId] = [resolve, reject];
 				});
 				promises.push(installedChunkData[2] = promise);

 				// 创建script标签加载chunk,添加到head中
 				var script = document.createElement('script');
 				var onScriptComplete;

 				script.charset = 'utf-8';
 				script.timeout = 120;
 				if (__webpack_require__.nc) {
 					script.setAttribute("nonce", __webpack_require__.nc);
 				}
 				script.src = jsonpScriptSrc(chunkId);

 				onScriptComplete = function (event) {
 					// avoid mem leaks in IE.
 					script.onerror = script.onload = null;
 					clearTimeout(timeout);
 					var chunk = installedChunks[chunkId];
 					//==0表示加载成功了
 					if(chunk !== 0) {
 						if(chunk) {
 							var errorType = event && (event.type === 'load' ? 'missing' : event.type);
 							var realSrc = event && event.target && event.target.src;
 							var error = new Error('Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')');
 							error.type = errorType;
 							error.request = realSrc;
 							chunk[1](error);
 						}
 						installedChunks[chunkId] = undefined;
 					}
 				};
 				var timeout = setTimeout(function(){
 					onScriptComplete({ type: 'timeout', target: script });
 				}, 120000);
 				script.onerror = script.onload = onScriptComplete;
 				document.head.appendChild(script);
 			}
 		}
 		//返回promise
 		return Promise.all(promises);
 	};

 	// 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, { enumerable: true, get: getter });
 		}
 	};

 	// define __esModule on exports
 	__webpack_require__.r = function(exports) {
 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
 		}
 		Object.defineProperty(exports, '__esModule', { value: true });
 	};

 	// create a fake namespace object
 	// mode & 1: value is a module id, require it
 	// mode & 2: merge all properties of value into the ns
 	// mode & 4: return value when already ns object
 	// mode & 8|1: behave like require
 	__webpack_require__.t = function(value, mode) {
 		if(mode & 1) value = __webpack_require__(value);
 		if(mode & 8) return value;
 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
 		var ns = Object.create(null);
 		__webpack_require__.r(ns);
 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
 		return ns;
 	};

 	// 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 = "";

 	// on error function for async loading
 	__webpack_require__.oe = function(err) { console.error(err); throw err; };

 	var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
 	var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
 	jsonpArray.push = webpackJsonpCallback;
 	jsonpArray = jsonpArray.slice();
 	for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
 	var parentJsonpFunction = oldJsonpFunction;


 	// Load entry module and return exports
 	return __webpack_require__(__webpack_require__.s = "./src/index.js");
 })
 ({

"./src/b.js":
(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"c\", function() { return c; });\nconst b = '我是b';\nconst c = 'sb';\n/* harmony default export */ __webpack_exports__[\"default\"] = (b);\n\n\n//# sourceURL=webpack:///./src/b.js?");

}),

"./src/index.js":
 (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b */ \"./src/b.js\");\n\n__webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./a */ \"./src/a.js\"))\n    .then((res) => {\n        console.log\n    })\nlet a = _b__WEBPACK_IMPORTED_MODULE_0__[\"default\"]+1;\n\n\n//# sourceURL=webpack:///./src/index.js?");

})

 });

这个app.js和同步加载的方式比起来有以下变化。

  • 多了一个 webpack_require.e 用于加载被分割出去的,需要异步加载的 Chunk 对应的文件;
  • 多了一个 webpackJsonp 函数用于从异步加载的文件中安装模块。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant