You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(self["webpackChunkwebpack_demo"]=self["webpackChunkwebpack_demo"]||[]).push([[179],{/***/573:
/***/((__unused_webpack_module,__unused_webpack___webpack_exports__,__webpack_require__)=>{"use strict";;// CONCATENATED MODULE: ./src/js/title.jsvargetTitle=functiongetTitle(){return'柴门闻犬吠,风雪夜归人。';};// EXTERNAL MODULE: ./src/js/log.jsvarlog=__webpack_require__(967);varlog_default=/*#__PURE__*/__webpack_require__.n(log);;// CONCATENATED MODULE: ./src/images/logo.svgconstlogo_namespaceObject="data:image/svg+xml;base64,...........";;// CONCATENATED MODULE: ./src/index.js// Test import of a JavaScript function// Test import of an asset// Test import of stylesvarlogo=document.createElement('img');logo.src=logo_namespaceObject;varheading=document.createElement('h1');heading.textContent=getTitle();varapp=document.querySelector('#root');app.append(logo,heading);setTimeout(function(){__webpack_require__.e(/* import() | info */996).then(__webpack_require__.bind(__webpack_require__,67)).then(function(v){varfooter=document.createElement('footer');footer.textContent=v.text;app.append(footer);log_default()();});},100);__webpack_require__.e(/* import() */232).then(__webpack_require__.bind(__webpack_require__,232)).then(function(v){vardiv=document.createElement('div');div.textContent=v.author;app.append(div);});/***/}),/***/967:
/***/((module)=>{module.exports=function(){console.log('xxxxxxxxx');};/***/})},/******/__webpack_require__=>{// webpackRuntimeModules/******//* webpack/runtime/startup prefetch *//******/(()=>{/******/__webpack_require__.O(0,[179],()=>{/******/__webpack_require__.E(996);/******/},5);/******/})();/******//******/var__webpack_exec__=(moduleId)=>(__webpack_require__(__webpack_require__.s=moduleId))/******/var__webpack_exports__=(__webpack_exec__(573));/******/}]);
/* webpack/runtime/load script */(()=>{varinProgress={};vardataWebpackPrefix="webpack-demo:";// loadScript function to load a script via script tag__webpack_require__.l=(url,done,key,chunkId)=>{// 如果已经在加载中了,直接返回if(inProgress[url]){inProgress[url].push(done);return;}varscript,needAttach;if(key!==undefined){varscripts=document.getElementsByTagName("script");for(vari=0;i<scripts.length;i++){vars=scripts[i];if(s.getAttribute("src")==url||s.getAttribute("data-webpack")==dataWebpackPrefix+key){script=s;break;}}}if(!script){needAttach=true;script=document.createElement('script');script.charset='utf-8';script.timeout=120;if(__webpack_require__.nc){script.setAttribute("nonce",__webpack_require__.nc);}script.setAttribute("data-webpack",dataWebpackPrefix+key);script.src=url;}inProgress[url]=[done];varonScriptComplete=(prev,event)=>{// avoid mem leaks in IE.script.onerror=script.onload=null;clearTimeout(timeout);vardoneFns=inProgress[url];// 加载完成就删除inProgress[url]deleteinProgress[url];script.parentNode&&script.parentNode.removeChild(script);doneFns&&doneFns.forEach((fn)=>(fn(event)));if(prev)returnprev(event);};vartimeout=setTimeout(onScriptComplete.bind(null,undefined,{type: 'timeout',target: script}),120000);script.onerror=onScriptComplete.bind(null,script.onerror);script.onload=onScriptComplete.bind(null,script.onload);needAttach&&document.head.appendChild(script);};})();/* webpack/runtime/ensure chunk */(()=>{__webpack_require__.f={};// This file contains only the entry chunk.// The chunk loading function for additional chunks__webpack_require__.e=(chunkId)=>{returnPromise.all(Object.keys(__webpack_require__.f).reduce((promises,key)=>{__webpack_require__.f[key](chunkId,promises);returnpromises;},[]));};})();// import()|require.ensure 的核心实现代码__webpack_require__.f.j=(chunkId,promises)=>{// JSONP chunk loading for javascriptvarinstalledChunkData=__webpack_require__.o(installedChunks,chunkId) ? installedChunks[chunkId] : undefined;if(installedChunkData!==0){// 0 means "already installed".// a Promise means "currently loading".if(installedChunkData){// 正在加载,那么取出promise放到 promises 即可promises.push(installedChunkData[2]);}else{if(666!=chunkId){// setup Promise in chunk cachevarpromise=newPromise((resolve,reject)=>(installedChunkData=installedChunks[chunkId]=[resolve,reject]));promises.push(installedChunkData[2]=promise);// start chunk loading 构造异步模块的正确加载路径(publicPath + filename)varurl=__webpack_require__.p+__webpack_require__.u(chunkId);// create error before stack unwound to get useful stacktrace latervarerror=newError();// 只需要处理加载失败的问题(因为chunk代码执行会更新installedChunks[chunkId]为0)varloadingEnded=(event)=>{if(__webpack_require__.o(installedChunks,chunkId)){installedChunkData=installedChunks[chunkId];// 加入没有加载成功(chunk代码执行会更新installedChunks[chunkId]为0),// 则置为 undefined(chunk not loaded)if(installedChunkData!==0)installedChunks[chunkId]=undefined;if(installedChunkData){varerrorType=event&&(event.type==='load' ? 'missing' : event.type);varrealSrc=event&&event.target&&event.target.src;error.message='Loading chunk '+chunkId+' failed.\n('+errorType+': '+realSrc+')';error.name='ChunkLoadError';error.type=errorType;error.request=realSrc;installedChunkData[1](error);}}};__webpack_require__.l(url,loadingEnded,"chunk-"+chunkId,chunkId);}elseinstalledChunks[chunkId]=0;}}};
(一)从打包产物——html 开始
从浏览器角度,html 是应用入口和运行起点。可以看到,编译出的JS通过
<script>
标签插入了 html。/js/runtime.dacd45666f2c5eb35b8d.bundle.js
是 webpack 提供的运行时,是编译出代码能运行的基础;/js/main.7086fc1df0c0b6a0d989.bundle.js
是我们的入口模块(可能因为CONCATENATE MODULE
的优化,除了入口module外有其它module的代码)。这里需额外注意到:
defer
异步加载,不阻塞 html 下载和解析,且保证这些 script 按序执行(保证了runtime先执行);按序执行非常重要,runtime 脚本提供了webpack的全局变量/方法,提供了模块加载执行的能力等等。(二)打包产物——模块/chunk 是怎么包装的?
简单来说,我们写的js文件(模块/module)被webpack处理后变成了什么?
有最简单的
src/js/info.js
:编译后就是
dist/js/info.be139a6f62da8ba74d4c.chunk.js
:为了让编译后的代码保持可读性,webpack的配置去除了压缩。我们可以看到:
代码被包装在
(__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 我们的真正代码 }
;(module) => { 我们的真正代码 }
,这取决于代码是否 ES Module 形式的等等。module|exports|require
的环境,保证了我们代码能正常运行。当然这三个变量webpack会编译并匹配包装函数。__webpack_require__
上提供了一些工具函数,具体是什么下个章节讨论。webpack的编译产出是 chunk,是一个或多个module产出一个chunk。所以我们看到
{moduleId: wrappedModuleCode}
的形式:{67: (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 代码 })}
。最终chunk的形式是
(self["webpackChunkwebpack_demo"] = self["webpackChunkwebpack_demo"] || []).push(参数)
。self["webpackChunkwebpack_demo"].push
保证了数组里面如果有代码需要运行是可以运行的,而不是静态的数组。[[chunkId], moreModules, runtime]
,runtime
就是在 push 时可以运行的代码。看看entry point编译出的 initial chunk 会是什么样?
(三)打包产物——webpack runtime 有哪些功能
一切魔法都在 webpack runtime 脚本和模块代码的包装中,从 webpack runtime 开始分析编译出的代码,看看一个简单的项目是怎么跑起来的。而我们写的
import/export
这些模块化的代码,是怎么成功跑在浏览器里的。这里首先明确一个概念:
import()
或者SplitChunksPlugin
产生。import/require/<img src=...>
等等。一定程度上,中文“模块”一词可能存在混用以上概念,注意分辨。
然后我们正式来分析 webpack runtime。webpack runtime 代码根据配置/是否有异步模块等不同,稍有不同,但总共不超过几百行代码。
webpack runtime 结构
整个webpack runtime是个IIFE(Immediately Invoked Function Expression),整体结构如下:
定义了
__webpack_modules__
来存储所有模块,定义了__webpack_require__
来作为模块中用到的require
方法。通过一堆 IIFE 在
__webpack_require__
上定义了一堆工具函数,这些函数可以被编译的模块去使用。最后一个IIFE中定义了一个唯一全局变量
'webpackChunkwebpack_demo'
,该变量为数组,提供 push 方法来加载 chunk。output.chunkLoadingGlobal
等配置确定(默认值是'webpackChunkwebpack'
);webpackChunkwebpack_demo.push
来执行,或者说所有的模块被它包装:(self["webpackChunkwebpack_demo"] = self["webpackChunkwebpack_demo"] || []).push([[id],{id:code},runtime])
最终我们的入口 chunk 执行时,通过
webpackChunkwebpack_demo.push
来最终执行业务代码。webpack runtime 中的工具函数解析
核心的
'webpackChunkwebpack_demo'
(即webpackJsonpCallback
)webpackChunkwebpack_demo.push
是什么?webpackChunkwebpack_demo.push
最终调用webpackJsonpCallback
。来具体看看
webpackJsonpCallback
作用是什么。请结合上面的 initial chunk 代码来看(看调用chunkLoadingGlobal.push
的传参)。webpackJsonpCallback
作为真正的业务模块调用入口,主要就是把 push 进来的 chunk 指定的模块加载到__webpack_modules__
,并执行 chunk 指定的业务代码(模块)。__webpack_require__.O
在干什么?webpackJsonpCallback
中只剩__webpack_require__.O
没搞明白作用,这一小节来解释下。多数时候
__webpack_require__.O
接收的参数全部是undefined
,即什么都不干。所以略过它多数时候也不影响理解webpack流程。但我们的入口 chunk 其实会传入不一样的参数,我们来看下:这是initial chunk的runtime函数执行时,会调用
__webpack_require__.O
,并且是在业务代码执行前。然后等webpackJsonpCallback
执行时,__webpack_require__.O
会执行第二次,此时会开始 prefetch id 为996
的 chunk。这符合预期:prefetch 在父 chunk 加载完成后开始。
__webpack_require__.F
和__webpack_require__.E
怎么配合完成 prefetch/preload代码一目了然。
重要的jsonp加载chunk相关
__webpack_require__.l
&__webpack_require__.f.j
&__webpack_require__.e
这里讲讲 webpack 最基本的怎么加载 chunk 的(jsonp)。
显而易见的
__webpack_require__.o
&__webpack_require__.r
&__webpack_require__.d
1.
__webpack_require__.o
即hasOwnProperty
2.
__webpack_require__.r
给
exports
对象加上__esModule=true
等标记。3.
__webpack_require__.d
给
exports
对象加上我们导出的那些属性/方法(其它模块通过import
来使用)。注意,这里是通过getter
来导出。The text was updated successfully, but these errors were encountered: