Skip to content
Go back

Webpack's async import

Published:  at  11:42 AM

文章目录

Introduction

Webpack’s async import (dynamic import) enables code splitting and lazy loading. This article analyzes the transformed code to understand how webpack implements async module loading at runtime.

Bundled Runtime Code

Module System Core

// Module storage: maps module ID to module definition
var modules = {};

// Module cache: stores installed/loaded modules
var cache = {};

// Module loading function
function require(moduleId) {
  // Return cached module if already loaded
  if (cache[moduleId]) {
    return cache[moduleId];
  }

  // Create and cache new module
  var module = (cache[moduleId] = { exports: {} });

  // Execute module definition, populate exports
  modules[moduleId](module, module.exports, require);

  return module.exports;
}

// Attach module storage and cache to require function
require.m = modules; // Module definitions
require.c = cache; // Installed modules

Module Definition Helpers

// Mark module as ES module
require.r = exports => {
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
  Object.defineProperty(exports, "__esModule", { value: true });
};

// Define getters for exports
require.d = (exports, definition) => {
  for (var key in definition) {
    Object.defineProperty(exports, key, {
      enumerable: true,
      get: definition[key],
    });
  }
};

Chunk Loading Utilities

// Base URL for loading chunks
require.p = "";

// Generate chunk filename from chunk ID
require.u = chunkId => chunkId + ".main.js";

// Create and inject script tag to load chunk
require.l = url => {
  let script = document.createElement("script");
  script.src = url;
  document.head.appendChild(script);
};

Async Chunk Loading

// Track installed/downloading chunks
// Value 0 = installed, array [resolve, reject] = pending
var installedChunks = { main: 0 };

// JSONP callback for chunk loading
require.f.j = (chunkId, promises) => {
  var installedChunkData;

  // Create promise and store resolve/reject handlers
  var promise = new Promise((resolve, reject) => {
    installedChunkData = installedChunks[chunkId] = [resolve, reject];
  });

  // Store promise in promises array for Promise.all()
  promises.push((installedChunkData[2] = promise));

  // Load chunk script
  var url = require.p + require.u(chunkId);
  require.l(url);
};

// Load chunk and return promise
require.e = chunkId => {
  let promises = [];
  require.f.j(chunkId, promises);
  return Promise.all(promises);
};

Global Chunk Loading Callback

// Callback to handle loaded chunk data
var webpackJsonCallback = (chunkIds, moreModules) => {
  var resolves = [];

  // Resolve all pending promises for loaded chunks
  for (var i = 0; i < chunkIds.length; i++) {
    var chunkId = chunkIds[i];
    resolves.push(installedChunks[chunkId][0]); // Get resolve function
    installedChunks[chunkId] = 0; // Mark as installed
  }

  // Merge new modules into module storage
  Object.assign(modules, moreModules);
};

// Setup global array for JSONP chunk loading
var chunkLoadingGlobal = (window["webpackChunk_app_bundle"] = []);
chunkLoadingGlobal.push = webpackJsonCallback; // Override push to trigger callback

Entry Point Usage

// Async load a chunk after 10 seconds
window.setTimeout(() => {
  require.e("src_video_js").then(require.bind(null, './src/video.js')).then(() => {
    // Chunk loaded, module now available via require()
  });
}, 10000);

Lazy Chunk Module Definition

// Chunk module definition (loaded via JSONP)
window["webpackChunk_app_bundle"].push([
  ["src_video_js"],
  {
    "./src/video.js": (module, exports, require) => {
      require.r(exports);
      require.d(exports, {
        default: () => DEFAULT_EXPORT,
      });
      var DEFAULT_EXPORT = "video";
    },
  },
]);

Loading Flow Summary

  1. Trigger: require.e(chunkId) is called
  2. Promise Creation: Create promise with resolve/reject handlers, store in installedChunks
  3. Script Injection: Create <script> tag with chunk URL and append to DOM
  4. Chunk Execution: Browser loads and executes chunk script
  5. JSONP Callback: Chunk calls window["webpackChunk_app_bundle"].push(), triggering webpackJsonCallback
  6. Module Registration: New modules merged into modules storage
  7. Promise Resolution: Pending promises resolved, loading complete

Key Concepts

ConceptDescription
modulesAll module definitions keyed by module ID
cacheAlready loaded modules to avoid re-execution
installedChunksChunk loading state tracking
require.pPublic path for chunk URLs
require.lScript injection for dynamic loading
JSONP PatternChunk data pushed via global array callback


Previous Post
Writing Babel Plugins From Scratch
Next Post
Typescript类型体操