阅读 185

🍭 源码分析:深入浅出webpack-1

什么是webpack?

At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph which maps every module your project needs and generates one or more bundles.

通过理解不同JS文件间的依赖关系,形成打包文件的工具

模块化的核心

  • 依赖处理

  • 分割作用域

webpack的方案

  • 自己实现一套 exports require 的逻辑来协调不同规范下的模块API
  • 使用函数进行作用域分割(eval? 目前使用 source-map 的形式还没研究eval)

从一些例子🌰开始

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
var target = 'var'

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, 'entry.js'),
  output: {
    clean: true,
    library: {
      name: 'MyLibrary',
      type: 'var',
    },
  },
  devtool: 'source-map',
  plugins: [
    new HtmlWebpackPlugin({
      title: target,
      filename: 'index.html',
    }),
  ],
}
复制代码

dist/index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>var</title>
  <meta name="viewport" content="width=device-width, initial-scale=1"><script defer src="main.js"></script></head>
  <body>
  </body>
</html>
复制代码

没有依赖

entry.js

console.log(1)
复制代码

main.js

var MyLibrary;
/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!******************!*\
  !*** ./entry.js ***!
  \******************/
console.log(1)

console.log(2)
MyLibrary = __webpack_exports__;
/******/ })()
;
//# sourceMappingURL=main.js.map
复制代码

开始有依赖了

import { name, default as foo } from './foo'

console.log(1, name, foo)

export default 1
export const entry = 'this is entry'
const second = 'this is second'
export { second }


while (false) {}

export const name = 'foo name'

export default 'foo default'
复制代码
var MyLibrary;
/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ "./foo.js":
/*!****************!*\
  !*** ./foo.js ***!
  \****************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "name": () => (/* binding */ name),
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
while (false) {}

const name = 'foo name'

/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ('foo default');


/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/make namespace object */
/******/ 	(() => {
/******/ 		// define __esModule on exports
/******/ 		__webpack_require__.r = (exports) => {
/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 			}
/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
/******/ 		};
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!******************!*\
  !*** ./entry.js ***!
  \******************/
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__),
/* harmony export */   "entry": () => (/* binding */ entry),
/* harmony export */   "second": () => (/* binding */ second)
/* harmony export */ });
/* harmony import */ var _foo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./foo */ "./foo.js");


console.log(1, _foo__WEBPACK_IMPORTED_MODULE_0__.name, _foo__WEBPACK_IMPORTED_MODULE_0__.default)

/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (1);
const entry = 'this is entry'
const second = 'this is second'


})();

MyLibrary = __webpack_exports__;
/******/ })()
;
//# sourceMappingURL=main.js.map
复制代码

分析思路如下

__webpack_require__ 上面的几个方法

  • d define 核心

  • o Object.prototype.hasOwnProperty

  • r namespce

先看是咋用的

export default 1
export const entry = 'this is entry'
const second = 'this is second'
export { second }
复制代码
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__),
/* harmony export */   "entry": () => (/* binding */ entry),
/* harmony export */   "second": () => (/* binding */ second)
/* harmony export */ });
复制代码
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
复制代码

去除缓冲 和 define 转换的等价代码

var test;
(() => {
  var __webpack_modules__ = {
    "./foo.js": (__webpack_exports__, __webpack_require__) => {
      __webpack_require__.d(__webpack_exports__, {
        name: () => /* binding */ name,
        default: () => __WEBPACK_DEFAULT_EXPORT__,
      });

      while (false) {}

      const name = "foo name";

      const __WEBPACK_DEFAULT_EXPORT__ = "foo default";
    },
  };

  function __webpack_require__(moduleId) {
    var module = {
      exports: {},
    };
    __webpack_modules__[moduleId](module.exports, __webpack_require__);
    return module.exports;
  }

  (() => {
    __webpack_require__.d = (exports, definition) => {
      for (var key in definition) {
        if (
          __webpack_require__.o(definition, key) &&
          !__webpack_require__.o(exports, key)
        ) {
          Object.defineProperty(exports, key, {
            enumerable: true,
            get: definition[key],
          });
        }
      }
    };
  })();
  (() => {
    __webpack_require__.o = (obj, prop) =>
      Object.prototype.hasOwnProperty.call(obj, prop);
  })();

  const __webpack_exports__ = {};

  (() => {
    __webpack_require__.d(__webpack_exports__, {
      default: () => __WEBPACK_DEFAULT_EXPORT__,
      entry: () => entry,
      second: () => second,
    });

    var _foo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./foo.js");

    console.log(
      1,
      _foo__WEBPACK_IMPORTED_MODULE_0__.name,
      _foo__WEBPACK_IMPORTED_MODULE_0__.default
    );

    const __WEBPACK_DEFAULT_EXPORT__ = 1;
    const entry = "this is entry";
    const second = "this is second";
  })();

  test = __webpack_exports__;
})();

复制代码

重点需要去理解的内容

webpack.js.org/concepts/ 几个核心概念

output.type

里面有多种类型

Configure how the library will be exposed.

  • Type: string

    Types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp' and 'system', but others might be added by plugins.

特别提提下使用umd的时候出现的self

The **Window.self** read-only property returns the window itself, as a WindowProxy. It can be used with dot notation on a window object (that is, window.self) or standalone (self). The advantage of the standalone notation is that a similar notation exists for non-window contexts, such as in Web Workers. By using self, you can refer to the global scope in a way that will work not only in a window context (self will resolve to window.self) but also in a worker context (self will then resolve to WorkerGlobalScope.self).

文章分类
前端