Webpack完整打包流程分析

本文详细分析了Webpack的完整打包过程,包括初始化阶段(合并配置、创建compiler、注册插件)、编译阶段(创建compilation、读取入口、编译模块)、生成阶段和写入阶段。通过源码解析,阐述了Webpack如何处理模块依赖、调用loader和插件,以及最终输出到磁盘的过程,旨在帮助前端开发者深入理解Webpack的内部工作机制。
摘要由CSDN通过智能技术生成

前言

webpack 在前端工程领域起到了中流砥柱的作用,理解它的内部实现机制会对你的工程建设提供很大的帮助(不论是定制功能还是优化打包)。

下面我们基于 webpack5 源码结构,对整个打包流程进行简单梳理并进行实现,便与思考和理解每个阶段所做的事情,为今后扩展和定制工程化能力打下基础。

一、准备工作

在流程分析过程中我们会简单实现 webpack 的一些功能,部分功能的实现会借助第三方工具:

  • tapable 提供 Hooks 机制来接入插件进行工作;
  • babel 相关依赖可用于将源代码解析为 AST,进行模块依赖收集和代码改写。
// 创建仓库
mkdir webpack-demo && cd webpack-demo && npm init -y

// 安装 babel 相关依赖
npm install @babel/parser @babel/traverse @babel/types @babel/generator -D

// 安装 tapable(注册/触发事件流)和 fs-extra 文件操作依赖
npm install tapable fs-extra -D 

接下来我们在 src 目录下新建两个入口文件和一个公共模块文件:

mkdir src && cd src && touch entry1.js && touch entry2.js && touch module.js 

并分别为文件添加一些内容:

// src/entry1.js
const module = require('./module');
const start = () => 'start';
start();
console.log('entry1 module: ', module);

// src/entry2.js
const module = require('./module');
const end = () => 'end';
end();
console.log('entry2 module: ', module);

// src/module.js
const name = 'cegz';
module.exports = {name,
}; 

有了打包入口,我们再来创建一个 webpack.config.js 配置文件做一些基础配置:

// ./webpack.config.js
const path = require('path');
const CustomWebpackPlugin = require('./plugins/custom-webpack-plugin.js');

module.exports = {entry: {entry1: path.resolve(__dirname, './src/entry1.js'),entry2: path.resolve(__dirname, './src/entry2.js'),},context: process.cwd(),output: {path: path.resolve(__dirname, './build'),filename: '[name].js',},plugins: [new CustomWebpackPlugin()],resolve: {extensions: ['.js', '.ts'],},module: {rules: [{test: /\.js/,use: [path.resolve(__dirname, './loaders/transformArrowFnLoader.js'), // 转换箭头函数],},],},
}; 

以上配置,指定了两个入口文件,以及一个 output.build 输出目录,同时还指定了一个 plugin 和一个 loader

接下来我们编写 webpack 的核心入口文件,来实现打包逻辑。这里我们创建 webpack 核心实现所需的文件:

// cd webpack-demo
mkdir lib && cd lib
touch webpack.js // webpack 入口文件
touch compiler.js // webpack 核心编译器
touch compilation.js // webpack 核心编译对象
touch utils.js // 工具函数 

这里我们创建了两个比较相似的文件:compilercompilation,在这里做下简要说明:

  • compiler:webpack 的编译器,它提供的 run 方法可用于创建 compilation 编译对象来处理代码构建工作;
  • compilation:由 compiler.run 创建生成,打包编译的工作都由它来完成,并将打包产物移交给 compiler 做输出写入操作。

对于入口文件 lib/webpack.js,你会看到大致如下结构:

// lib/webpack.js
function webpack(options) {...
}

module.exports = webpack; 

对于执行入口文件的测试用例,代码如下:

// 测试用例 webpack-demo/build.js
const webpack = require('./lib/webpack');
const config = require('./webpack.config');

const compiler = webpack(config);

// 调用run方法进行打包
compiler.run((err, stats) => {if (err) {console.log(err, 'err');}// console.log('构建完成!', stats.toJSON());
}); 

接下来,我们从 lib/webpack.js 入口文件,按照以下步骤开始分析打包流程。

1、初始化阶段 - webpack

  • 合并配置项
  • 创建 compiler
  • 注册插件

2、编译阶段 - build

  • 读取入口文件
  • 从入口文件开始进行编译
  • 调用 loader 对源代码进行转换
  • 借助 babel 解析为 AST 收集依赖模块
  • 递归对依赖模块进行编译操作

3、生成阶段 - seal

  • 创建 chunk 对象
  • 生成 assets 对象

4、写入阶段 - emit

二、初始化阶段

初始化阶段的逻辑集中在调用 webpack(config) 时候,下面我们来看看 webpack() 函数体内做了哪些事项。

2.1、读取与合并配置信息

通常,在我们的工程的根目录下,会有一个 webpack.config.js 作为 webpack 的配置来源;

除此之外,还有一种是通过 webpak bin cli 命令进行打包时,命令行上携带的参数也会作为 webpack 的配置。

在配置文件中包含了我们要让 webpack 打包处理的入口模块、输出位置、以及各种 loader、plugin 等;

在命令行上也同样可以指定相关的配置,且权重高于配置文件。(下面将模拟 webpack cli 参数合并处理)

所以,我们在 webpack 入口文件这里将先做一件事情:合并配置文件与命令行的配置。

// lib/webpack.js
function we
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值