1.调试 webpack #

1.1 通过 chrome 调试 #

node --inspect-brk ./node_modules/webpack-cli/bin/cli.js

然后打开 Chrome 浏览器控制台就可以调试了

1.2 通过执行命令调试 #

.vscode\launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "debug webpack",
      "cwd": "${workspaceFolder}",
      "program": "${workspaceFolder}/node_modules/webpack-cli/bin/cli.js"
    }
  ]
}

1.3 debugger.js #

const webpack = require("webpack");
const webpackOptions = require("./webpack.config");
const compiler = webpack(webpackOptions);
//4.执行对象的run方法开始执行编译
compiler.run((err, stats) => {
  console.log(err);
  console.log(
    stats.toJson({
      assets: true,
      chunks: true,
      modules: true,
    })
  );
});

2. tapable.js #

class SyncHook {
  constructor() {
    this.taps = [];
  }
  tap(name, fn) {
    this.taps.push(fn);
  }
  call() {
    this.taps.forEach((tap) => tap());
  }
}

let hook = new SyncHook();
hook.tap("some name", () => {
  console.log("some name");
});

class Plugin {
  apply() {
    hook.tap("Plugin", () => {
      console.log("Plugin ");
    });
  }
}
new Plugin().apply();
hook.call();

3. webpack 编译流程 #

  1. 初始化参数:从配置文件和 Shell 语句中读取并合并参数,得出最终的配置对象
  2. 用上一步得到的参数初始化 Compiler 对象
  3. 加载所有配置的插件
  4. 执行对象的 run 方法开始执行编译
  5. 根据配置中的entry找出入口文件
  6. 从入口文件出发,调用所有配置的Loader对模块进行编译
  7. 再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  8. 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk
  9. 再把每个 Chunk 转换成一个单独的文件加入到输出列表
  10. 在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果

2020webpackflow

3.1 debugger.js #

debugger.js

// 引入自定义的webpack模块
const webpack = require('./my-webpack');
// 引入Node.js的文件系统模块,用于操作文件
const fs = require('fs');
// 引入webpack配置文件
const config = require('./webpack.config');
// 使用配置文件创建一个webpack编译器实例
const compiler = webpack(config);
// 运行编译器,开始构建过程
compiler.run((err, stats) => {
  // 打印构建错误信息(如果有的话)
  console.log(err);
  // 将构建统计数据转换为JSON字符串,包括模块、代码块和资源信息
  let statsString = JSON.stringify(stats.toJson({
    modules: true,
    chunks: true,
    assets: true
  }));
  // 将统计数据字符串写入文件myStats.json,用于分析构建过程
  fs.writeFileSync('./myStats.json', statsString);
});

3.2 webpack.config.js #

webpack.config.js

const path = require("path");
const RunPlugin = require("./plugins/run-plugin");
const DonePlugin = require("./plugins/done-plugin");
module.exports = {
  mode: "development",
  devtool: false,
  entry: {
    entry1: "./src/entry1.js",
    entry2: "./src/entry2.js",
  },
  output: {
    path: path.resolve("dist"),
    filename: "[name].js",
  },
  resolve: {
    extensions: [".js", ".jsx", ".ts", ".tsx", ".json"],
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          path.resolve(__dirname, "loaders/logger1-loader.js"),
          path.resolve(__dirname, "loaders/logger2-loader.js"),
        ],
      },
    ],
  },
  plugins: [
    new RunPlugin(), 
    new DonePlugin(), 
  ],
};

3.3 my-webpack.js #

my-webpack.js

const Compiler = require('./Compiler');
function webpack(config) {
  const argv = process.argv.slice(2);
  const shellOptions = argv.reduce((shellOptions, options) => {
    const [key, value] = options.split('=');
    shellOptions[key.slice(2)] = value;
    return shellOptions;
  }, {});
  const finalOptions = { ...config,
    ...shellOptions
  };
  const compiler = new Compiler(finalOptions);
  finalOptions.plugins.forEach(plugin => {
    plugin.apply(compiler);
  });
  return compiler;
}
module.exports = webpack;

3.4 Compiler.js #

Compiler.js

// 引入tapable库中的SyncHook类,用于创建同步钩子
const {SyncHook} = require('tapable');
// 引入path模块,用于处理文件和目录路径
const path = require('path');
// 引入自定义的Complication模块
const Complication = require('./Complication');
// 引入Node.js的文件系统模块,用于操作文件
const fs = require('fs');
// 定义Compiler类
class Compiler {
    // 构造函数,接收一个options参数
    constructor(options) {
        // 保存options配置
        this.options = options;
        // 初始化钩子对象,包含run和done两个同步钩子
        this.hooks = {
            run: new SyncHook(),
            done: new SyncHook()
        };
    }
    // run方法,接收一个回调函数callback
    run(callback) {
        // 调用run钩子
        this.hooks.run.call();
        // 定义onCompiled回调函数,用于处理编译结果
        const onCompiled = (err, stats, fileDependencies) => {
            // 获取编译生成的资源
            const {assets} = stats;
            // 遍历资源,将资源写入输出目录
            for (let filename in assets) {
                let filePath = path.posix.join(this.options.output.path, filename);
                fs.writeFileSync(filePath, assets[filename], 'utf-8');
            }
            // 调用外部传入的回调函数
            callback(err, {toJson: () => stats});
            // 监听文件依赖的变化,重新编译
            [...fileDependencies].forEach(file => {
                fs.watch(file, () => this.compile(onCompiled));
            });
        };
        // 开始编译
        this.compile(onCompiled);
        // 调用done钩子
        this.hooks.done.call();
    }
    // 编译方法
    compile(onCompiled) {
        // 创建Complication实例
        const complication = new Complication(this.options);
        // 调用Complication的build方法开始构建
        complication.build(onCompiled);
    }
}
// 导出Compiler类
module.exports = Compiler;

3.5 Complication.js #

Complication.js

// 导入Node.js内置的path模块,用于处理文件路径
const path = require('path');
// 导入Node.js内置的fs模块,用于操作文件系统
const fs = require('fs');
// 导入babel-types库,用于处理AST节点
const types = require('babel-types');
// 导入@babel/parser库,用于将源代码解析成抽象语法树(AST)
const parser = require('@babel/parser');
// 导入@babel/traverse库,用于遍历和操作AST
const traverse = require('@babel/traverse').default;
// 导入@babel/generator库,用于将修改过的AST重新生成源代码
const generator = require('@babel/generator').default;
// 定义toUnixSeq函数,将Windows风格的文件路径转换为Unix风格
function toUnixSeq(filePath) {
    // 使用正则表达式替换所有的反斜杠(\)为正斜杠(/)
    return filePath.replace(/\\/g, '/');
}
// 定义Complication类,用于处理编译过程
class Complication {
    // 构造函数,接收一个options参数
    constructor(options) {
        // 将传入的options对象赋值给实例的options属性
        this.options = options;
        // 设置实例的context属性为传入的options.context值,如果未传入,则使用当前工作目录的Unix风格路径
        this.options.context = this.options.context || toUnixSeq(process.cwd());
        // 初始化一个Set,用于存储文件依赖,避免重复添加
        this.fileDependencies = new Set();
        // 初始化一个数组,用于存储模块信息
        this.modules = [];
        // 初始化一个数组,用于存储代码块信息
        this.chunks = [];
        // 初始化一个空对象,用于存储输出文件的资源信息
        this.assets = {};
    }
    build(onCompiled) {
        // 定义一个空对象,用于存储入口信息
        let entry = {};
        // 判断options.entry的类型
        if (typeof this.options.entry === 'string') {
            // 如果是字符串类型,将其作为默认入口文件,将main作为键名
            entry.main = this.options.entry;
        } else {
            // 否则直接使用options.entry作为入口信息
            entry = this.options.entry;
        }
        // 遍历entry对象,处理每个入口文件
        for (let entryName in entry) {
            // 获取入口文件的完整路径
            let entryFilePath = path.posix.join(this.options.context, entry[entryName]);
            // 添加入口文件路径到文件依赖集合
            this.fileDependencies.add(entryFilePath);
            // 调用buildModule方法构建入口模块
            let entryModule = this.buildModule(entryName, entryFilePath);
            // 创建一个chunk对象,包含名称、入口模块和与该入口关联的模块
            let chunk = {
                name: entryName,
                entryModule,
                modules: this.modules.filter(module => module.names.includes(entryName))
            };
            // 将chunk对象添加到chunks数组
            this.chunks.push(chunk);
        }
        // 遍历chunks数组,为每个chunk生成输出文件
        this.chunks.forEach(chunk => {
            // 替换输出文件名模板中的[name]为chunk名称
            let outputFilename = this.options.output.filename.replace('[name]', chunk.name);
            // 调用getSourceCode方法获取chunk的源码,将其添加到assets对象
            this.assets[outputFilename] = getSourceCode(chunk);
        });
        // 调用onCompiled回调函数,传入构建结果
        onCompiled(null, {
            modules: this.modules,
            chunks: this.chunks,
            assets: this.assets
        }, this.fileDependencies);
    }
    buildModule(entryName, modulePath) {
        // 读取模块文件的原始源代码
        let rawSourceCode = fs.readFileSync(modulePath, 'utf8');
        // 从options中获取模块规则
        let { rules } = this.options.module;
        // 定义一个空数组,用于存储模块加载器
        let loaders = [];
        // 遍历规则,根据匹配的规则将加载器添加到loaders数组中
        rules.forEach(rule => {
            if (modulePath.match(rule.test)) {
                loaders.push(...rule.use);
            }
        });
        // 使用reduceRight逐个应用加载器,将原始源代码转换为处理后的源代码
        let transformedSourceCode = loaders.reduceRight((sourceCode, loaderPath) => {
            const loaderFn = require(loaderPath);
            return loaderFn(sourceCode);
        }, rawSourceCode);
        // 生成模块ID(相对路径)
        let moduleId = './' + path.posix.relative(this.options.context, modulePath);
        // 创建模块对象
        let module = {
            id: moduleId,
            names: [entryName],
            dependencies: []
        };
        // 将模块对象添加到模块数组中
        this.modules.push(module);
        // 使用@babel/parser解析处理后的源代码,生成AST(抽象语法树)
        let ast = parser.parse(transformedSourceCode, {
            sourceType: "module"
        });
        // 使用@babel/traverse遍历AST,处理require调用
        traverse(ast, {
            CallExpression: ({ node }) => {
                if (node.callee.name === 'require') {
                    let depModuleName = node.arguments[0].value;
                    let dirName = path.posix.dirname(modulePath);
                    let depModulePath = path.posix.join(dirName, depModuleName);
                    let { extensions } = this.options.resolve;
                    depModulePath = tryExtensions(depModulePath, extensions);
                    this.fileDependencies.add(depModulePath);
                    let depModuleId = "./" + path.posix.relative(this.options.context, depModulePath);
                    node.arguments[0] = types.stringLiteral(depModuleId);
                    module.dependencies.push({
                        depModuleId,
                        depModulePath
                    });
                }
            }
        });
        // 使用@babel/generator将AST转换回源代码
        const { code } = generator(ast);
        // 将生成的源代码添加到模块对象中
        module._source = code;
        // 处理模块依赖
        module.dependencies.forEach(({ depModuleId, depModulePath }) => {
            let existModule = this.modules.find(item => item.id === depModuleId);
            if (existModule) {
                existModule.names.push(entryName);
            } else {
                this.buildModule(entryName, depModulePath);
            }
        });
        return module;
    }
}
// 定义tryExtensions函数,用于尝试不同的文件扩展名,找到对应的模块文件
function tryExtensions(modulePath, extensions) {
    // 如果原始路径的文件存在,则直接返回该路径
    if (fs.existsSync(modulePath)) {
        return modulePath;
    }
    // 遍历所有提供的扩展名
    for (let i = 0; i < extensions.length; i++) {
        // 生成一个新的文件路径,将扩展名添加到原始路径后
        let filePath = modulePath + extensions[i];
        // 如果新路径的文件存在,则返回该路径
        if (fs.existsSync(filePath)) {
            return filePath;
        }
    }
    // 如果尝试所有扩展名后都没有找到对应的文件,则抛出错误
    throw new Error(`模块${modulePath}未找到`);
}
function getSourceCode(chunk) {
    return `
  (() => {
    var modules = {
      ${chunk.modules.filter(module => module.id !== chunk.entryModule.id).map(module => `
            "${module.id}": module => {
               ${module._source}
              }
            `)}  
    };
    var cache = {};
    function require(moduleId) {
      var cachedModule = cache[moduleId];
      if (cachedModule !== undefined) {
        return cachedModule.exports;
      }
      var module = cache[moduleId] = {
        exports: {}
      };
      modules[moduleId](module, module.exports, require);
      return module.exports;
    }
    var exports = {};
    (() => {
      ${chunk.entryModule._source}
    })();
  })();
  `;
}
module.exports = Complication;

3.6 RunPlugin.js #

plugins\RunPlugin.js

class RunPlugin{
    apply(compiler){
        compiler.hooks.run.tap('RunPlugin',()=>{
            console.log('run 开始编译');
        });
    }
}
module.exports = RunPlugin;

3.7 DonePlugin.js #

plugins\DonePlugin.js

class DonePlugin{
    apply(compiler){
        compiler.hooks.done.tap('DonePlugin',()=>{
            console.log('done 结束编译');
        });
    }
}
module.exports = DonePlugin;

3.9 logger1-loader.js #

loaders\logger1-loader.js

function loader(source){
  console.log('logger1-loader');
  return source+'//logger1';
}
module.exports = loader;

3.10 logger2-loader.js #

loaders\logger2-loader.js

function loader(source){
  console.log('logger2-loader');
  return source+'//logger2';
}
module.exports = loader;

3.11 src\entry1.js #

src\entry1.js

const title = require('./title');
console.log('entry1 ' ,title);

3.12 src\entry2.js #

src\entry2.js

const title = require('./title');
console.log('entry2',title);

3.13 src\title.js #

src\title.js

const name = require('./name');
module.exports = 'title'+name;

3.14 src\name.js #

src\name.js

module.exports = 'name';

4.Stats 对象 #

字段 含义
modules 记录了所有解析后的模块
chunks 记录了所有 chunk
assets 记录了所有要生成的文件
npx webpack --profile --json > stats.json
{
  "hash": "780231fa9b9ce4460c8a", //编译使用的 hash
  "version": "5.8.0", // 用来编译的 webpack 的版本
  "time": 83, // 编译耗时 (ms)
  "builtAt": 1606538839612, //编译的时间
  "publicPath": "auto", //资源访问路径
  "outputPath": "C:\\webpack5\\dist", //输出目录
  "assetsByChunkName": {
    //代码块和文件名的映射
    "main": ["main.js"]
  },
  "assets": [
    //资源数组
    {
      "type": "asset", //资源类型
      "name": "main.js", //文件名称
      "size": 2418, //文件大小
      "chunkNames": [
        //对应的代码块名称
        "main"
      ],
      "chunkIdHints": [],
      "auxiliaryChunkNames": [],
      "auxiliaryChunkIdHints": [],
      "emitted": false,
      "comparedForEmit": true,
      "cached": false,
      "info": {
        "javascriptModule": false,
        "size": 2418
      },
      "related": {},
      "chunks": ["main"],
      "auxiliaryChunks": [],
      "isOverSizeLimit": false
    }
  ],
  "chunks": [
    //代码块数组
    {
      "rendered": true,
      "initial": true,
      "entry": true,
      "recorded": false,
      "size": 80,
      "sizes": {
        "javascript": 80
      },
      "names": ["main"],
      "idHints": [],
      "runtime": ["main"],
      "files": ["main.js"],
      "auxiliaryFiles": [],
      "hash": "d25ad7a8144077f69783",
      "childrenByOrder": {},
      "id": "main",
      "siblings": [],
      "parents": [],
      "children": [],
      "modules": [
        {
          "type": "module",
          "moduleType": "javascript/auto",
          "identifier": "C:\\webpack5\\src\\index.js",
          "name": "./src/index.js",
          "nameForCondition": "C:\\webpack5\\src\\index.js",
          "index": 0,
          "preOrderIndex": 0,
          "index2": 1,
          "postOrderIndex": 1,
          "size": 55,
          "sizes": {
            "javascript": 55
          },
          "cacheable": true,
          "built": true,
          "codeGenerated": true,
          "cached": false,
          "optional": false,
          "orphan": false,
          "dependent": false,
          "issuer": null,
          "issuerName": null,
          "issuerPath": null,
          "failed": false,
          "errors": 0,
          "warnings": 0,
          "profile": {
            "total": 38,
            "resolving": 26,
            "restoring": 0,
            "building": 12,
            "integration": 0,
            "storing": 0,
            "additionalResolving": 0,
            "additionalIntegration": 0,
            "factory": 26,
            "dependencies": 0
          },
          "id": "./src/index.js",
          "issuerId": null,
          "chunks": ["main"],
          "assets": [],
          "reasons": [
            {
              "moduleIdentifier": null,
              "module": null,
              "moduleName": null,
              "resolvedModuleIdentifier": null,
              "resolvedModule": null,
              "type": "entry",
              "active": true,
              "explanation": "",
              "userRequest": "./src/index.js",
              "loc": "main",
              "moduleId": null,
              "resolvedModuleId": null
            }
          ],
          "usedExports": null,
          "providedExports": null,
          "optimizationBailout": [],
          "depth": 0
        },
        {
          "type": "module",
          "moduleType": "javascript/auto",
          "identifier": "C:\\webpack5\\src\\title.js",
          "name": "./src/title.js",
          "nameForCondition": "C:\\webpack5\\src\\title.js",
          "index": 1,
          "preOrderIndex": 1,
          "index2": 0,
          "postOrderIndex": 0,
          "size": 25,
          "sizes": {
            "javascript": 25
          },
          "cacheable": true,
          "built": true,
          "codeGenerated": true,
          "cached": false,
          "optional": false,
          "orphan": false,
          "dependent": true,
          "issuer": "C:\\webpack5\\src\\index.js",
          "issuerName": "./src/index.js",
          "issuerPath": [
            {
              "identifier": "C:\\webpack5\\src\\index.js",
              "name": "./src/index.js",
              "profile": {
                "total": 38,
                "resolving": 26,
                "restoring": 0,
                "building": 12,
                "integration": 0,
                "storing": 0,
                "additionalResolving": 0,
                "additionalIntegration": 0,
                "factory": 26,
                "dependencies": 0
              },
              "id": "./src/index.js"
            }
          ],
          "failed": false,
          "errors": 0,
          "warnings": 0,
          "profile": {
            "total": 0,
            "resolving": 0,
            "restoring": 0,
            "building": 0,
            "integration": 0,
            "storing": 0,
            "additionalResolving": 0,
            "additionalIntegration": 0,
            "factory": 0,
            "dependencies": 0
          },
          "id": "./src/title.js",
          "issuerId": "./src/index.js",
          "chunks": ["main"],
          "assets": [],
          "reasons": [
            {
              "moduleIdentifier": "C:\\webpack5\\src\\index.js",
              "module": "./src/index.js",
              "moduleName": "./src/index.js",
              "resolvedModuleIdentifier": "C:\\webpack5\\src\\index.js",
              "resolvedModule": "./src/index.js",
              "type": "cjs require",
              "active": true,
              "explanation": "",
              "userRequest": "./title.js",
              "loc": "1:12-33",
              "moduleId": "./src/index.js",
              "resolvedModuleId": "./src/index.js"
            },
            {
              "moduleIdentifier": "C:\\webpack5\\src\\title.js",
              "module": "./src/title.js",
              "moduleName": "./src/title.js",
              "resolvedModuleIdentifier": "C:\\webpack5\\src\\title.js",
              "resolvedModule": "./src/title.js",
              "type": "cjs self exports reference",
              "active": true,
              "explanation": "",
              "userRequest": null,
              "loc": "1:0-14",
              "moduleId": "./src/title.js",
              "resolvedModuleId": "./src/title.js"
            }
          ],
          "usedExports": null,
          "providedExports": null,
          "optimizationBailout": [
            "CommonJS bailout: module.exports is used directly at 1:0-14"
          ],
          "depth": 1
        }
      ],
      "origins": [
        {
          "module": "",
          "moduleIdentifier": "",
          "moduleName": "",
          "loc": "main",
          "request": "./src/index.js"
        }
      ]
    }
  ],
  "modules": [
    //模块数组
    {
      "type": "module",
      "moduleType": "javascript/auto",
      "identifier": "C:\\webpack5\\src\\index.js",
      "name": "./src/index.js",
      "nameForCondition": "C:\\webpack5\\src\\index.js",
      "index": 0,
      "preOrderIndex": 0,
      "index2": 1,
      "postOrderIndex": 1,
      "size": 55,
      "sizes": {
        "javascript": 55
      },
      "cacheable": true,
      "built": true,
      "codeGenerated": true,
      "cached": false,
      "optional": false,
      "orphan": false,
      "issuer": null,
      "issuerName": null,
      "issuerPath": null,
      "failed": false,
      "errors": 0,
      "warnings": 0,
      "profile": {
        "total": 38,
        "resolving": 26,
        "restoring": 0,
        "building": 12,
        "integration": 0,
        "storing": 0,
        "additionalResolving": 0,
        "additionalIntegration": 0,
        "factory": 26,
        "dependencies": 0
      },
      "id": "./src/index.js",
      "issuerId": null,
      "chunks": ["main"],
      "assets": [],
      "reasons": [
        {
          "moduleIdentifier": null,
          "module": null,
          "moduleName": null,
          "resolvedModuleIdentifier": null,
          "resolvedModule": null,
          "type": "entry",
          "active": true,
          "explanation": "",
          "userRequest": "./src/index.js",
          "loc": "main",
          "moduleId": null,
          "resolvedModuleId": null
        }
      ],
      "usedExports": null,
      "providedExports": null,
      "optimizationBailout": [],
      "depth": 0
    },
    {
      "type": "module",
      "moduleType": "javascript/auto",
      "identifier": "C:\\webpack5\\src\\title.js",
      "name": "./src/title.js",
      "nameForCondition": "C:\\webpack5\\src\\title.js",
      "index": 1,
      "preOrderIndex": 1,
      "index2": 0,
      "postOrderIndex": 0,
      "size": 25,
      "sizes": {
        "javascript": 25
      },
      "cacheable": true,
      "built": true,
      "codeGenerated": true,
      "cached": false,
      "optional": false,
      "orphan": false,
      "issuer": "C:\\webpack5\\src\\index.js",
      "issuerName": "./src/index.js",
      "issuerPath": [
        {
          "identifier": "C:\\webpack5\\src\\index.js",
          "name": "./src/index.js",
          "profile": {
            "total": 38,
            "resolving": 26,
            "restoring": 0,
            "building": 12,
            "integration": 0,
            "storing": 0,
            "additionalResolving": 0,
            "additionalIntegration": 0,
            "factory": 26,
            "dependencies": 0
          },
          "id": "./src/index.js"
        }
      ],
      "failed": false,
      "errors": 0,
      "warnings": 0,
      "profile": {
        "total": 0,
        "resolving": 0,
        "restoring": 0,
        "building": 0,
        "integration": 0,
        "storing": 0,
        "additionalResolving": 0,
        "additionalIntegration": 0,
        "factory": 0,
        "dependencies": 0
      },
      "id": "./src/title.js",
      "issuerId": "./src/index.js",
      "chunks": ["main"],
      "assets": [],
      "reasons": [
        {
          "moduleIdentifier": "C:\\webpack5\\src\\index.js",
          "module": "./src/index.js",
          "moduleName": "./src/index.js",
          "resolvedModuleIdentifier": "C:\\webpack5\\src\\index.js",
          "resolvedModule": "./src/index.js",
          "type": "cjs require",
          "active": true,
          "explanation": "",
          "userRequest": "./title.js",
          "loc": "1:12-33",
          "moduleId": "./src/index.js",
          "resolvedModuleId": "./src/index.js"
        },
        {
          "moduleIdentifier": "C:\\webpack5\\src\\title.js",
          "module": "./src/title.js",
          "moduleName": "./src/title.js",
          "resolvedModuleIdentifier": "C:\\webpack5\\src\\title.js",
          "resolvedModule": "./src/title.js",
          "type": "cjs self exports reference",
          "active": true,
          "explanation": "",
          "userRequest": null,
          "loc": "1:0-14",
          "moduleId": "./src/title.js",
          "resolvedModuleId": "./src/title.js"
        }
      ],
      "usedExports": null,
      "providedExports": null,
      "optimizationBailout": [
        "CommonJS bailout: module.exports is used directly at 1:0-14"
      ],
      "depth": 1
    }
  ],
  "entrypoints": {
    //入口点
    "main": {
      "name": "main",
      "chunks": ["main"],
      "assets": [
        {
          "name": "main.js",
          "size": 2418
        }
      ],
      "filteredAssets": 0,
      "assetsSize": 2418,
      "auxiliaryAssets": [],
      "filteredAuxiliaryAssets": 0,
      "auxiliaryAssetsSize": 0,
      "children": {},
      "childAssets": {},
      "isOverSizeLimit": false
    }
  },
  "namedChunkGroups": {
    //命名代码块组
    "main": {
      "name": "main",
      "chunks": ["main"],
      "assets": [
        {
          "name": "main.js",
          "size": 2418
        }
      ],
      "filteredAssets": 0,
      "assetsSize": 2418,
      "auxiliaryAssets": [],
      "filteredAuxiliaryAssets": 0,
      "auxiliaryAssetsSize": 0,
      "children": {},
      "childAssets": {},
      "isOverSizeLimit": false
    }
  },
  "errors": [],
  "errorsCount": 0,
  "warnings": [],
  "warningsCount": 0,
  "children": []
}