千家信息网

webpack动态import原理是什么

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,今天小编给大家分享一下webpack动态import原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下
千家信息网最后更新 2025年01月20日webpack动态import原理是什么

今天小编给大家分享一下webpack动态import原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

例子

// index.jsimport('./test').then(fn => {  console.log(fn.default());})
// test.jsexport default function func() {  return 1}

打包后的代码包含两个文件 bundle.js 和 0.js

点击展开bundle.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 + ".bundle.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 = "./src/index.js");/******/ })/************************************************************************//******/ ({/***/ "./src/index.js":/*!**********************!*\  !*** ./src/index.js ***!  \**********************//*! no static exports found *//***/ (function(module, exports, __webpack_require__) {eval("__webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./test */ \"./src/test.js\")).then(function (fn) {\n  console.log(fn[\"default\"]());\n});\n\n//# sourceURL=webpack:///./src/index.js?");/***/ })/******/ });

点击展开0.js

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0],{/***/ "./src/test.js":/*!*********************!*\  !*** ./src/test.js ***!  \*********************//*! exports provided: default *//***/ (function(module, __webpack_exports__, __webpack_require__) {"use strict";eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return func; });\nfunction func() {\n  return 1;\n}\n\n//# sourceURL=webpack:///./src/test.js?");/***/ })}]);

1. 模块加载

webpack通过__webpack_require__加载模块代码

// bundle.jsfunction __webpack_require__(moduleId)        // 如果模块已经加载,直接返回模块导出        if(installedModules[moduleId]) {                return installedModules[moduleId].exports;        }        // 模块导出和模块信息        var module = installedModules[moduleId] = {                i: moduleId,                l: false,                exports: {}        }        // 执行模块代码        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)        module.l = true // 标记模块已经加载完成        return module.exports;}
__webpack_require__('index.js')

然后执行index.js编译后的代码,如下。

Promise.all(  [    __webpack_require__.e(0)  ]).then(  __webpack_require__.bind(null, "./src/test.js")).then(function (fn) {  console.log(fn.default());}));

2. jsonp动态加载script

先一步步来,看下__webpack_require__.e这个方法,它是最先调用的。

// bundle.js__webpack_require__.e = function requireEnsure(chunkId) {        var promises = []        var installedChunkData = installedChunks[chunkId];        // 如果这个chunk已经加载过了 就不需要加载了        if(installedChunkData !== 0) { // 0 means "already installed"                if(installedChunkData) {                        promises.push(installedChunkData[2]);                } else {                        // 为这个chunk创建一个promise                        var promise = new Promise(function(resolve, reject) {                               // 记录这个chunk对应promise的resolve和reject方法                                installedChunkData = installedChunks[chunkId] = [resolve, reject];                        });                         // promises数组里添加这个chunk对应的promise                        promises.push(installedChunkData[2] = promise)                        // ============== 动态创建script =================                        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);};

总结一下,上述代码做的事情

  • 如果chunk没有被加载过,会为这个chunk创建一个promise对象

  • 将promise对象存在promises数组中

  • 将promise的resolve 和 reject存在installedChunks[chunkId]

3. 执行异步脚本

经过上面的过程,会动态加载0.js的脚本代码

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0],{"./src/test.js":(function(module, __webpack_exports__, __webpack_require__) {"use strict";eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return func; });\nfunction func() {\n  return 1;\n}\n\n//# sourceURL=webpack:///./src/test.js?");/***/ })}]);

可以看到window上有一个webpackJsonp数组,那么这个东西是从哪里来的呢?,我们来看下面的代码。

// bundle.jsvar 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;

其实一开始初始化时已经覆盖实现了webpackJsonp.push方法

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0],{  // test.js引入的模块代码})// 等价于webpackJsonpCallback([[0],{  // test.js引入的模块代码})

下面再看看webpackJsonpCallback代码的实现

4. webpackJsonpCallback

// bundle.jsfunction webpackJsonpCallback(data) {        // chunkid        var chunkIds = data[0];       // chunkid对应的模块        var moreModules = data[1]        var moduleId, chunkId, i = 0, resolves = [];        for(;i < chunkIds.length; i++) {                chunkId = chunkIds[i];                if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {                        // 收集chunk对应的resolve方法                        resolves.push(installedChunks[chunkId][0]);                }                // 标记该chunk已经加载                installedChunks[chunkId] = 0;        }        for(moduleId in moreModules) {                if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {                         // 添加chunk模块,到全局modules对象中                        modules[moduleId] = moreModules[moduleId];                }        }        if(parentJsonpFunction) parentJsonpFunction(data)         // 依次执行chunk对应promise的resolve方法        while(resolves.length) {                resolves.shift()();        }};

还是总结一下上面代码的过程

  • 收集chunk对应的resolve方法, 前面执行__webpack_require__.e时放在了installedChunks[chunkId]

  • 将异步chunk下的所有模块 添加到 全局modules

  • 依次执行chunk对应promise的resolve方法

5. 执行异步模块代码

回到一开始index.js的代码

Promise.all(  [    __webpack_require__.e(0)  ]).then(  __webpack_require__.bind(null, "./src/test.js")).then(function (fn) {  console.log(fn.default());}));

经过上面的步骤,此时promise已经resolve了,__webpack_require__.bind(null, "./src/test.js") 会被执行, 此时异步模块的代码已经在modules上了,所以可以直接加载。

最后,执行fn方法

console.log(fn.default());

流程图

以上就是"webpack动态import原理是什么"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

0