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知识点的掌握。

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

谢谢大家!

相关推荐
熊的猫10 小时前
DOM 规范 — MutationObserver 接口
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
Martin -Tang14 小时前
vite和webpack的区别
前端·webpack·node.js·vite
王解15 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
熊的猫1 天前
ES6 中 Map 和 Set
前端·javascript·vue.js·chrome·webpack·node.js·es6
前端青山1 天前
webpack指南
开发语言·前端·javascript·webpack·前端框架
理想不理想v1 天前
执行npm run build -- --report后,生产report.html文件是什么?
java·前端·javascript·vue.js·webpack·node.js
王解3 天前
【Webpack配置全解析】打造你的专属构建流程️(4)
前端·webpack·node.js
几何心凉4 天前
Webpack 中无法解析别名路径的原因及解决方案
运维·前端·webpack
friend_ship6 天前
Vue Cli的配置中configureWebpack和chainWebpack的主要作用及区别是什么?
vue.js·webpack·chainwebpack
friend_ship6 天前
Vite与Vue Cli的区别与详解
vue.js·webpack·rollup·vite·vue脚手架·vue cli