一、Mode 配置

这里要简单讲一下,后面还会提到它的其他用法。

Mode 配置选项,可以告知 webpack 使用相应模式的内置优化:

  • 默认值是 production
  • 可选值有:’none’ | ‘development’ | ‘production’

这几个选项有什么样的区别呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/main.js",
output: {
filename: "bundle.js",
//__dirname,当前文件的上级目录的绝对路径
path: path.resolve(__dirname, "./build"),
// assetModuleFilename: 'img/[name].[hash:6][ext]',
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "webpack项目",
}),
],
};

mode: 'development',这样编译完的代码不会进行压缩丑化。

Mode 配置代表更多 ,后续再了解。

二、模块化原理

webpack 支持 CommoJS 原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 定义了一个对象
// 模块的路径(key): 函数(value)
var __webpack_modules__ = {
"./src/js/format.js": function (module) {
const dateFormat = (date) => {
return "2020-12-12";
};
const priceFormat = (price) => {
return "100.00";
};

// 将我们要导出的变量, 放入到module对象中的exports对象
module.exports = {
dateFormat,
priceFormat,
};
},
};

// 定义一个对象, 作为加载模块的缓存
var __webpack_module_cache__ = {};

// 是一个函数, 当我们加载一个模块时, 都会通过这个函数来加载
function __webpack_require__(moduleId) {
// 1.判断缓存中是否已经加载过
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}

// 2.给module变量和__webpack_module_cache__[moduleId]赋值了同一个对象
var module = (__webpack_module_cache__[moduleId] = { exports: {} });

// 3.加载执行模块
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);

// 4.导出module.exports {dateFormat: function, priceForamt: function}
return module.exports;
}

// 具体开始执行代码逻辑
!(function () {
// 1.加载./src/js/format.js
const { dateFormat, priceFormat } = __webpack_require__("./src/js/format.js");
console.log(dateFormat("abc"));
console.log(priceFormat("abc"));
})();

webpack 支持 ES6 Module 原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// 1.定义了一个对象, 对象里面放的是我们的模块映射
var __webpack_modules__ = {
"./src/es_index.js": function (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) {
// 调用r的目的是记录时一个__esModule -> true
__webpack_require__.r(__webpack_exports__);

// _js_math__WEBPACK_IMPORTED_MODULE_0__ == exports
var _js_math__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__("./src/js/math.js");

console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.mul(20, 30));
console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.sum(20, 30));
},
"./src/js/math.js": function (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) {
__webpack_require__.r(__webpack_exports__);

// 调用了d函数: 给exports设置了一个代理definition
// exports对象中本身是没有对应的函数
__webpack_require__.d(__webpack_exports__, {
sum: function () {
return sum;
},
mul: function () {
return mul;
},
});

const sum = (num1, num2) => {
return num1 + num2;
};
const mul = (num1, num2) => {
return num1 * num2;
};
},
};

// 2.模块的缓存
var __webpack_module_cache__ = {};

// 3.require函数的实现(加载模块)
function __webpack_require__(moduleId) {
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}

!(function () {
// __webpack_require__这个函数对象添加了一个属性: d -> 值function
__webpack_require__.d = function (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],
});
}
}
};
})();

!(function () {
// __webpack_require__这个函数对象添加了一个属性: o -> 值function
__webpack_require__.o = function (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
};
})();

!(function () {
// __webpack_require__这个函数对象添加了一个属性: r -> 值function
__webpack_require__.r = function (exports) {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
}
Object.defineProperty(exports, "__esModule", { value: true });
};
})();

__webpack_require__("./src/es_index.js");

CommonJS 与 ES6 Module 相互导入原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
var __webpack_modules__ = {
"./src/index.js": function (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var _js_format__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__("./src/js/format.js");
var _js_format__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(
_js_format__WEBPACK_IMPORTED_MODULE_0__
);

// es module导出内容, CommonJS导入内容
const math = __webpack_require__("./src/js/math.js");

// CommonJS导出内容, es module导入内容
console.log(math.sum(20, 30));
console.log(math.mul(20, 30));
console.log(
_js_format__WEBPACK_IMPORTED_MODULE_0___default().dateFormat("aaa")
);
console.log(
_js_format__WEBPACK_IMPORTED_MODULE_0___default().priceFormat("bbb")
);
},
"./src/js/format.js": function (module) {
const dateFormat = (date) => {
return "2020-12-12";
};
const priceFormat = (price) => {
return "100.00";
};
module.exports = {
dateFormat,
priceFormat,
};
},

"./src/js/math.js": function (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
sum: function () {
return sum;
},
mul: function () {
return mul;
},
});
const sum = (num1, num2) => {
return num1 + num2;
};

const mul = (num1, num2) => {
return num1 * num2;
};
},
};

var __webpack_module_cache__ = {};

// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].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;
}

!(function () {
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function (module) {
var getter =
module && module.__esModule
? function () {
return module["default"];
}
: function () {
return module;
};
__webpack_require__.d(getter, { a: getter });
return getter;
};
})();

/* webpack/runtime/define property getters */
!(function () {
// define getter functions for harmony exports
__webpack_require__.d = function (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 */
!(function () {
__webpack_require__.o = function (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
};
})();

/* webpack/runtime/make namespace object */
!(function () {
// 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 });
};
})();

__webpack_require__("./src/index.js");

源码问题,慢慢看吧

三、 source-map

我们的代码通常运行在浏览器上时,是通过打包压缩的:

也就是真实跑在浏览器上的代码,和我们编写的代码其实是有差异的

  • 比如 ES6 的代码可能被转换成 ES5
  • 比如对应的代码行号、列号在经过编译后肯定会不一致
  • 比如代码进行丑化压缩时,会将编码名称等修改
  • 比如我们使用了 TypeScript 等方式编写的代码,最终转换成 JavaScript

但是,当代码报错需要调试时(debug),调试转换后的代码是很困难的,况且我们不能保证代码不出错,那么如何可以调试这种转换后不一致的代码呢?答案就是 source-map

  • source-map 是从已转换(压缩后的)的代码,映射到原始的源文件(npm run build 之前的)一种类型文件
  • 使浏览器可以重构原始源并在调试器中显示重建的原始源

如何使用

第一步:根据源文件,生成 source-map 文件,webpack 在打包时,可以通过配置生成 source-map

1
devtool: "source-map";

第二步:在转换后的代码,最后添加一个注释,它指向 sourcemap

1
//# sourceMappingURL=common.bundle.js.map

浏览器会根据我们的注释,查找相应的 source-map,并且根据 source-map 还原我们的代码,方便进行调试。

正常情况下压缩丑化后,不使用 source-map,报错后我们无法准确的找到错误处,尤其项目过大时。

使用 source-map:

1
2
entry: './src/main.js',
devtool: 'source-map',

此时浏览器报错可以快速定位:

分析 source-map

最初 source-map 生成的文件带下是原始文件的 10 倍,第二版减少了约 50%,第三版又减少了 50%,所以目前一个 133kb 的文件,最终的 source-map 的大小大概在 300kb。

目前的 source-map 长什么样子呢?

  • version:当前使用的版本,也就是最新的第三版
  • sources:从哪些文件转换过来的 source-map 和打包的代码(最初始的文件)
  • names:转换前的变量和属性名称(因为我目前使用的是 development 模式,所以不需要保留转换前的名 称)
  • mappings:source-map 用来和源文件映射的信息(比如位置信息等),一串 base64 VLQ(veriablelength quantity 可变长度值)编码
  • file:打包后的文件(浏览器加载的文件)
  • sourceContent:转换前的具体代码信息(和 sources 是对应的关系)
  • sourceRoot:所有的 sources 相对的根目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
"version": 3,
"file": "bundle.js",
"mappings": "kCAkBAA,SAASC,KAAKC,YAlBd,WACE,IAAIC,EAAUH,SAASI,cAAc,OACrCD,EAAQE,UAAY,CAAC,QAAS,WAAWC,KAAK,KAC9CH,EAAQI,UAAY,UAGpB,MAAMC,EAAQ,IAAIC,MAElBN,EAAQD,YAAYM,GAGpB,MAAME,EAAQV,SAASI,cAAc,OAKrC,OAJAM,EAAMC,MAAMC,MAAQ,QACpBF,EAAMC,MAAME,OAAS,QAErBV,EAAQD,YAAYQ,GACbP,EAEiBW,MCjBtBC,EAA2B,GAG/B,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaE,QAGrB,IAAIC,EAASN,EAAyBE,GAAY,CAGjDG,QAAS,IAOV,OAHAE,EAAoBL,GAAUI,EAAQA,EAAOD,QAASJ,GAG/CK,EAAOD,S,+BCnBfG,QAAQC,ICCa,S",
"sources": [
"webpack://01_webpack_learn/./src/js/component.js",
"webpack://01_webpack_learn/webpack/bootstrap",
"webpack://01_webpack_learn/./src/main.js",
"webpack://01_webpack_learn/./src/js/math.js"
],
"sourcesContent": [
"function component() {\r\n let element = document.createElement('div');\r\n element.innerHTML = ['Hello', 'Webpack'].join(' ');\r\n element.className = 'content';\r\n\r\n //创建一个img标签\r\n const imgEL = new Image();\r\n // imgEL.src = require('../img/7.jpg').default;\r\n element.appendChild(imgEL);\r\n\r\n //创建一个div 设置背景图片\r\n const divEL = document.createElement('div');\r\n divEL.style.width = 200 + 'px';\r\n divEL.style.height = 200 + 'px';\r\n // divEL.className = 'bg-img';\r\n element.appendChild(divEL);\r\n return element;\r\n}\r\ndocument.body.appendChild(component());\r\n",
"// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n",
"import { message } from './js/math.js';\r\nimport './js/component.js'\r\nconsole.log(message);",
"// function sum(n1, n2) {\r\n// return n1 + n2;\r\n// }\r\nexport const message=\"Hello\""
],
"names": [
"document",
"body",
"appendChild",
"element",
"createElement",
"innerHTML",
"join",
"className",
"imgEL",
"Image",
"divEL",
"style",
"width",
"height",
"component",
"__webpack_module_cache__",
"__webpack_require__",
"moduleId",
"cachedModule",
"undefined",
"exports",
"module",
"__webpack_modules__",
"console",
"log"
],
"sourceRoot": ""
}

生成 source-map

如何在使用 webpack 打包的时候,生成对应的 source-map 呢?

  • webpack 提供了非常多的选项(目前是 26 个),来处理 source-map https://webpack.docschina.org/configuration/devtool/
  • 选择不同的值,生成的 source-map 会稍微有差异,打包的过程也会有性能的差异,可以根据不同的情况进行选择

不生成 source-map

  • false:不使用 source-map,也就是没有任何和 source-map 相关的内容。
  • none:production 模式下的默认值,不生成 source-map。
  • eval:development 模式下的默认值,不生成 source-map
  • 但是它会在 eval 执行的代码中,添加 //# sourceURL=; 会被浏览器在执行时解析,并且在调试面板中生成对应的一些文件目录,方便我们调试代码;

比如:

1
2
3
eval(
"function component() {\r\n let element = document.createElement('div');\r\n element.innerHTML = ['Hello', 'Webpack'].join(' ');\r\n element.className = 'content';\r\n\r\n //创建一个img标签\r\n const imgEL = new Image();\r\n // imgEL.src = require('../img/7.jpg').default;\r\n element.appendChild(imgEL);\r\n\r\n //创建一个div 设置背景图片\r\n const divEL = document.createElement('div');\r\n divEL.style.width = 200 + 'px';\r\n divEL.style.height = 200 + 'px';\r\n // divEL.className = 'bg-img';\r\n element.appendChild(divEL);\r\n return element;\r\n}\r\ndocument.body.appendChild(component());\r\n\n\n//# sourceURL=webpack://01_webpack_learn/./src/js/component.js?"
);

source-map

生成一个独立的 source-map 文件,并且在 bundle 文件中有一个注释,指向 source-map 文件

bundle 文件中有如下的注释: //# sourceMappingURL=bundle.js.map

开发工具会根据这个注释找到 source-map 文件,并且解析

对应的就是上上面的 source-map。

eval-source-map

生成 sourcemap,但是 source-map 是以 DataUrl 添加到 eval 函数的后面

1
2
3
eval(
'__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "message": function() { return /* binding */ message; }\n/* harmony export */ });\n// function sum(n1, n2) {\r\n// return n1 + n2;\r\n// }\r\nconst message="Hello"//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvanMvbWF0aC5qcy5qcyIsIm1hcHBpbmdzIjoiOzs7O0FBQUE7QUFDQTtBQUNBO0FBQ08iLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8wMV93ZWJwYWNrX2xlYXJuLy4vc3JjL2pzL21hdGguanM/OWMzOSJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBmdW5jdGlvbiBzdW0objEsIG4yKSB7XHJcbi8vICAgcmV0dXJuIG4xICsgbjI7XHJcbi8vIH1cclxuZXhwb3J0IGNvbnN0IG1lc3NhZ2U9XCJIZWxsb1wiIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./src/js/math.js\n'
);

inline-source-map

会生成 sourcemap,但是 source-map 是以 DataUrl添加到 bundle 文件的后面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
!(function () {
'use strict';
/*!*********************!*\
!*** ./src/main.js ***!
\*********************/
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _js_math_js__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__(/*! ./js/math.js */ './src/js/math.js');
/* harmony import */ var _js_component_js__WEBPACK_IMPORTED_MODULE_1__ =
__webpack_require__(/*! ./js/component.js */ './src/js/component.js');
/* harmony import */ var _js_component_js__WEBPACK_IMPORTED_MODULE_1___default =
/*#__PURE__*/ __webpack_require__.n(
_js_component_js__WEBPACK_IMPORTED_MODULE_1__
);

console.log(_js_math_js__WEBPACK_IMPORTED_MODULE_0__.message);
})();
/******/
})();
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7O0FDbEJBO0FBQ0E7QUFDQTtBQUNPOzs7Ozs7VUNIUDtVQUNBOztVQUVBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBOztVQUVBO1VBQ0E7O1VBRUE7VUFDQTtVQUNBOzs7OztXQ3RCQTtXQUNBO1dBQ0E7V0FDQSxlQUFlLDRCQUE0QjtXQUMzQyxlQUFlO1dBQ2YsaUNBQWlDLFdBQVc7V0FDNUM7V0FDQTs7Ozs7V0NQQTtXQUNBO1dBQ0E7V0FDQTtXQUNBLHlDQUF5Qyx3Q0FBd0M7V0FDakY7V0FDQTtXQUNBOzs7OztXQ1BBLDhDQUE4Qzs7Ozs7V0NBOUM7V0FDQTtXQUNBO1dBQ0EsdURBQXVELGlCQUFpQjtXQUN4RTtXQUNBLGdEQUFnRCxhQUFhO1dBQzdEOzs7Ozs7Ozs7Ozs7Ozs7QUNOdUM7QUFDYjtBQUMxQixZQUFZLGdEQUFPLEUiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8wMV93ZWJwYWNrX2xlYXJuLy4vc3JjL2pzL2NvbXBvbmVudC5qcyIsIndlYnBhY2s6Ly8wMV93ZWJwYWNrX2xlYXJuLy4vc3JjL2pzL21hdGguanMiLCJ3ZWJwYWNrOi8vMDFfd2VicGFja19sZWFybi93ZWJwYWNrL2Jvb3RzdHJhcCIsIndlYnBhY2s6Ly8wMV93ZWJwYWNrX2xlYXJuL3dlYnBhY2svcnVudGltZS9jb21wYXQgZ2V0IGRlZmF1bHQgZXhwb3J0Iiwid2VicGFjazovLzAxX3dlYnBhY2tfbGVhcm4vd2VicGFjay9ydW50aW1lL2RlZmluZSBwcm9wZXJ0eSBnZXR0ZXJzIiwid2VicGFjazovLzAxX3dlYnBhY2tfbGVhcm4vd2VicGFjay9ydW50aW1lL2hhc093blByb3BlcnR5IHNob3J0aGFuZCIsIndlYnBhY2s6Ly8wMV93ZWJwYWNrX2xlYXJuL3dlYnBhY2svcnVudGltZS9tYWtlIG5hbWVzcGFjZSBvYmplY3QiLCJ3ZWJwYWNrOi8vMDFfd2VicGFja19sZWFybi8uL3NyYy9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIGNvbXBvbmVudCgpIHtcclxuICBsZXQgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gIGVsZW1lbnQuaW5uZXJIVE1MID0gWydIZWxsbycsICdXZWJwYWNrJ10uam9pbignICcpO1xyXG4gIGVsZW1lbnQuY2xhc3NOYW1lID0gJ2NvbnRlbnQnO1xyXG5cclxuICAvL+WIm+W7uuS4gOS4qmltZ+agh+etvlxyXG4gIGNvbnN0IGltZ0VMID0gbmV3IEltYWdlKCk7XHJcbiAgLy8gaW1nRUwuc3JjID0gcmVxdWlyZSgnLi4vaW1nLzcuanBnJykuZGVmYXVsdDtcclxuICBlbGVtZW50LmFwcGVuZENoaWxkKGltZ0VMKTtcclxuXHJcbiAgLy/liJvlu7rkuIDkuKpkaXYg6K6+572u6IOM5pmv5Zu+54mHXHJcbiAgY29uc3QgZGl2RUwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuICBkaXZFTC5zdHlsZS53aWR0aCA9IDIwMCArICdweCc7XHJcbiAgZGl2RUwuc3R5bGUuaGVpZ2h0ID0gMjAwICsgJ3B4JztcclxuICAvLyBkaXZFTC5jbGFzc05hbWUgPSAnYmctaW1nJztcclxuICBlbGVtZW50LmFwcGVuZENoaWxkKGRpdkVMKTtcclxuICByZXR1cm4gZWxlbWVudDtcclxufVxyXG5kb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbXBvbmVudCgpKTtcclxuIiwiLy8gZnVuY3Rpb24gc3VtKG4xLCBuMikge1xyXG4vLyAgIHJldHVybiBuMSArIG4yO1xyXG4vLyB9XHJcbmV4cG9ydCBjb25zdCBtZXNzYWdlPVwiSGVsbG9cIiIsIi8vIFRoZSBtb2R1bGUgY2FjaGVcbnZhciBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX18gPSB7fTtcblxuLy8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbmZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG5cdHZhciBjYWNoZWRNb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdO1xuXHRpZiAoY2FjaGVkTW9kdWxlICE9PSB1bmRlZmluZWQpIHtcblx0XHRyZXR1cm4gY2FjaGVkTW9kdWxlLmV4cG9ydHM7XG5cdH1cblx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcblx0dmFyIG1vZHVsZSA9IF9fd2VicGFja19tb2R1bGVfY2FjaGVfX1ttb2R1bGVJZF0gPSB7XG5cdFx0Ly8gbm8gbW9kdWxlLmlkIG5lZWRlZFxuXHRcdC8vIG5vIG1vZHVsZS5sb2FkZWQgbmVlZGVkXG5cdFx0ZXhwb3J0czoge31cblx0fTtcblxuXHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cblx0X193ZWJwYWNrX21vZHVsZXNfX1ttb2R1bGVJZF0obW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cblx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcblx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xufVxuXG4iLCIvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuX193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG5cdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuXHRcdGZ1bmN0aW9uKCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuXHRcdGZ1bmN0aW9uKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCB7IGE6IGdldHRlciB9KTtcblx0cmV0dXJuIGdldHRlcjtcbn07IiwiLy8gZGVmaW5lIGdldHRlciBmdW5jdGlvbnMgZm9yIGhhcm1vbnkgZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgZGVmaW5pdGlvbikge1xuXHRmb3IodmFyIGtleSBpbiBkZWZpbml0aW9uKSB7XG5cdFx0aWYoX193ZWJwYWNrX3JlcXVpcmVfXy5vKGRlZmluaXRpb24sIGtleSkgJiYgIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBrZXkpKSB7XG5cdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywga2V5LCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZGVmaW5pdGlvbltrZXldIH0pO1xuXHRcdH1cblx0fVxufTsiLCJfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmosIHByb3ApIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApOyB9IiwiLy8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuXHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcblx0fVxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xufTsiLCJpbXBvcnQgeyBtZXNzYWdlIH0gZnJvbSAnLi9qcy9tYXRoLmpzJztcclxuaW1wb3J0ICcuL2pzL2NvbXBvbmVudC5qcydcclxuY29uc29sZS5sb2cobWVzc2FnZSk7Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9

