首页 > 其他分享 >分析webpack编译结果, 实现__webpack_require__函数

分析webpack编译结果, 实现__webpack_require__函数

时间:2024-06-04 23:33:21浏览次数:10  
标签:__ exports require module js webpack

分析webpack编译结果, 实现__webpack_require__函数

本篇文章我们通过手写来分析一下Webpack打包后的代码, 并研究一下Webpack是如何将多个模块合并在一起的

首先控制台输入npm init -y初始化一个项目, 再输入npm i webpack webpack-cli -D安装Webpack

在src目录想创建入口文件index.js, index.js导入了一个文件a.js并打印

我使用的Webpack版本如下:

  • webpack: 5.89.0
  • webpack-cli: 5.1.4
// src/index.js

console.log('index module')
var a = require("./a")
console.log(a)

src目录下创建一个a.js文件, a.js文件中导出一个对象

// src/a.js

console.log("a module")
module.exports = {
  a: "a",
  b: "b"
}

首先我们定义一个对象__webpack_modules__, 这个对象中保存着所有模块, 其中key是模块id, 一般是文件的路径作为key, value则是一个函数, 对应着模块的代码; 模块中运用到的module、exports、require作为函数的参数传入, require为了和node中的require做区别, 因此命名为__webpack_require__

// 该对象中保存着所有模块 以及模块对应代码
var __webpack_modules__ = {
  "./src/a.js": function (module, exports) {
    console.log("a module")
    module.exports = {
      a: "a",
      b: "b"
    }
  },
  "./src/index.js": function (module, exports, __webpack_require__) {
    console.log("index module")
    var a = __webpack_require__("./src/a.js")
    console.log(a)
}

为了避免上面代码造成全局变量污染, 我们将它作为立即执行函数的参数传入

(function (__webpack_modules__) {})({
  // 该对象中保存着所有模块 以及模块对应代码
  "./src/a.js": function (module, exports) {
    console.log("a module")
    module.exports = {
      a: "a",
      b: "b"
    }
  },
  "./src/index.js": function (module, exports, __webpack_require__) {
    console.log("index module")
    var a = __webpack_require__("./src/a.js")
    console.log(a)
  }
})

接下来实现__webpack_require__函数, 该函数接收一个moduleId(__webpack_modules__中的key)作为参数, 并运行一个模块, 得到该模块的导出结果

实现思路, 定义一个module对象, 用于存放导出结果, module对象中有一个exports属性, 该属性也对应一个对象; 通过moduleId我们可以得到该模块的函数, 执行这个函数就可以得到这个模块的导出结果; 在立即执行函数中调用__webpack_require__执行入口文件

(function (__webpack_modules__) {
  function __webpack_require__(moduleId) {
    var module = {
      exports: {}
    }
    var func = __webpack_modules__[moduleId] // 得到对应模块
    func(module, module.exports, __webpack_require__) // 执行对应的模块代码
    return module.exports // 返回导出结果
  }

  // 执行入口文件
  __webpack_require__("./src/index.js")
})({
  // 该对象中保存着所有模块 以及模块对应代码
  "./src/a.js": function (module, exports) {
    console.log("a module")
    module.exports = {
      a: "a",
      b: "b"
    }
  },
  "./src/index.js": function (module, exports, __webpack_require__) {
    console.log("index module")
    var a = __webpack_require__("./src/a.js")
    console.log(a)
  }
})

这样我们就是实现了Webpack合并多个模块, 但是当我们读取某一模块的时候, 我们应将它的导出结果缓存起来, 当下一次再导入该模块时, 就不需要再执行这个模块的代码, 直接从缓存中读取导出结果即可, 所以我们需要实现缓存完善上面代码

实现思路: 立即执行函数中定义一个__webpack_module_cache__对象用于缓存, 在__webpack_require__函数中, 优先从缓存中读取, 若缓存中有值, 那么直接返回缓存中的结果, 若没有值在执行对应模块的代码, 并将导出结果缓存, 再对之前的代码进行简单优化, 示例代码如下:

(function (__webpack_modules__) {
  var __webpack_module_cache__ = {} // 用于缓存

  function __webpack_require__(moduleId) {
    // 从缓存中读取
    var cacheModule = __webpack_module_cache__[moduleId]
    // 缓存中有值 直接返回缓存的结果
    if (cacheModule !== undefined) return cacheModule

    // 定义module存放导出结果 并赋值给缓存对象
    var module = __webpack_module_cache__[moduleId] = {
      exports: {}
    }
    // 执行对应的模块代码
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__)
    // 返回导出结果
    return module.exports
  }

  // 执行入口函数
  __webpack_require__("./src/index.js")
  __webpack_require__("./src/index.js")
  __webpack_require__("./src/index.js")
  __webpack_require__("./src/index.js")
})({
  // 该对象中保存着所有模块 以及模块对应代码
  "./src/a.js": function (module, exports) {
    console.log("a module")
    module.exports = {
      a: "a",
      b: "b"
    }
  },
  "./src/index.js": function (module, exports, __webpack_require__) {
    console.log("index module")
    var a = __webpack_require__("./src/a.js")
    console.log(a)
  }
})

此时我们控制输入npx webpack --mode=development使用开发环境对代码进行打包, 将多余的注释删除后, 得到的代码如下:

(() => {
  var __webpack_modules__ = ({
    "./src/a.js":
      ((module) => {
        eval("console.log(\"a module\")\n\nmodule.exports = {\n  a: \"a\",\n  b: \"b\"\n}\n\n\n//# sourceURL=webpack://web/./src/a.js?");
      }),

    "./src/index.js":
      ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
        eval("console.log('index module')\nconst a = __webpack_require__(/*! ./a */ \"./src/a.js\")\nconsole.log(a)\n\n//# sourceURL=webpack://web/./src/index.js?");
      })
  });
  var __webpack_module_cache__ = {};


  function __webpack_require__(moduleId) {
    var cachedModule = __webpack_module_cache__[moduleId];
    if (cachedModule !== undefined) {
      return cachedModule.exports;
    }
    var module = __webpack_module_cache__[moduleId] = {
      exports: {}
    };

    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);

    return module.exports;
  }

  var __webpack_exports__ = __webpack_require__("./src/index.js");
})();

可以看到和我们所实现的有一些小区别, 其中Webpack的__webpack_modules__是定义在立即执行函数中的, 我们是作为立即执行函数的参数传入, 其实没有什么区别;

另外__webpack_modules__中的模块函数是使用eval执行的, 这是并且会加上一个注释, 其实这是为了开发者能够在浏览器控制台调试, 由于我们运行的是打包后的代码, 那么报错指向的错误地址就会指向打包后的代码, 是不利于我们调式的; 而使用eval配合//# sourceURL=webpack://web/./src/a.js?可以看做是一个简易版的source map, 仅仅是一种方便调试的手段而已

其他的就没有什么区别了, 生产环境无非就是对这些变量名进行了压缩丑化, 也是可以看懂的, 到这里我们已经分析了Webpack的编译结果, 并实现了Webpack合并模块的核心代码

标签:__,exports,require,module,js,webpack
From: https://blog.csdn.net/m0_71485750/article/details/139456387

相关文章

  • 数据治理--数据服务
                             ......
  • RGB/YUV转HDMI,PIN对PIN替换IT66121
    基本概述:一款HDMI发送芯片。输入输出最高分辨率支持4K@30Hz,最高采样率达到300MHz。支持YUV和RGB之间的色彩空间转换,数字接口支持YUV以及RGB格式输入。ⅡS接口以及S/PDIF接口支持高清音频的传输,其中S/PDIF接口既可以兼容IEC61937标准下的压缩音频传输,同时还......
  • FreeRTOS基础(十):FreeRTOS任务状态查询API函数介绍
         本篇博客较为基础,介绍时间片调度和常用的任务状态查询API函数接口使用。目录一、时间片调度简介二、任务状态查询API函数介绍2.1常用API函数总览2.2常用API函数介绍2.2.1获取指定任务优先级2.2.2 改变某个任务的任务优先级2.2.3 获取系统中任务的任......
  • 基于双向长短时记忆神经网络结合多头注意力机制BiLSTM-Mutilhead-Attention实现柴油机
    %加载数据集和标签load(‘diesel_dataset.mat’);%假设数据集存储在diesel_dataset.mat文件中data=diesel_dataset.data;labels=diesel_dataset.labels;%数据预处理%这里假设你已经完成了数据的预处理,包括特征提取、归一化等步骤%划分训练集和测试集[tra......
  • (PDF)图解深度学习 (山下隆义)关机按技术分享电子版
    书:pan.baidu.com/s/1k0aEyPyTpEtwu-5HBOW0SA提取码:axib笔记内容:深度学习基础: 介绍深度学习的基本概念和背景,包括神经网络的基础知识。神经网络的工作原理: 图解了神经网络的基本结构和工作原理,包括神经元、激活函数等。深度学习的历史: 探讨了深度学习的发展历程,介绍了一些......
  • (大全集)大规模数据处理入门与实战(套装全10册 Kafka权威指南 Flink基础教程 数据科学
    书:pan.baidu.com/s/1YNu61Jk91VeISAX2F7-64g提取码:14pd是一本涉及大规模数据处理的入门级别的书籍,它通常旨在向读者介绍大规模数据处理的基本概念、技术、工具和实际应用。一些笔记:大数据概述: 介绍大数据的定义、特征和发展趋势。分布式系统: 讨论大规模数据处理的基础,包括......
  • bug记录——报了一堆xtr1common和yvals_core.h的错误
    现象        今天使用VisualStdio2022时,突然出现了这样严重的报错,看得我一头雾水,而且无法启动VisualStdio2022的调试。原因    发现NULL没法直接使用时,跟着提示添加了如下的头文件。        #include<cstddef>,是C++标准库的头文件,在C的文件中使......
  • upload-labs第二关教程
    upload-labs第二关教程第一关的教程可以去主页里面去找。文章目录upload-labs第二关教程1.源代码审计2.MIME类型是什么?3.绕过方法分析1)修改content-typea.上传一个eval.php文件b.然后使用Burpsuite进行抓包修改content-typec.放包:d.打开开发者工具e.使用中国蚁剑进......
  • C语言Kruskal算法求最小生成树
    Kruskal算法求出最小生成树。图形算法描述先找最小权值边为1的边有(V1,V4),(V2,V9),保证不产生回路就可以成功选择边除去上一次找的边后,在找权值最小的边为2的有(V2,V3),(V4,V3),(V5,V6),(V9,V8),连接不产生回路的边除去之前找过的边,后面再看权值最小的边为3的边有(V1,V3),(V7,V8),(V9,V7)按顺......
  • 【免杀】-哥斯拉魔改(入门)
    目录魔改哥斯拉-JAR反编译打包构建1、反编译Jar2、新建项目,lib文件夹,添加源码3、配置模块工件4、简单测试一下关闭哈希效验魔改哥斯拉-防识别-打乱特征指纹抓包获取流量特征哥斯拉的三个强特征修改强特征魔改哥斯拉-防查杀-新增后门插件1、获取shell生成逻辑2、......