Webpack核心原理

Webpack

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具

webpack 的作用是将源代码编译(构建、打包)成最终代码

简单理解,就是把一堆玩具零件组装成一个完整的玩具。 Webpack又是如何把这一堆零件组成一个完整的东西呢?

一、Webpack打包内容分析

Webpack会帮助我们把一堆js文件进行打包,那打包的结果是什么呢?它与我们写的js文件有什么不同?

  • Webpack打包的结果肯定是js文件,这是不容置疑的。

Webpack打包后的js文件与我们写的js文件有什么不同

我们写的js文件如下

js 复制代码
/* index.js */
const a = require('./a.js');
const b = require('./b.js');
console.log(a+b)
    
/* a.js */
module.exports = 20;

/* b.js */
module.exports = 7;

执行webpack命令后,打包结果如下:(简化了Webpack的打包结果)

js 复制代码
(function (modules) {
  var installedModules = {};
  function require(moduleId) {
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, require);
    return module.exports;
  }
  return require("./src/index.js");
})
  ({
    "./src/a.js":
      (function (module, exports) {
        module.exports = 20;
      }),
    "./src/b.js":
      (function (module, exports) {
        module.exports = 7;
      }),
    "./src/index.js":
      (function (module, exports, require) {
        const a = require('./src/a.js');
        const b = require('./src/b.js');
        console.log(a + b)
      })
  });

我们发现webpack打包后的结果本质是一个立即执行函数 ,该函数接收一个对象作为实参,使用modules形参接收。

下面我们将逐步解析这个立即执行函数做了什么

1.先看modules形参里有什么?

js 复制代码
{ 
    "./src/a.js": (function (module, exports) { 
        module.exports = 20; 
        }), 
    "./src/b.js": (function (module, exports) {
        module.exports = 7;
        }),
    "./src/index.js": (function (module, exports, require) { 
        const a = require('./src/a.js'); 
        const b = require('./src/b.js'); 
        console.log(a + b) 
        }) 
}

这是一个对象,该对象的键为我们写的文件路径,值为一个函数,该函数内容为我们写的js代码(在Webpack中,我们写的js代码为做为字符串放在eval()函数中执行,这里方便理解,放在函数体中)

2.然后我们看这一段代码,它把require函数传入一个"./src/index.js"实参调用后的结果作为返回值

js 复制代码
15 return require("./src/index.js"); 

3.再看require函数里面做了什么事

webpack打包结果中,require函数为__webpack__require 在这里为了简写

js 复制代码
    // 7-11行 
    var module = installedModules[moduleId] = {
      exports: {}
    };
    // modules[moduleId]: 获取到对应模块的函数,首先是获取到的"./src/index.js"对应的函数
    // 然后使用call来改变this指向,并且传入三个参数
    modules[moduleId].call(module.exports, module, module.exports, require);
    return module.exports;
    

以上代码就是把该模块的this指向module.exports这个对象,modulemodule.exports作为模块的导出,把require这个函数作为模块的导入,最后把这个模块的导出结果作为返回值

  1. 如果我们导入了多个相同的模块 ,该模块的代码是不是要执行多次 ?所以使用了installedModules这个变量来保存模块的导出结果。
js 复制代码
    // 2行
    var installedModules = {};
    ...
    // 4-6行
    // 判断该模块是否缓存了导出结果
    if (installedModules[moduleId]) {
        return installedModules[moduleId].exports;
    }
    // 7-11行 
    // 定义该模块的导出
    var module = installedModules[moduleId] = {
      exports: {}
    };
    

以上代码就完成了对模块的导出内容的缓存。

  • 当没有缓存时,就执行该模块代码,然后缓存。
  • 当有缓存时,直接取出缓存并返回,无需执行该模块代码。

总结: Webpack打包后的本质就是一个立即执行函数,把我们需要的模块转换为key:value的键值对保存在一个对象里,然后作为参数传入到立即执行函数。Webpack写了一个require函数和module对象用来模拟CommonJS模块化完成功能的实现。

二、Webpack是如何工作的?

目录

  1. 初始化
  2. 编译
  3. 打包

1.初始化

初始化就是把命令行参数和webpack.config.js文件内容的配置进行合并,命令行参数会覆盖配置文件参数。

2.编译

Webpack在编译的过程中其实就做了一件事,就是把依赖收集,形成一个moudles参数

  • 依赖收集流程图

Webpack会根据一个入口文件index.js进行依赖收集,首先,根据文件ID(文件ID就是文件路径)检查这个模块是否被收集,如果被收集就不做任何处理,没有被收集就读取文件内容,进行抽象语法树分析,检查该模块依赖了哪些其他模块,把这些依赖模块存放到数组里,然后把require替换为__webpack__require,再把这个模块就添加到chunk里面。最后把依赖模块数组的每一项传入到入口文件中进行递归调用。

  • modules(chunk)

把编译好的模块存在在这里面

3.打包

打包阶段其实就很简单了,先生成一个js文件,文件内容为一个立即执行函数,然后把chunk生成一个对象,键为文件ID,值为一个函数,函数体里包裹着该模块代码,最后chunk作为参数传入到立即执行函数里面,这个立即执行函数里面提供了一个变量和一个函数,变量用来存储每个模块的导出结果,函数用来模拟require导入函数。

总结

在Webpack中,每个阶段还有很多事要做,比如编译阶段会对每个模块生成一个hash,然后对总的chunk也生成一个hash,我只是大致描述了一下Webpack的核心

三、关于loader和plugin

复制代码
可能有人会问Webpack中最强大的loader和plugin在这里为什么没有提?
loader和plugin这些东西并不是Webpack的核心,它们只是一些工具和插件,没有它们Webpack照样能够运行,有了它们会增强Webpack某一方面的功能。
比如六味地黄丸,只是对某一方面增强,并不会影响本身。

完结

我是一个小菜,希望我们大家能够友好沟通,一起完善对Webpack知识点的掌握。

也希望大佬们能够指出该文章的缺点和错误,还有您宝贵的意见,希望我们能够一起进步!

谢谢大家!

相关推荐
GISer_Jing21 小时前
前端构建工具(Webpack\Vite\esbuild\Rspack)拆包能力深度解析
前端·webpack·node.js
crary,记忆1 天前
MFE微前端基础版:Angular + Module Federation + webpack + 路由(Route way)完整示例
前端·学习·webpack·angular
古夕2 天前
webpack 之 Tree-shaking
前端·面试·webpack
GISer_Jing2 天前
前端构建工具Webapck、Vite——>前沿字节开源Rspack详解——2023D2大会
前端·javascript·webpack
古夕3 天前
Webpack 之 打包后的 bundle 文件内容解析
前端·面试·webpack
古夕3 天前
webpack 之 Loader 和 Plugin 接收参数对比
前端·面试·webpack
sg_knight3 天前
Rollup vs Webpack 深度对比:前端构建工具终极指南
前端·javascript·webpack·node.js·vue·rollup·vite
NoneCoder3 天前
Webpack 剖析与策略
前端·面试·webpack
a别念m3 天前
webpack基础与进阶
前端·webpack·node.js
贩卖纯净水.4 天前
webpack其余配置
前端·webpack·node.js