Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack 按需加载的模块怎么在浏览器中运行? #89

Open
dcharlie123 opened this issue Mar 9, 2023 · 0 comments
Open

webpack 按需加载的模块怎么在浏览器中运行? #89

dcharlie123 opened this issue Mar 9, 2023 · 0 comments

Comments

@dcharlie123
Copy link
Owner

dcharlie123 commented Mar 9, 2023

// index.js
import("./hello").then((result) => {
    console.log(result.default);
});

// hello.js
export default 'hello';

产出代码:

// main.js
var modules = {};
var cache = {};
function require(moduleId) {
  var cachedModule = cache[moduleId];
  if (cachedModule !== undefined) {
    return cachedModule.exports;
  }
  // 缓存和创建模块对象
  var module = (cache[moduleId] = {
    exports: {},
  });
  // 运行模块代码
  modules[moduleId](module, module.exports, require, moduleId);
  return module.exports;
}
function webpackJsonpCallback([chunkIds, moreModules]) {
  const result = [];
  for (let i = 0; i < chunkIds.length; i++) {
    const chunkId = chunkIds[i];
    result.push(installedChunks[chunkId][0]);//resolve
    installedChunks[chunkId] = 0; // 表示此代码块已经下载完毕
  }

  // 将代码块合并到 modules 对象中去
  for (const moduleId in moreModules) {
    modules[moduleId] = moreModules[moduleId];
  }
  //依次将require.e方法中的promise变为成功态
  while (result.length) {
    result.shift()();
  }
}
var installedChunks = {
  main: 0,
};
require.d = (exports, definition) => {
  for (var key in definition) {
    Object.defineProperty(exports, key, {
      enumerable: true,
      get: definition[key],
    });
  }
};
require.r = (exports) => {
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
  Object.defineProperty(exports, "__esModule", { value: true });
};
require.m = modules;
require.f = {};
require.l = function (url) {
  let script = document.createElement("script");
  script.src = url;
  document.head.appendChild(script);
};
require.f.j = function (chunkId, promises) {
  let installedChunkData;
  // 当前代码块的数据
  const promise = new Promise((resolve, reject) => {
    installedChunkData = installedChunks[chunkId] = [resolve, reject];
  });
  promises.push((installedChunkData[2] = promise));
  // 获取模块的访问路径
  const url = chunkId + ".main.js";

  require.l(url);
};
require.e = function (chunkId) {  
  let promises = [];
  require.f.j(chunkId, promises);
  console.log(promises);
  return Promise.all(promises);
};
var chunkLoadingGlobal = (window["webpack"] = []);
chunkLoadingGlobal.push = webpackJsonpCallback;
require
  .e("hello")
  .then(require.bind(require, "./src/hello.js"))
  .then((result) => console.log(result));
// hello.main.js
"use strict";
(self["webpack"] = self["webpack"] || []).push([
  ["hello"], {
    "./src/hello.js": ((module, exports, require) => {
      require.r(exports);
      require.d(exports, {
        "default": () => (_DEFAULT_EXPORT__)
      });
      const _DEFAULT_EXPORT__ = ("hello");
    })
  }
]);

原理:以 JSONP 的方式加载按需的脚本
从最后几行代码开始看:

var chunkLoadingGlobal = (window["webpack"] = []);
chunkLoadingGlobal.push = webpackJsonpCallback;
require
  .e("hello")
  .then(require.bind(require, "./src/hello.js"))
  .then((result) => console.log(result));
  • 申明一个全局的webpack数组并重写了push方法:
var chunkLoadingGlobal = (window["webpack"] = []);
chunkLoadingGlobal.push = webpackJsonpCallback;
  • push方法指向webpackJsonpCallback,当运行require.e("hello")时,函数内部申明一个promises数组,调用 require.f.j, require.f.j中往promises推入一个promise,其内容是installedChunkData=installedChunks[chunkId]=[resolve,reject]installedChunks[chunkId]第一个参数是resolve,第二个reject,第三个是promise本身(这里的chunkId在上面的代码即'hello'
  • 然后拼接路径,调用require.lrequire.l方法用动态创建script标签的方式引入hello.mian.js
 // 获取模块的访问路径
  const url = chunkId + ".main.js";
  require.l(url);
  require.l = function (url) {
    let script = document.createElement("script");
    script.src = url;
    document.head.appendChild(script);
  };
  • hello.main.js是自执行函数,使用webpak.push,因为上面重写了这个方法,所以其实调用了webpackJsonpCallback
  • 执行webpackJsonpCallback会依次将require.e方法中的promise变为成功态
function webpackJsonpCallback([chunkIds, moreModules]) {
  const result = [];
  for (let i = 0; i < chunkIds.length; i++) {
    const chunkId = chunkIds[i];
    result.push(installedChunks[chunkId][0]);//resolve
    installedChunks[chunkId] = 0; // 表示此代码块已经下载完毕
  }

  // 将代码块合并到 modules 对象中去
  for (const moduleId in moreModules) {
    modules[moduleId] = moreModules[moduleId];
  }
  //依次将require.e方法中的promise变为成功态
  while (result.length) {
    result.shift()();
  }
}
  • 然后就是跟同步一样的require逻辑了
.then(require.bind(require, "./src/hello.js"))
.then((result) => console.log(result));

代码来源:https://mp.weixin.qq.com/s/GYsKB1m-M-QByIj4qfZAsQ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant