webpack 打包初识
webpack 是用来做什么的,原理是什么
webpack 是一个打包模块的机制,把依赖的模块转化成可以代表这些包的静态文件,识别入口文件和模块依赖来打包代码,自动对代码使用的方式如 commonJS、amd、es6 import 进行分析,webpack 做的就是分析代码、转换代码、编译代码、输出代码。webpack 本身是一个 node 的模块,故 webpack.config.js 是以 commonJS 形式书写。
从输入到输出
webpack 中每个模块都有一个唯一的 ID ,是从 0 开始递增的。整个打包后的 bundle.js 是一个立即执行函数,参数是一个数组,数组的每一项都为一个 function,其内容则是每个模块内容,并按照 require (或其他模块化引入)的顺序排列。
commonJS
如下两个文件 a.js
与 b.js
其在 webpack@2 下打包之后的文件 bundle.js
与在 webpack@4.41.5 下打包之后的文件 bundle.js
。
// a.js
var b = require('./b.js');
console.log('a');
b.b1();
// b.js
exports.b1 = function () {
console.log('b1')
};
exports.b2 = function () {
console.log('b2')
};
// webpack@2
// bundle.js
/******/ (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] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = 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;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var b = __webpack_require__(1);
console.log('a');
b.b1();
/***/ },
/* 1 */
/***/ function(module, exports) {
exports.b1 = function () {
console.log('b1')
};
exports.b2 = function () {
console.log('b2')
};
/***/ }
/******/ ]);
// webpack@4.41.5
// bundle.js
!
function(e) {
var n = {};
function t(o) {
if (n[o]) return n[o].exports;
var r = n[o] = {
i: o,
l: !1,
exports: {}
};
return e[o].call(r.exports, r, r.exports, t),
r.l = !0,
r.exports
}
t.m = e,
t.c = n,
t.d = function(e, n, o) {
t.o(e, n) || Object.defineProperty(e, n, {
enumerable: !0,
get: o
})
},
t.r = function(e) {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
value: "Module"
}),
Object.defineProperty(e, "__esModule", {
value: !0
})
},
t.t = function(e, n) {
if (1 & n && (e = t(e)), 8 & n) return e;
if (4 & n && "object" == typeof e && e && e.__esModule) return e;
var o = Object.create(null);
if (t.r(o), Object.defineProperty(o, "default", {
enumerable: !0,
value: e
}), 2 & n && "string" != typeof e) for (var r in e) t.d(o, r,
function(n) {
return e[n]
}.bind(null, r));
return o
},
t.n = function(e) {
var n = e && e.__esModule ?
function() {
return e.
default
}:
function() {
return e
};
return t.d(n, "a", n),
n
},
t.o = function(e, n) {
return Object.prototype.hasOwnProperty.call(e, n)
},
t.p = "",
t(t.s = 0)
} ([function(e, n, t) {
var o = t(1);
console.log("a"),
o.b1()
},
function(e, n) {
n.b1 = function() {
console.log("b1")
},
n.b2 = function() {
console.log("b2")
}
}]);
_webpack_require
是模块加载函数,接收的参数即为模块 ID ,这个函数中调用了 module.call,参数为 module.exports,module,module.exports,_webpack_require
,相当于执行传入这个匿名函数中的数组项,并将其中的 exports 绑定到 module.exports
中。对于 b.js ,webpack 将整个文件包裹了起来,添加了三个参数变量,将 a.js 中 export 属性都绑定到了参数 exports
上,在 __webpack_require
函数中返回这个 export 对象从而导出 a.js 中返回的对象。对于 a.js ,使用的方式即从具有导入模块的数组入手获取依赖的对象,之后使用这个对象。
可以看到 webpack@4.41.5 相对于老版本的 webpack 基本相同,只不过增加一些属性,其中就有可以进行 commonJS 与 es6 module 混用模块的逻辑等。
ES 6 module
如下两个文件 app.js
与 a.js
,在 webpack@2 下打包之后的文件 bundle.js
与在 webpack@4.41.5 下打包之后的文件 bundle.js
。
// a.js
let a = 1
const b = 2
export function log(val) {
console.log(val)
}
export default{
a:a,
b:b
}
// app.js
import tt from './a'
import {log} from './a'
console.log(tt.b)
log(tt.b)
// webpack@2
// bundle.js
(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: {}
};
// 执行了导入的module,把export的值都放到module.exports中
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;
// identity function for calling harmony imports with the correct context
__webpack_require__.i = function(value) { return value; };
// 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 = 1);
})
/************************************************************************/
([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (immutable) */ __webpack_exports__["b"] = log;
let a = 1
const b = 2
function log(val) {
console.log(val)
}
/* harmony default export */ __webpack_exports__["a"] = {
a:a,
b:b
};
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__a__ = __webpack_require__(0);
console.log(__WEBPACK_IMPORTED_MODULE_0__a__["a" /* default */].b)
__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__a__["b" /* log */])(__WEBPACK_IMPORTED_MODULE_0__a__["a" /* default */].b)
/***/ })
]);
// webpack@4.41.5
// bundle.js
// 感叹号用于构建立即执行函数,取代 (function(){})() 的写法, 用括号包裹解析器会以函数表达式的方式调用定义函数,感叹号也可以
// 也可以将感叹号换为其他的一元运算符,如 ~,!!!,~~~ 甚至其组合
!
function(e) {
var t = {};
function r(n) {
if (t[n]) return t[n].exports;
var o = t[n] = {
i: n,
l: !1,
exports: {}
};
return e[n].call(o.exports, o, o.exports, r),
o.l = !0,
o.exports
}
r.m = e,
r.c = t,
r.d = function(e, t, n) {
r.o(e, t) || Object.defineProperty(e, t, {
enumerable: !0,
get: n
})
},
r.r = function(e) {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
value: "Module"
}),
Object.defineProperty(e, "__esModule", {
value: !0
})
},
r.t = function(e, t) {
if (1 & t && (e = r(e)), 8 & t) return e;
if (4 & t && "object" == typeof e && e && e.__esModule) return e;
var n = Object.create(null);
if (r.r(n), Object.defineProperty(n, "default", {
enumerable: !0,
value: e
}), 2 & t && "string" != typeof e) for (var o in e) r.d(n, o,
function(t) {
return e[t]
}.bind(null, o));
return n
},
r.n = function(e) {
var t = e && e.__esModule ?
function() {
return e.
default
}:
function() {
return e
};
return r.d(t, "a", t),
t
},
r.o = function(e, t) {
return Object.prototype.hasOwnProperty.call(e, t)
},
r.p = "",
r(r.s = 0)
} ([function(e, t, r) {
"use strict";
r.r(t);
var n, o = 2;
console.log(o),
n = o,
console.log(n)
}]);
对于 app.js 与 a.js 的处理和 commonJS 在 webpack@2 上基本相同。因为是 es 6 模块,故首先通过 Object.defineProperty
在 __webpack_exports__
上添加了属性 __esModule
,值为 true 表示这是一个 es 模块。需要注意的是在 webpack@4.41.5 中,其中立即函数执行的参数变为了一个???
参考: