返回

彻底拆解Webpack源码:深入理解webpack的打包原理

前端

当我们使用webpack去打包,最终都会产出一个或者多个可被浏览器直接加载执行的JavaScript文件,而这个过程往往会经历模块的解析、转换、合并以及优化等一系列流程。

接下来,我们就以[email protected]版本为例,一探webpack源码中模块化打包是如何实现的。

1. 源码文件结构解析

webpack源码位于webpack目录下,总共包含537个文件和38个目录,其中最核心的文件是webpack.js,它是webpack的核心入口文件。

webpack/
├── bin/
│  ├── webpack.js
│  └── webpack-cli.js
├── config/
│  └── webpack.config.js
├── node_modules/
├── package.json
├── README.md
├── scripts/
├── test/
├── types/
├── version.js
└── webpack.js

2. Webpack构建流程概述

webpack的构建流程可以概括为以下几个步骤:

  1. 初始化编译器 :在这一步中,webpack会创建一个compiler对象,compiler对象负责整个webpack的构建过程。
  2. 解析入口文件 :webpack会从入口文件开始,解析其依赖的模块,并构建一个模块依赖图。
  3. 应用规则 :webpack会根据配置的规则,对模块应用相应的处理,如转换、压缩等。
  4. 优化代码 :webpack会对代码进行优化,如代码压缩、代码分割等。
  5. 生成输出文件 :webpack会将处理后的代码生成输出文件,如JavaScript文件、CSS文件等。

3. 深入剖析Webpack源码

3.1 初始化编译器

// webpack.js
const Compiler = require('./Compiler');
const webpackOptions = configureWebpack(options);
const compiler = new Compiler(webpackOptions.context);

webpack.js文件中,首先会创建一个Compiler对象,Compiler对象负责整个webpack的构建过程。

3.2 解析入口文件

// Compiler.js
this.hooks.compilation.tap('Compiler', compilation => {
  this.parentCompilation = compilation;
  this.compilation = compilation;

  this.applyPlugins('make', compilation);

  this.processAssets();
  this.processModules();
});

Compiler对象的make钩子中,webpack会调用processModules方法来解析入口文件。processModules方法会从入口文件开始,解析其依赖的模块,并构建一个模块依赖图。

3.3 应用规则

// Compiler.js
this.hooks.thisCompilation.tap('Compiler', compilation => {
  const handler = {
    get(obj, prop) {
      const { tap, callAsync } = compilation[prop];

      return (...args) => {
        return tap.call(compilation, ...args);
      };
    }
  };

  const plugins = this.plugins.slice();
  const modules = this.modules.slice();

  plugins.push(compilation.resolverFactory);
  modules.push(compilation.dependencyTemplates);
  modules.push(compilation.normalModuleFactory);
  modules.push(compilation.contextModuleFactory);

  const copiedCompilation = new Proxy(compilation, handler);
  compilation.dependencyFactories = modules;
  compilation.resolverFactory = plugins;
});

Compiler对象的thisCompilation钩子中,webpack会将webpack的插件和模块添加到compilation对象中。compilation对象负责整个webpack的编译过程。

3.4 优化代码

// Compiler.js
this.hooks.compilation.tap('Compiler', compilation => {
  compilation.hooks.afterOptimizeAssets.tapAsync('Compiler', (assets, callback) => {
    this.applyPluginsAsync('after-optimize-assets', assets, callback);
  });
});

Compiler对象的compilation钩子中,webpack会调用afterOptimizeAssets钩子来对代码进行优化。afterOptimizeAssets钩子会调用webpack的插件来对代码进行优化,如代码压缩、代码分割等。

3.5 生成输出文件

// Compiler.js
this.hooks.compilation.tap('Compiler', compilation => {
  compilation.hooks.afterEmit.tapAsync('Compiler', (compilation, callback) => {
    this.emitAssets(compilation, callback);
  });
});

Compiler对象的compilation钩子中,webpack会调用afterEmit钩子来生成输出文件。afterEmit钩子会调用webpack的插件来生成输出文件,如JavaScript文件、CSS文件等。

4. 总结

以上就是webpack模块化打包的核心流程,希望对您有所帮助。