Appearance
createChunkAssets
对于这个函数我们需要先了解一下 webpack 的一个属性 output
output 位于对象最顶级键(key),包括了一组选项,指示 webpack 如何去输出、以及在哪里输出你的「bundle、asset 和其他你所打包或使用 webpack 载入的任何内容」。
特别是其中两个值 filename 和 chunkFilename。这两个决定了输出的文件的入口起点的文件名,chunkFilename 决定了非入口(non-entry) chunk 文件的名称。分别对应上一步 seal chunk 图生产中 入口 chunk 和其依赖的异步 blockChunk。
js
let createChunkAssets = () => {
const outputOptions = this.outputOptions;
const cachedSourceMap = new Map();
const alreadyWrittenFiles = new Map();
for (let i = 0; i < this.chunks.length; i++) {
const chunk = this.chunks[i];
chunk.files = [];
let source;
let file;
let filenameTemplate;
try {
const template = chunk.hasRuntime() ? this.mainTemplate : this.chunkTemplate;
const manifest = template.getRenderManifest({
chunk,
hash: this.hash,
fullHash: this.fullHash,
outputOptions,
moduleTemplates: this.moduleTemplates,
dependencyTemplates: this.dependencyTemplates,
}); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
for (const fileManifest of manifest) {
const cacheName = fileManifest.identifier;
const usedHash = fileManifest.hash;
filenameTemplate = fileManifest.filenameTemplate;
const pathAndInfo = this.getPathWithInfo(filenameTemplate, fileManifest.pathOptions);
file = pathAndInfo.path;
const assetInfo = pathAndInfo.info;
// check if the same filename was already written by another chunk
const alreadyWritten = alreadyWrittenFiles.get(file);
if (alreadyWritten !== undefined) {
if (alreadyWritten.hash === usedHash) {
if (this.cache) {
this.cache[cacheName] = {
hash: usedHash,
source: alreadyWritten.source,
};
}
chunk.files.push(file);
this.hooks.chunkAsset.call(chunk, file);
continue;
} else {
throw new Error(
`Conflict: Multiple chunks emit assets to the same filename ${file}` +
` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})`
);
}
}
if (this.cache && this.cache[cacheName] && this.cache[cacheName].hash === usedHash) {
source = this.cache[cacheName].source;
} else {
source = fileManifest.render();
// Ensure that source is a cached source to avoid additional cost because of repeated access
if (!(source instanceof CachedSource)) {
const cacheEntry = cachedSourceMap.get(source);
if (cacheEntry) {
source = cacheEntry;
} else {
const cachedSource = new CachedSource(source);
cachedSourceMap.set(source, cachedSource);
source = cachedSource;
}
}
if (this.cache) {
this.cache[cacheName] = {
hash: usedHash,
source,
};
}
}
this.emitAsset(file, source, assetInfo);
chunk.files.push(file);
this.hooks.chunkAsset.call(chunk, file);
alreadyWrittenFiles.set(file, {
hash: usedHash,
source,
chunk,
});
}
} catch (err) {
this.errors.push(new ChunkRenderError(chunk, file || filenameTemplate, err));
}
}
};
const template = chunk.hasRuntime() ? this.mainTemplate : this.chunkTemplate;
这是根据 chunk 是入口 chunk 还是依赖的异步 blockChunk,来决定生成文件的 template
- 获取 render 对象
js
const manifest = template.getRenderManifest({
chunk,
hash: this.hash,
fullHash: this.fullHash,
outputOptions,
moduleTemplates: this.moduleTemplates,
dependencyTemplates: this.dependencyTemplates,
});
这一步
- 生成文件的路径
js
// filenameTemplate 文件名称的模板 如 * , [name].[hash:8].js
// fileManifest.pathOptions 在步骤2中处理后的文件路径相关信息
const pathAndInfo = this.getPathWithInfo(filenameTemplate, fileManifest.pathOptions);
在这一步也是调用了很多钩子函数。通过处理后的文件路径信息然后调用相应模板的 getAssetPathWithInfo() 去处理资源路径问题。
在主 mainTemplate 的 getAssetPathWithInfo 中是通过 mainTemplate.hooks.assetPath
钩子函数去调用在 WebpackOptionsApply.process() 定义的 TemplatedPathPlugin 插件。
下面我们看这个插件的
js
mainTemplate.hooks.assetPath.tap('TemplatedPathPlugin', replacePathVariables);
/**
* 替换路径中的变量
* @param {[type]} path [description]
* @param {[type]} data [description]
* @param {[type]} assetInfo [description]
* @return {[type]} [description]
*/
const replacePathVariables = (path, data, assetInfo) => {
const chunk = data.chunk;
// chunk 的id
const chunkId = chunk && chunk.id;
// chunk的名称用来替换 name
const chunkName = chunk && (chunk.name || chunk.id);
const chunkHash = chunk && (chunk.renderedHash || chunk.hash);
const chunkHashWithLength = chunk && chunk.hashWithLength;
const contentHashType = data.contentHashType;
const contentHash = (chunk && chunk.contentHash && chunk.contentHash[contentHashType]) || data.contentHash;
const contentHashWithLength =
(chunk && chunk.contentHashWithLength && chunk.contentHashWithLength[contentHashType]) ||
data.contentHashWithLength;
const module = data.module;
const moduleId = module && module.id;
const moduleHash = module && (module.renderedHash || module.hash);
const moduleHashWithLength = module && module.hashWithLength;
if (typeof path === 'function') {
path = path(data);
}
if (data.noChunkHash && (REGEXP_CHUNKHASH_FOR_TEST.test(path) || REGEXP_CONTENTHASH_FOR_TEST.test(path))) {
throw new Error(`Cannot use [chunkhash] or [contenthash] for chunk in '${path}' (use [hash] instead)`);
}
return (
path
// 替换路径中的 [hash] [hash:8]
.replace(REGEXP_HASH, withHashLength(getReplacer(data.hash), data.hashWithLength, assetInfo))
// 替换路径中的 [chunkhash] [chunkhash:8]
.replace(REGEXP_CHUNKHASH, withHashLength(getReplacer(chunkHash), chunkHashWithLength, assetInfo))
// 替换路径中的 [contenthash] [contenthash:8]
.replace(REGEXP_CONTENTHASH, withHashLength(getReplacer(contentHash), contentHashWithLength, assetInfo))
// 替换路径中的 [modulehash] [modulehash:8]
.replace(REGEXP_MODULEHASH, withHashLength(getReplacer(moduleHash), moduleHashWithLength, assetInfo))
// 替换路径中的 [id] 以chunkId作为值
.replace(REGEXP_ID, getReplacer(chunkId))
// 替换路径中的 [moduleid] 以moduleId作为值
.replace(REGEXP_MODULEID, getReplacer(moduleId))
// 替换路径中的 [name] 以chunkName作为值
.replace(REGEXP_NAME, getReplacer(chunkName))
// 替换路径中的 [file] 以filename作为值
.replace(REGEXP_FILE, getReplacer(data.filename))
// 替换路径中的 [filebase] 以basename作为值
.replace(REGEXP_FILEBASE, getReplacer(data.basename))
// query is optional, it's OK if it's in a path but there's nothing to replace it with
// // 替换路径中的 [query] 以chunk的query值
.replace(REGEXP_QUERY, getReplacer(data.query, true))
// only available in sourceMappingURLComment
// 替换路径中的 [url] 以url作为值
.replace(REGEXP_URL, getReplacer(data.url))
.replace(/\[\\(\\*[\w:]+\\*)\\\]/gi, '[$1]')
);
};
下面我们需要分别说明一下这几个值是怎么获取的
hash
chunkhash
contenthash
modulehash
id
模块标识符(module identifier)
moduleid
name
file
filebase
query
url
js
getPathWithInfo(filename, data) {
data = data || {};
data.hash = data.hash || this.hash;
return this.mainTemplate.getAssetPathWithInfo(filename, data);
}
4. render
异步 block chunk 模块
首先我们看一份简单的结果
js
(window['webpackJsonp'] = window['webpackJsonp'] || []).push([
[1],
{
/***/ './resolve/src/modules/asyncModule.js':
/*!********************************************!*\
!*** ./resolve/src/modules/asyncModule.js ***!
\********************************************/
/*! exports provided: default */
/***/ function(module, __webpack_exports__, __webpack_require__) {
'use strict';
eval(
'__webpack_require__.r(__webpack_exports__);\nlet moduleName = \'asyncModule\';\n\n/* harmony default export */ __webpack_exports__["default"] = ({\n\tmoduleName: moduleName,\n\tfunc() {\n\t\tconsole.log(moduleName);\n\t}\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL2FzeW5jTW9kdWxlLmpzLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vL3Jlc29sdmUvc3JjL21vZHVsZXMvYXN5bmNNb2R1bGUuanM/ZDQzOCJdLCJzb3VyY2VzQ29udGVudCI6WyJsZXQgbW9kdWxlTmFtZSA9ICdhc3luY01vZHVsZSc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcblx0bW9kdWxlTmFtZTogbW9kdWxlTmFtZSxcclxuXHRmdW5jKCkge1xyXG5cdFx0Y29uc29sZS5sb2cobW9kdWxlTmFtZSk7XHJcblx0fSxcclxufTtcclxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBSkEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/modules/asyncModule.js\n'
);
/***/
},
},
]);
js
source = fileManifest.render();
这一步主要做以下:
- 处理 chunk 中依赖的模块代码
renderChunkModules()
这个方法的主要流程是处理模块中所有的同步和异步模块,然后对于同步的模块经过 Template.renderChunkModules()
-> chunkTemplate.hooks.modules.call()
-> chunkTemplate.hooks.render.call()
-> chunkTemplate.hooks.renderWithEntry.call()
,然后在最后将所有的模块作为一个对象的值,其 key 为 module 的 id 处理。
这样其结构就变成如下:
js
// 还是有一些其他的注释
{
"./resolve/src/modules/asyncModule1.js" :
/*!*********************************************!
*!*** ./resolve/src/modules/asyncModule1.js ***!
*********************************************/
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("具体的代码 //# sourceURL=[module]\n//# sourceMappingURL=")
})
}
简要代码如下
js
static renderChunkModules(
chunk,
filterFn,
moduleTemplate,
dependencyTemplates,
prefix = ""
) {
// 处理当前chunk中所有的模块将其变成可以执行的代码
const allModules = modules.map(module => {
return {
id: module.id,
source: moduleTemplate.render(module, dependencyTemplates, {
chunk
})
};
});
// 将所有的处理后的模块代码作为值,module.id为键变成一个对象
source.add("{\n");
allModules.sort(stringifyIdSortPredicate).forEach((module, idx) => {
if (idx !== 0) {
source.add(",\n");
}
source.add(`\n/***/ ${JSON.stringify(module.id)}:\n`);
source.add(module.source);
});
source.add(`\n\n${prefix}}`);
return source;
}
处理每一个的模块的代码如下:
js
renderJavascript(chunkTemplate, chunk, moduleTemplate, dependencyTemplates) {
// 生成子chunk的代码
const moduleSources = Template.renderChunkModules(
chunk,
m => typeof m.source === "function",
moduleTemplate,
dependencyTemplates
);
const core = chunkTemplate.hooks.modules.call(
moduleSources,
chunk,
moduleTemplate,
dependencyTemplates
);
let source = chunkTemplate.hooks.render.call(
core,
chunk,
moduleTemplate,
dependencyTemplates
);
if (chunk.hasEntryModule()) {
source = chunkTemplate.hooks.renderWithEntry.call(source, chunk);
}
chunk.rendered = true;
return new ConcatSource(source, ";");
}
- 处理模块中的依赖和导出
module.source( dependencyTemplates, this.runtimeTemplate, this.type )
这一步很重要主要是操作模块的内容的,如处理模块中的依赖和导出
import common from '../common/common';
转变成 "/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./resolve/src/common/common.js");"
- 生成 module 的 sourcemap 内容和模块调用代码
const moduleSourcePostModule = this.hooks.module.call( moduleSourcePostContent, module, options, dependencyTemplates );
最后结果就变成这个样子了
如上图所示,其生成当前模块的 sourcemap 代码
- 将处理后的代码变成一个函数,在加载后自动执行
this.hooks.render.call( moduleSourcePostModule, module, options, dependencyTemplates )
在 module 中只是生成了 module 的内容代码,下面需要再给其添加 调用的代码,如下图:
可见其给每一个模块添加了一个(function(module, __webpack_exports__, __webpack_require__){ ... })
- 添加模块化注释代码
this.hooks.package.call( moduleSourcePostRender, module, options, dependencyTemplates )
继续操作上面的代码,在这一步主要就是添加模块化的注释代码,表明这个模块原来的路径是什么
可见其给每一个模块添加了一个./resolve/src/modules/asyncModule1_1.js
的注释
- 添加异步模块加载的代码
let source = chunkTemplate.hooks.render.call( core, chunk, moduleTemplate, dependencyTemplates );
这一步是添加 jsonp 的代码
这样其结构就变成如下:
js
(window['webpackJsonp'] = window['webpackJsonp'] || []).push([
[`${chunk.ids}`],
{
模块的代码,
},
]);
上面这是对于异步模块 chunk 的处理方式,还有对于入口模块的处理方式
入口模块
对于入口模块其处理方式跟上面不一样,首先其使用的是mainTemplate去处理入口 chunk,然后其经历一下几个过程
js
render(hash, chunk, moduleTemplate, dependencyTemplates) {
const buf = this.renderBootstrap(
hash,
chunk,
moduleTemplate,
dependencyTemplates
);
let source = this.hooks.render.call(
new OriginalSource(
Template.prefix(buf, " \t") + "\n",
"webpack/bootstrap"
),
chunk,
hash,
moduleTemplate,
dependencyTemplates
);
if (chunk.hasEntryModule()) {
source = this.hooks.renderWithEntry.call(source, chunk, hash);
}
if (!source) {
throw new Error(
"Compiler error: MainTemplate plugin 'render' should return something"
);
}
chunk.rendered = true;
return new ConcatSource(source, ";");
}
- 其中
renderBootstrap
是主要的过程,在这个方法中其生成启动代码、异步模块初始化加载和 prefetch 延迟加载、module 缓存等代码,大体如下:
js
let render = (hash, chunk, moduleTemplate, dependencyTemplates) => {
// 生成入口模块的启动代码
const buf = this.renderBootstrap(hash, chunk, moduleTemplate, dependencyTemplates);
};
/**
* TODO webpack 5: remove moduleTemplate and dependencyTemplates
* @param {string} hash hash to be used for render call
* @param {Chunk} chunk Chunk instance
* @param {ModuleTemplate} moduleTemplate ModuleTemplate instance for render
* @param {Map<Function, DependencyTemplate>} dependencyTemplates dependency templates
* @returns {string[]} the generated source of the bootstrap code
*/
renderBootstrap(hash, chunk, moduleTemplate, dependencyTemplates) {
const buf = [];
buf.push(
this.hooks.bootstrap.call(
"",
chunk,
hash,
moduleTemplate,
dependencyTemplates
)
);
buf.push(this.hooks.localVars.call("", chunk, hash));
buf.push("");
buf.push("// The require function");
buf.push(`function ${this.requireFn}(moduleId) {`);
buf.push(Template.indent(this.hooks.require.call("", chunk, hash)));
buf.push("}");
buf.push("");
buf.push(
Template.asString(this.hooks.requireExtensions.call("", chunk, hash))
);
buf.push("");
buf.push(Template.asString(this.hooks.beforeStartup.call("", chunk, hash)));
const afterStartupCode = Template.asString(
this.hooks.afterStartup.call("", chunk, hash)
);
if (afterStartupCode) {
// TODO webpack 5: this is a bit hacky to avoid a breaking change
// change it to a better way
buf.push("var startupResult = (function() {");
}
buf.push(Template.asString(this.hooks.startup.call("", chunk, hash)));
if (afterStartupCode) {
buf.push("})();");
buf.push(afterStartupCode);
buf.push("return startupResult;");
}
return buf;
}
具体通过以下过程:
- jsonp 结果回调方法
buf.push( this.hooks.bootstrap.call( "", chunk, hash, moduleTemplate, dependencyTemplates ) );
这一部分处理的是生成一下代码
js
/******/ // 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 (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/
}
/******/ installedChunks[chunkId] = 0;
/******/
}
/******/ for (moduleId in moreModules) {
/******/ if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/
}
/******/
}
/******/ if (parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ while (resolves.length) {
/******/ resolves.shift()();
/******/
}
/******/
/******/
} // The module cache
- 模块一些变量的处理,如 installedModules、installedCssChunks
this.hooks.localVars.call("", chunk, hash)
这一步有多个钩子函数分别为
this.hooks.localVars.tap("MainTemplate", (source, chunk, hash) => {})
这个钩子函数代码甚少就是添加模块缓存
js
this.hooks.localVars.tap('MainTemplate', (source, chunk, hash) => {
return Template.asString([source, '// The module cache', 'var installedModules = {};']);
});
mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => {})
这个钩子函数是 mini-css-extra-plugin 添加的,主要是添加加载的 css 的缓存
js
mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => {
const chunkMap = this.getCssChunkObject(chunk);
if (Object.keys(chunkMap).length > 0) {
return Template.asString([
source,
'',
'// object to store loaded CSS chunks',
'var installedCssChunks = {',
Template.indent(chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(',\n')),
'}',
]);
}
return source;
});
mainTemplate.hooks.localVars.tap( "JsonpMainTemplatePlugin", (source, chunk, hash) => {})
这个钩子函数是 JsonpMainTemplatePlugin 添加的,主要是处理入口模块中异步 block chunks 的,分为初始化就加载的 chunk 其存放在 installedChunks,延迟加载的,存放在deferredModules,同时生成异步加载 chunkd 的路径 path 的函数 jsonpScriptSrc
js
mainTemplate.hooks.localVars.tap('JsonpMainTemplatePlugin', (source, chunk, hash) => {
const extraCode = [];
// 是否存在异步block chunk,
if (needChunkLoadingCode(chunk)) {
extraCode.push(
'',
'// object to store loaded and loading chunks',
'// undefined = chunk not loaded, null = chunk preloaded/prefetched',
'// Promise = chunk loading, 0 = chunk loaded',
'var installedChunks = {',
Template.indent(chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(',\n')),
'};',
'',
needEntryDeferringCode(chunk)
? needPrefetchingCode(chunk)
? 'var deferredModules = [], deferredPrefetch = [];'
: 'var deferredModules = [];'
: ''
);
}
// 生成异步加载的chunk的路径获取函数
if (needChunkOnDemandLoadingCode(chunk)) {
extraCode.push(
'',
'// script path function',
'function jsonpScriptSrc(chunkId) {',
Template.indent([`return ${mainTemplate.requireFn}.p + ${getScriptSrcPath(hash, chunk, 'chunkId')}`]),
'}'
);
}
if (extraCode.length === 0) return source;
return Template.asString([source, ...extraCode]);
});
通过上面的代码在入口 chunk 中又添加了一下的代码
mainTemplate.hooks.localVars.tap( "WasmMainTemplatePlugin", (source, chunk) => {})
这个钩子函数是 WasmMainTemplatePlugin 添加的,主要是处理二进制模块的,暂时不做阅读
this.hooks.require.tap("MainTemplate", (source, chunk, hash) => {})
最后我们看一下通过 renderBootstrap 具体生成的结果
this.hooks.render.call()
在这个钩子函数中我们也找到一个事件 MainTemplate,然后我们看这个钩子函数,其主要做两件事:
将 renderBootstrap 通过一个函数包裹
(function(modules) { renderBootstrap内容 })( module内容 )
通过
MainTemplate.hooks.modules.call(new RawSource(''), chunk, hash, moduleTemplate, dependencyTemplates)
生成入口 chunk 依赖的同步 module 的内容,具体可以参考前面的renderChunkModules
的流程,这边也是通过 Template.js 去处理入口 chunk 依赖的同步 module,然后作为前面 function 的参数。
源码如下:
js
this.hooks.render.tap('MainTemplate', (bootstrapSource, chunk, hash, moduleTemplate, dependencyTemplates) => {
const source = new ConcatSource();
source.add('/******/ (function(modules) { // webpackBootstrap\n');
source.add(new PrefixSource('/******/', bootstrapSource));
source.add('/******/ })\n');
source.add('/************************************************************************/\n');
source.add('/******/ (');
// 生成同步module的依赖对象 如 {}
source.add(this.hooks.modules.call(new RawSource(''), chunk, hash, moduleTemplate, dependencyTemplates));
source.add(')');
return source;
});
- 装换成 ConcatSource 实例对象
最后 将 chunk 标记为已 render(chunk.rendered = true
), 然后将 render 的结果作为参数生成 ConcatSource 的实例对象
总结
对于入口 EntryModule,其跟依赖的 blockchunk 的处理方式有异有同,分别如下:
不同点:
- 加载方式
对于 blockChunk,其不是直接添加到 html 中而是通过入口 EntryModule 去决定其初始化加载还是可能 prefetch 加载,所以需要在入口 EntryModule 生成加载异步 blockchunk 的方法(**webpack_require.e**)
对于入口 EntryModule,其 chunk.hasEntryModule 所以可以在 html-webpack-plugin 中去判断直接插入到 html 中
- 对于同步 module 的加载方式不一样
在 blockChunk 中其将所有的模块存放在
window['webpackJsonp']
, 然后在入口 module 中修改window['webpackJsonp'].push
方法,使得在执行异步 blockChunk 的时候将异步 blockChunk 的 module 通过 webpackJsonpCallback()添加到 modules 中对于入口 EntryModule 中依赖的通过 modules,其直接作为参数 modules 的值(
(function(modules) {})({ module1 , module2 ...})
)
相同点
- 同时通过 Template.js 去处理依赖的同步 module,并将其变成一个 key 为module.id,值为 module 的内容的对象属性
js
/******/ (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 (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/
}
/******/ installedChunks[chunkId] = 0;
/******/
}
/******/ for (moduleId in moreModules) {
/******/ if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/
}
/******/
}
/******/ if (parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ while (resolves.length) {
/******/ resolves.shift()();
/******/
}
/******/
/******/
} // The module cache
/******/
/******/
/******/ /******/ var installedModules = {}; // object to store loaded and loading chunks // undefined = chunk not loaded, null = chunk preloaded/prefetched // Promise = chunk loading, 0 = chunk loaded
/******/
/******/ /******/ /******/ /******/ var installedChunks = {
/******/ main: 0,
/******/
}; // script path function
/******/
/******/
/******/
/******/ /******/ function jsonpScriptSrc(chunkId) {
/******/ return __webpack_require__.p + '' + ({}[chunkId] || chunkId) + '.js';
/******/
} // 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;
/******/
} // This file contains only the entry chunk. // The chunk loading function for additional chunks
/******/
/******/ /******/ /******/ __webpack_require__.e = function requireEnsure(chunkId) {
/******/ var promises = []; /******/ // JSONP chunk loading for javascript
/******/
/******/
/******/ /******/ var installedChunkData = installedChunks[chunkId];
/******/ if (installedChunkData !== 0) {
// 0 means "already installed".
/******/
/******/ // a Promise means "currently loading".
/******/ if (installedChunkData) {
/******/ promises.push(installedChunkData[2]);
/******/
} else {
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise(function(resolve, reject) {
/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
/******/
});
/******/ promises.push((installedChunkData[2] = promise)); // start chunk loading
/******/
/******/ /******/ 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); // create error before stack unwound to get useful stacktrace later
/******/
/******/ /******/ var error = new Error();
/******/ onScriptComplete = function(event) {
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var chunk = installedChunks[chunkId];
/******/ if (chunk !== 0) {
/******/ if (chunk) {
/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ var realSrc = event && event.target && event.target.src;
/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ error.name = 'ChunkLoadError';
/******/ 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);
/******/
}
/******/
}
/******/ 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 = './resolve/src/main.js'));
/******/
})(
/************************************************************************/
/******/ {
/***/ './resolve/src/common/common.js':
/*!**************************************!*\
!*** ./resolve/src/common/common.js ***!
\**************************************/
/*! exports provided: default */
/***/ function(module, __webpack_exports__, __webpack_require__) {
'use strict';
eval(
'__webpack_require__.r(__webpack_exports__);\nconst a = \'123\';\n\n/* harmony default export */ __webpack_exports__["default"] = ({\n\ta\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9jb21tb24vY29tbW9uLmpzLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vL3Jlc29sdmUvc3JjL2NvbW1vbi9jb21tb24uanM/ZGQ3ZSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBhID0gJzEyMyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcblx0YSxcclxufTtcclxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBREEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/common/common.js\n'
);
/***/
},
/***/ './resolve/src/css/css.css':
/*!*********************************!*\
!*** ./resolve/src/css/css.css ***!
\*********************************/
/*! no static exports found */
/***/ function(module, exports, __webpack_require__) {
eval(
'// extracted by mini-css-extract-plugin//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9jc3MvY3NzLmNzcy5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3Jlc29sdmUvc3JjL2Nzcy9jc3MuY3NzPzVhMGUiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gZXh0cmFjdGVkIGJ5IG1pbmktY3NzLWV4dHJhY3QtcGx1Z2luIl0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./resolve/src/css/css.css\n'
);
/***/
},
/***/ './resolve/src/main.js':
/*!*****************************!*\
!*** ./resolve/src/main.js ***!
\*****************************/
/*! no exports provided */
/***/ function(module, __webpack_exports__, __webpack_require__) {
'use strict';
eval(
'__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _modules_module1__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./modules/module1 */ "./resolve/src/modules/module1.js");\n/* harmony import */ var _modules_module2__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @/modules/module2 */ "./resolve/src/modules/module2.js");\n/* harmony import */ var _css_css_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @/css/css.css */ "./resolve/src/css/css.css");\n/* harmony import */ var _css_css_css__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_css_css_css__WEBPACK_IMPORTED_MODULE_2__);\n\n\n\nvar name = "development" + \'-ele\';\n\nconst module3 = __webpack_require__(/*! ./modules/module3 */ "./resolve/src/modules/module3.js");\n\nconst moduleName = \'main\';\n\nObject(_modules_module2__WEBPACK_IMPORTED_MODULE_1__["func"])();\n\n\n\nconst asyncModule = resolve => __webpack_require__.e(/*! AMD require */ 1).then(function() { var __WEBPACK_AMD_REQUIRE_ARRAY__ = [__webpack_require__(/*! @/modules/asyncModule */ "./resolve/src/modules/asyncModule.js")]; (resolve).apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);}.bind(this)).catch(__webpack_require__.oe);\n\nconst asyncModule1 = resolve => __webpack_require__.e(/*! AMD require */ 2).then(function() { var __WEBPACK_AMD_REQUIRE_ARRAY__ = [__webpack_require__(/*! @/modules/asyncModule1 */ "./resolve/src/modules/asyncModule1.js")]; (resolve).apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);}.bind(this)).catch(__webpack_require__.oe);\n\nconsole.log(module3);\nconsole.log(asyncModule);\nconsole.log(asyncModule1);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tYWluLmpzLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vL3Jlc29sdmUvc3JjL21haW4uanM/YTk4ZSJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgbW9kdWxlMSBmcm9tICcuL21vZHVsZXMvbW9kdWxlMSc7XHJcbmltcG9ydCB7IGZ1bmMgfSBmcm9tICdAL21vZHVsZXMvbW9kdWxlMic7XHJcblxyXG52YXIgbmFtZSA9IHByb2Nlc3MuZW52Lk5PREVfRU5WICsgJy1lbGUnO1xyXG5cclxuY29uc3QgbW9kdWxlMyA9IHJlcXVpcmUoJy4vbW9kdWxlcy9tb2R1bGUzJyk7XHJcblxyXG5jb25zdCBtb2R1bGVOYW1lID0gJ21haW4nO1xyXG5cclxuZnVuYygpO1xyXG5cclxuaW1wb3J0ICdAL2Nzcy9jc3MuY3NzJztcclxuXHJcbmNvbnN0IGFzeW5jTW9kdWxlID0gcmVzb2x2ZSA9PiByZXF1aXJlKC8qIHdlYnBhY2tDaHVua05hbWU6IFwiYXN5bmNNb2R1bGVcIiAqLyBbJ0AvbW9kdWxlcy9hc3luY01vZHVsZSddLCByZXNvbHZlKTtcclxuXHJcbmNvbnN0IGFzeW5jTW9kdWxlMSA9IHJlc29sdmUgPT4gcmVxdWlyZSgvKiB3ZWJwYWNrQ2h1bmtOYW1lOiBcImFzeW5jTW9kdWxlMVwiICovIFsnQC9tb2R1bGVzL2FzeW5jTW9kdWxlMSddLCByZXNvbHZlKTtcclxuXHJcbmNvbnNvbGUubG9nKG1vZHVsZTMpO1xyXG5jb25zb2xlLmxvZyhhc3luY01vZHVsZSk7XHJcbmNvbnNvbGUubG9nKGFzeW5jTW9kdWxlMSk7XHJcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/main.js\n'
);
/***/
},
/***/ './resolve/src/modules/module1.js':
/*!****************************************!*\
!*** ./resolve/src/modules/module1.js ***!
\****************************************/
/*! exports provided: default */
/***/ function(module, __webpack_exports__, __webpack_require__) {
'use strict';
eval(
'__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./resolve/src/common/common.js");\n\n\nlet moduleName = \'module1\';\n\n/* harmony default export */ __webpack_exports__["default"] = ({\n\tmoduleName: moduleName,\n\tfunc() {\n\t\tconsole.log(moduleName);\n\t},\n\tcommon: _common_common__WEBPACK_IMPORTED_MODULE_0__["default"]\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTEuanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vcmVzb2x2ZS9zcmMvbW9kdWxlcy9tb2R1bGUxLmpzP2RmMjUiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNvbW1vbiBmcm9tICcuLi9jb21tb24vY29tbW9uJztcclxuXHJcbmxldCBtb2R1bGVOYW1lID0gJ21vZHVsZTEnO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG5cdG1vZHVsZU5hbWU6IG1vZHVsZU5hbWUsXHJcblx0ZnVuYygpIHtcclxuXHRcdGNvbnNvbGUubG9nKG1vZHVsZU5hbWUpO1xyXG5cdH0sXHJcblx0Y29tbW9uXHJcbn07XHJcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBTEEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/modules/module1.js\n'
);
/***/
},
/***/ './resolve/src/modules/module2.js':
/*!****************************************!*\
!*** ./resolve/src/modules/module2.js ***!
\****************************************/
/*! exports provided: func, moduleName */
/***/ function(module, __webpack_exports__, __webpack_require__) {
'use strict';
eval(
'__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "func", function() { return func; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "moduleName", function() { return moduleName; });\n/* harmony import */ var _module2_1__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./module2_1 */ "./resolve/src/modules/module2_1.js");\n\n\nconsole.log(_module2_1__WEBPACK_IMPORTED_MODULE_0__["default"]);\nconst func = () => {\n\tconsole.log(moduleName);\n};\n\nconst moduleName = \'module2\';//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTIuanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vcmVzb2x2ZS9zcmMvbW9kdWxlcy9tb2R1bGUyLmpzP2QzMjQiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IG1vZHVsZTJfMSBmcm9tICcuL21vZHVsZTJfMSc7XHJcblxyXG5jb25zb2xlLmxvZyhtb2R1bGUyXzEpO1xyXG5leHBvcnQgY29uc3QgZnVuYyA9ICgpID0+IHtcclxuXHRjb25zb2xlLmxvZyhtb2R1bGVOYW1lKTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBtb2R1bGVOYW1lID0gJ21vZHVsZTInO1xyXG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./resolve/src/modules/module2.js\n'
);
/***/
},
/***/ './resolve/src/modules/module2_1.js':
/*!******************************************!*\
!*** ./resolve/src/modules/module2_1.js ***!
\******************************************/
/*! exports provided: func, moduleName */
/***/ function(module, __webpack_exports__, __webpack_require__) {
'use strict';
eval(
'__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "func", function() { return func; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "moduleName", function() { return moduleName; });\nconst func = () => {\n\tconsole.log(moduleName);\n};\n\nconst moduleName = \'module2_1\';//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTJfMS5qcy5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTJfMS5qcz9kMDg1Il0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBjb25zdCBmdW5jID0gKCkgPT4ge1xyXG5cdGNvbnNvbGUubG9nKG1vZHVsZU5hbWUpO1xyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IG1vZHVsZU5hbWUgPSAnbW9kdWxlMl8xJztcclxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUFBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./resolve/src/modules/module2_1.js\n'
);
/***/
},
/***/ './resolve/src/modules/module3.js':
/*!****************************************!*\
!*** ./resolve/src/modules/module3.js ***!
\****************************************/
/*! exports provided: default */
/***/ function(module, __webpack_exports__, __webpack_require__) {
'use strict';
eval(
'__webpack_require__.r(__webpack_exports__);\nlet moduleName = \'module3\';\n\n/* harmony default export */ __webpack_exports__["default"] = ({\n\tmoduleName: moduleName,\n\tfunc() {\n\t\tconsole.log(moduleName);\n\t}\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTMuanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vcmVzb2x2ZS9zcmMvbW9kdWxlcy9tb2R1bGUzLmpzPzNkZmEiXSwic291cmNlc0NvbnRlbnQiOlsibGV0IG1vZHVsZU5hbWUgPSAnbW9kdWxlMyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcblx0bW9kdWxlTmFtZTogbW9kdWxlTmFtZSxcclxuXHRmdW5jKCkge1xyXG5cdFx0Y29uc29sZS5sb2cobW9kdWxlTmFtZSk7XHJcblx0fSxcclxufTtcclxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBSkEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/modules/module3.js\n'
);
/***/
},
/******/
}
);
5. emitAsset()
在 render 中通过 mainTemplate 或者 chunkTemplate 等去生成了各个 chunk 的 source 内容,最后存放在 ConcatSource 实例中,然后又通过 CachedSource、cachedSourceMap、complation.cache 三个去缓存生成的结果。然后进入到 this.emitAsset(file, source, assetInfo);
中。
emitAsset()作用只是去通过生成的 asset 的 file 名称去缓存 source 的内容
6. this.hooks.chunkAsset.call(chunk, file);
没有了...