同步加载打包文件分析

安装模块

cnpm i webpack webpack-cli html-webpack-plugin clean-webpack-plugin -D

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "development",
  devtool: "source-map",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "main.js",
  },
  module: {},
  plugins: [
    new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ["**/*"] }),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      filename: "index.html",
    }),
  ],
  devServer: {},
};

src\index.js

let title = require("./title.js");
console.log(title);

src\title.js

module.exports = "title";

src\index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>webpack</title>
  </head>
  <body></body>
</html>

package.json

"scripts": {
  "build": "webpack"
}

打包文件

(() => {
  // 该入口文件引入的模块,会议键值对的形式放在 modules 里面
  // 不管你是用什么样的路径来加载的,最终模块 ID (key值)统一会变成相对根目录的相对路径
  //  - index.js ./src/index.js
  //  - title.js  ./src/title.js
  //  - jquery  ./node_modules/jquery/dist/jquery.js
  var modules = ({
    "./src/title.js":
      ((module, module_exports, require) => {
        // 将 title 放入 exports 中
        module.exports = "title";
      })
  });

  // 缓存模块 
  var cache = {};
  function require(moduleId) {
    // 如果存在缓存,直接从缓存中获取
    if (cache[moduleId]) {
      return cache[moduleId].exports;
    }

    // 没有缓存,则需要放入缓存中
    var module = cache[moduleId] = {
      exports: {} // 默认值为空,引入模块后会赋值到这里
    };
    // 加载模块,执行该模块代码
    modules[moduleId](module, module.exports, require);

    return module.exports;
  }

  // 入口文件加载后,执行以下代码
  (() => {
    let title = require("./src/title.js");
    console.log(title);
  })();
})();

异步加载打包文件分析

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "development",
  devtool: "source-map",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "main.js",
  },
  module: {},
  plugins: [
    new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ["**/*"] }),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      filename: "index.html",
    }),
  ],
  devServer: {},
};

src\index.js

import(/* webpackChunkName: "hello" */ "./hello").then((result) => {
  console.log(result.default);
});

hello.js

export default 'hello';

dist\main.js

(() => {
  // 存放着所有的模块定义,包括懒加载,或者说异步加载过来的模块定义
  var modules = ({});

  var cache = {};
  // 因为在 require 的时候,只会读取 modules 里面的模块定义
  function require(moduleId) {
    if (cache[moduleId]) {//先看缓存里有没有已经缓存的模块对象
      return cache[moduleId].exports;//如果有就直接返回
    }
    // module.exports默认值 就是一个空对象
    var module = { exports: {} };
    cache[moduleId] = module;
    // 会在模块的代码执行时候给module.exports赋值
    modules[moduleId].call(module.exports, module, module.exports, require);
    return module.exports;
  }

  require.f = {};
  // 如何异步加载额外的代码块 chunkId=hello
  // 2.创建 promise,发起 jsonp 请求
  require.e = (chunkId) => {
    let promises = [];
    require.f.j(chunkId, promises);
    return Promise.all(promises); // 等这个 promise 数组里的 promise 都成功之后
  }
  require.p = ''; // publicPath 资源访问路径
  require.u = (chunkId) => { // 参数是代码块的名字,返回值是这个代码的文件名
    return chunkId + '.main.js';
  }

  // 已经安装的代码块, main 代码块的名字:0 表示已经就绪
  let installedChunks = {
    main: 0,
    hello: 0
  }

  //3.通过 jsonp 异步加载 chunkId,也就是 hello 这个代码块
  require.f.j = (chunkId, promises) => {
    // 创建一个新的 promise 数组,放到了数组中去
    let promise = new Promise((resolve, reject) => {
      installedChunks[chunkId] = [resolve, reject];
    });
    promises.push(promise);
    var url = require.p + require.u(chunkId);// /hello.main.js
    require.l(url);
  }

  // http://127.0.0.1:8082/hello.main.js
  // 4.通过 JSONP 请求这个新的 url 地址
  require.l = (url) => {
    let script = document.createElement('script');
    script.src = url;
    document.head.appendChild(script);// 一旦添加 head 里,浏览器会立刻发出请求
  }

  //6.开始执行回调
  var webpackJsonpCallback = ([chunkIds, moreModules]) => {
    // chunkIds = ['hello']=>[resolve,reject]
    // let resolves = chunkIds.map(chunkId => installedChunks[chunkId][0]);
    let resolves = [];
    for (let i = 0; i < chunkIds.length; i++) {
      let chunkData = installedChunks[chunkIds[i]];
      installedChunks[chunkIds[i]] = 0;
      resolves.push(chunkData[0]);
    }

    // 把异步加载回来的额外的代码块合并到总的模块定义对象 modules 上去
    for (let moduleId in moreModules) {
      modules[moduleId] = moreModules[moduleId];
    }
    resolves.forEach(resolve => resolve());
  }
  
  /**
   * 将 definition 的属性都放到 exports 里面(用代理的方式实现)
   */
  require.d = (exports, definition) => {
    for (let key in definition) {
      Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
    }
  }
  /**
   * 新增 2 个属性 Symbol.toStringTag 和 __esModule,用于标识原来是 esModule 模块
   */
  require.r = (exports) => {
    Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); // Object.prototype.toString.call(exports) === [object Module]
    Object.defineProperty(exports, '__esModule', { value: true }); // exports.__esModule=true 
  }

  // 0.把空数组赋给了 window["webpack5"],然后重写的 window["webpack5"].push
  var chunkLoadingGlobal = window["webpack5"] = [];
  // 然后重写的 window["webpack5"].push = webpackJsonpCallback
  chunkLoadingGlobal.push = webpackJsonpCallback;

  // 异步加载 hello chunk 代码块,然后把 hello chunk 代码块里的模块定义合并到主模块定义里去
  // 再去加载这个 hello.js 这个模块,拿到模块的导出结果
  // 1.准备加载异步代码块 hello
  require.e("hello").then(require.bind(require, "./src/hello.js")).then(result => {
    console.log(result.default);
  })
})();

hello.main.js

// 5.执行 window["webpack5"] 上的 push 方法,传递参数 [chunkIds,moreModules]
(window["webpack5"] = window["webpack5"] || []).push([["hello"], {
    "./src/hello.js": ((module, exports, require) => {
        require.r(exports);
        require.d(exports, {
            "default": () => __WEBPACK_DEFAULT_EXPORT__
        });
        const __WEBPACK_DEFAULT_EXPORT__ = ('hello ');
    })
}]);

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于产品、设计、开发的问题和看法,还有技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程

了解更多

目录

  1. 1. 同步加载打包文件分析
    1. 1.1. 安装模块
    2. 1.2. webpack.config.js
    3. 1.3. src\index.js
    4. 1.4. src\title.js
    5. 1.5. src\index.html
    6. 1.6. package.json
    7. 1.7. 打包文件
  2. 2. 异步加载打包文件分析
    1. 2.1. webpack.config.js
    2. 2.2. src\index.js
    3. 2.3. hello.js
    4. 2.4. dist\main.js
    5. 2.5. hello.main.js