Webpack源码探索

278 阅读4分钟
  • Webpack启动流程

    • 入口执行文件
      平常我们配置webpack指令的时候都会在 package.json如下图: image.png 相当于执行 npx webpack命令,其根本就是找到 node_module文件夹下面的 .bin文件夹中的 webpack.js文件并且执行,所以 webpack打包入口执行文件其实就是/node_module/.bin/webpack.js
    • webpack.js源码探索-入口函数
      image.png
    • cli对象
      image.png 这里是通过 isInstalled函数来确定是否安装 webpack-cli再看 isInstalled函数 image.png
    • 如果没有安装 webpack-cli的源码执行情况
      image.png image.png image.png
    • runCli函数
      image.png image.png
    • webpack-cli中的入口文件源码探索
      image.png image.png
    • WebpackCLI对象源码探索
      image.png image.png
    • Webpack-cli作用
      Webpack-cli的作用其实就是将打包指令后的参数和webpack配置文件中的参数合并成一个配置文件然后调用webpack进行打包,所以像一些脚手架其实就没有安装 webpack-cli,他们都是自己将这些参数合并然后调用webpack
  • Webpack源码探索

    • Webpack源码下载以及准备工作
      • 第一步:下载源码
      • 第二步: 安装依赖,执行 yarn install或者 npm install
      • 第三步:写一些简单的代码(使用webpack源码打包使用)
        需要注意应为源码中有配置eslint,所以代码中使用到了 es module,所以这里需要改一下 eslint的一些配置如下图 image.png
      • 第四步:打包配置文件以及打包执行文件
        • 配置文件
          应为我们写的一个简单的代码中也就是几行js不涉及 css等所以配置文件只需要简单的写一个babel-loader就好具体如下:
          const path = require("path");
          
          module.exports = {
             mode: "development",
             devtool: "source-map",
             context: path.resolve(__dirname, "."),
             entry: "./src/main.js",
             output: {
                     filename: "bundle.js",
                     path: path.resolve(__dirname, "./build")
             },
             module: {
                     rules: [
                             {
                                     test: /\.js$/,
                                     use: "babel-loader"
                             }
                     ]
             },
             plugins: []
          };
          
        • 打包文件
          上文中说到 webpack-cli的源码我们就知道其实最终就是调用了 webpack函数进行打包操作 所以打包文件其实就是可以直接调用源码中的 webpack函数然后将配置作为参数传入即可具体代码如下:
          const webpack = require("../lib/webpack");
          const config = require("./webpack.config");
          
          const compiler = webpack(config);
          
          compiler.run((err, stats) => {
             if (err) {
                     console.error(err);
             } else {
                     console.log(stats);
             }
          });
          
          
    • 创建Compiler
      1. webpack函数源码 先看 webpack函数发现: image.png 发现该函数就是通过 createCompiler的一个函数创建 compiler并返回,所以 webpack打包的第一步就是创建Compiler
      2. createCompiler函数源码 image.png createCompiler方法的主要功能就是返回一个Compiler对象并且注入所有插件
        需要主要的是::webpack中很多参数都会被转换成插件被注入到整个打包的声明周期中具体可以查看 process源码如下图: image.png image.png
    • Compiler中run方法执行的Hook

      上文中说到了插件的执行会在 webpack打包的某个周期内执行,也说道了很多钩子,此时我们看一下Compiler对象的构造方法发现,在这里注册了一个 hooks对象也就是上文所说的钩子,然后再这个对象中定了了很多周期,插件的 apply方法中就会监听某一个周期然后开始执行对应的方法,如下图: image.png 而这些钩子的执行就是在run方法的特定时间点执行某一个钩子如下图run方法源码 image.png

    • Compiler和Compilation的区别
      • Compiler是贯穿整个编译的声明周期的,每次打包都只会创建这一次,Compilation主要用在 compile-make阶段,只有准备编译某个模块的时候才会被撞见比如准备编译 index.js模块
      • Compilation主要是和编译内容有关的Compiler是整个的打包编译,所以当你开启 watch的时候,当某个模块内容出现变化了,此时并不会重新创建Compiler,应为重新创建Compiler就意味着需要重新注册插件等操作,所以此时只需要重新创建Compilation就行,所以当我们修改了 webpack配置文件的时候一定要重新执行开启编译的命令

      image.png 通过源码我们也可以看出 Compilation创建的时间点,并且仅仅在compile-make使用,当编译的方法执行完成之后Compilation就会被销毁

    • build 入口查找

      build阶段主要是在 make这个钩子出发的时候开始的,在注册所有插件的时候(注册的时候实在createCompiler方法中上文有提到过),可以发现这里注册了 EntryOptionPlugin插件并且调用 apply方法 image.png 接下来再看 EntryOptionPlugin方法的 apply方法的源码 image.png这里监听了 entryOption钩子而 entryOption这个钩子是在EntryOptionPlugin注册并调用 apply方法之前就被调用的如下图: image.png 所以这里的监听会马上被执行接下来再看 applyEntryOption方法发现这里首先会判断 entry这个字段是否是个函数,不是函数的话在这里判断了多入口的情况如下图: image.png 这里我们只看单个入口的情况,也就是需要看一下 EntryPlugin插件的 apply方法 image.png 至此我们终于找到了对 make钩子的监听,发现里面调用的是 compilation这个类的 addEntry方法然后进一步跟踪 addEntry方法的源码 image.png image.png image.png image.png image.png 再看 buildQueue这个队列的初始化 image.png 有一个参数是 processor对应的是 _buildModule方法 image.png 至此也就找到了 build的入口