….这老长。

cheap-source-map

会生成 sourcemap,但是会更加高效一些(cheap 低开销)

因为它没有生成列映射(Column Mapping)

因为在开发中,我们只需要行信息通常就可以定位到错误了

cheap-module-source-map

会生成 sourcemap,类似于 cheap-source-map,但是对源自 loader 的 sourcemap 处理会更好

这里有一个很模糊的概念:对源自 loader 的 sourcemap 处理会更好,官方也没有给出很好的解释

其实是如果 loader 对我们的源码进行了特殊的处理,比如 babel,代码不贴了,看下 cheap-source-map 和 cheap-module-source-map 生成文件的区别:

可以看出,cheap-module-source-map 生成的文件更贴近我们的源代码。

hidden-source-map

会生成 sourcemap,但是不会对 source-map 文件进行引用

相当于删除了打包文件中对 sourcemap 的引用注释

如果我们手动添加进来,那么 sourcemap 就会生效了

1
2
// 被删除掉的
//# sourceMappingURL=bundle.js.map

nosources-source-map

会生成 sourcemap,但是生成的 sourcemap 只有错误信息的提示,不会生成源代码文件;

多个值组合

webpack 提供给我们的 26 个值,是可以进行多组合的。

组合的规则如下:

  • inline-|hidden-|eval:三个值时三选一
  • nosources:可选值
  • cheap 可选值,并且可以跟随 module 的值

那么在开发中,最佳的实践是什么呢?

  • 开发阶段:推荐使用 source-map 或者 cheap-module-source-map
    • 这分别是 vue 和 react 使用的值,可以获取调试信息,方便快速开发
  • 测试阶段:推荐使用 source-map 或者 cheap-module-source-map
    • 测试阶段我们也希望在浏览器下看到正确的错误提示
  • 发布阶段:false、缺省值(不写)