webpack Compiler

什么是Compiler

Compiler 负责编译,贯穿webpack的整个生命周期,Compiler 对象包含了当前运行Webpack的配置,包括entry、output、loaders等配置,这个对象在启动Webpack时被实例化,而且是全局唯一的。Plugin可以通过该对象获取到Webpack的配置信息进行处理。我们可以在源码中看看Compiler是怎么定义的。
在这里插入图片描述

class Compiler extends Tapable {
	constructor(context) {
		super();
		this.hooks = {};
		// TODO webpack 5 remove this
		this.hooks.infrastructurelog = this.hooks.infrastructureLog;

		this._pluginCompat.tap("Compiler", options => {
		});

		this.name = undefined;
		this.parentCompilation = undefined;
		this.outputPath = "";
		this.outputFileSystem = null;
		this.inputFileSystem = null;
		this.recordsInputPath = null;
		this.recordsOutputPath = null;
		this.records = {};
		this.removedFiles = new Set();
		this.fileTimestamps = new Map();
		this.contextTimestamps = new Map();
		this.resolverFactory = new ResolverFactory();
		this.infrastructureLogger = undefined;
		this.resolvers = {
		};
		this.options = /** @type {WebpackOptions} */ ({});
		this.context = context;
		this.requestShortener = new RequestShortener(context);
		this.running = false;
		this.watchMode = false;
		this._assetEmittingSourceCache = new WeakMap();
		this._assetEmittingWrittenFiles = new Map();
	}

	getInfrastructureLogger(name) {
	}

	watch(watchOptions, handler) {
	}

	run(callback) {
	}

	runAsChild(callback) {
	}

	purgeInputFileSystem() {
	}

	emitAssets(compilation, callback) {
	}

	emitRecords(callback) {
	}

	createChildCompiler(
		compilation,
		compilerName,
		compilerIndex,
		outputOptions,
		plugins
	) {
	}

	isChild() {
	}

	createCompilation() {
		return new Compilation(this);
	}

	newCompilation(params) {
	}

	createNormalModuleFactory() {
	}

	createContextModuleFactory() {
	}

	newCompilationParams() {
	}

	compile(callback) {
	}
}

把详细的内容去掉,我们可以看到,Compiler 对象中包含了 options, hoks, context, name, outPutPath等熟悉,和watch, run, createCompilation, compile 等方法。

Compiler 是在webpack 启动的时候实例化的,那我们知道了webpack存在这些属性和方法,那么我们可以知道webpack 可以读取到这些属性和调用这些方法,来完成编译工作。

Compiler.hooks

this.hooks = {
			
			shouldEmit: new SyncBailHook(["compilation"]),
			/** @type {AsyncSeriesHook<Stats>} */
			done: new AsyncSeriesHook(["stats"]),
			/** @type {AsyncSeriesHook<>} */
			additionalPass: new AsyncSeriesHook([]),
			/** @type {AsyncSeriesHook<Compiler>} */
			beforeRun: new AsyncSeriesHook(["compiler"]),
			/** @type {AsyncSeriesHook<Compiler>} */
			run: new AsyncSeriesHook(["compiler"]),
			/** @type {AsyncSeriesHook<Compilation>} */
			emit: new AsyncSeriesHook(["compilation"]),
			/** @type {AsyncSeriesHook<string, Buffer>} */
			assetEmitted: new AsyncSeriesHook(["file", "content"]),
			/** @type {AsyncSeriesHook<Compilation>} */
			afterEmit: new AsyncSeriesHook(["compilation"]),

			/** @type {SyncHook<Compilation, CompilationParams>} */
			thisCompilation: new SyncHook(["compilation", "params"]),
			/** @type {SyncHook<Compilation, CompilationParams>} */
			compilation: new SyncHook(["compilation", "params"]),
			/** @type {SyncHook<NormalModuleFactory>} */
			normalModuleFactory: new SyncHook(["normalModuleFactory"]),
			/** @type {SyncHook<ContextModuleFactory>}  */
			contextModuleFactory: new SyncHook(["contextModulefactory"]),

			/** @type {AsyncSeriesHook<CompilationParams>} */
			beforeCompile: new AsyncSeriesHook(["params"]),
			/** @type {SyncHook<CompilationParams>} */
			compile: new SyncHook(["params"]),
			/** @type {AsyncParallelHook<Compilation>} */
			make: new AsyncParallelHook(["compilation"]),
			/** @type {AsyncSeriesHook<Compilation>} */
			afterCompile: new AsyncSeriesHook(["compilation"]),

			/** @type {AsyncSeriesHook<Compiler>} */
			watchRun: new AsyncSeriesHook(["compiler"]),
			/** @type {SyncHook<Error>} */
			failed: new SyncHook(["error"]),
			/** @type {SyncHook<string, string>} */
			invalid: new SyncHook(["filename", "changeTime"]),
			/** @type {SyncHook} */
			watchClose: new SyncHook([]),

			/** @type {SyncBailHook<string, string, any[]>} */
			infrastructureLog: new SyncBailHook(["origin", "type", "args"]),

			// TODO the following hooks are weirdly located here
			// TODO move them for webpack 5
			/** @type {SyncHook} */
			environment: new SyncHook([]),
			/** @type {SyncHook} */
			afterEnvironment: new SyncHook([]),
			/** @type {SyncHook<Compiler>} */
			afterPlugins: new SyncHook(["compiler"]),
			/** @type {SyncHook<Compiler>} */
			afterResolvers: new SyncHook(["compiler"]),
			/** @type {SyncBailHook<string, Entry>} */
			entryOption: new SyncBailHook(["context", "entry"])
		};

通过代码可以看到 compiler.hooks 是钩子贯穿了整改webpack打包的生命周期,那么我们的插件就是注册到这些钩子(订阅钩子事件),当执行到这些钩子函数时,将会通知插件,并且通过回调返回参数给插件,就可以实现插件逻辑。

插件可以做些什么

通知学习插件的执行时间点,我们可以执行什么(改变输出),要改变输出,取决于我们可以获取到什么,以及对它做些什么修改操作,比如我们可以去除注释,去除空格,合并代码,压缩文件,提取公共代码,改变配置,修改,改变输出等。

内置插件和自定义插件

内置插件

在这里插入图片描述
我们可以看到项目存在很多内置插件,每个插件有自己独自的功能,比如DllPlugin插件,这个插件是在一个额外的独立的 webpack 设置中创建一个只有 dll 的 bundle(dll-only-bundle)。 这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。

class DllPlugin {
	/**
	 * @param {DllPluginOptions} options options object
	 */
	constructor(options) {
		validateOptions(schema, options, "Dll Plugin");
		this.options = options;
	}

	apply(compiler) {
	// 1. 在entryOption 时触发
		compiler.hooks.entryOption.tap("DllPlugin", (context, entry) => {
			const itemToPlugin = (item, name) => {
			// 如果是数组,执行调用DllEntryPlugin插件
				if (Array.isArray(item)) {
					return new DllEntryPlugin(context, item, name);
				}
				throw new Error("DllPlugin: supply an Array as entry");
			};
			// 判断参数类型,如果是object 但不是 array
			if (typeof entry === "object" && !Array.isArray(entry)) {
			// 遍历对象的keys
				Object.keys(entry).forEach(name => {
					itemToPlugin(entry[name], name).apply(compiler);
				});
			} else {
				itemToPlugin(entry, "main").apply(compiler);
			}
			return true;
		});
		new LibManifestPlugin(this.options).apply(compiler);
		if (!this.options.entryOnly) {
			new FlagAllModulesAsUsedPlugin("DllPlugin").apply(compiler);
		}
	}
}

自定义插件

在根目录下定义一个 ts, 在 vue.config.json中引入。
自定义写了一个插件,将插件注册到entryOption钩子中,npm run build 可以看到打印:在这里插入图片描述
在这里插入图片描述
打印出了上下文和enrty 入口文件,那么我们也是可以在这里进行修改的,比如修改入口文件。
当换成 run 钩子时,
在这里插入图片描述
返回的参数就是一个compiler实例。也就是说每一个钩子函数都会返回不一样的参数,我们可以通过我们的需求注册到不同的钩子里面,去对不同流程做我们想要的操作。

_pluginCompat: SyncBailHook {        
    _args: [ 'options' ],
    taps: [ [Object], [Object], [Object] ],
    interceptors: [],
    call: [Function: lazyCompileHook],
    promise: [Function: lazyCompileHook],
    callAsync: [Function: lazyCompileHook],
    _x: undefined
  },
  hooks: {
    shouldEmit: SyncBailHook {
    
    },
    done: AsyncSeriesHook {
      
    },
    additionalPass: AsyncSeriesHook {
  
    },
    beforeRun: AsyncSeriesHook {
    
    },
    run: AsyncSeriesHook {
    
    },
    emit: AsyncSeriesHook {
    
    },
    assetEmitted: AsyncSeriesHook {
     
    },
    afterEmit: AsyncSeriesHook {
    
    },
    thisCompilation: SyncHook {
    
    },
    compilation: SyncHook {
    
    },
    normalModuleFactory: SyncHook {
    
    },
    contextModuleFactory: SyncHook {
   
    },
    beforeCompile: AsyncSeriesHook {
    
    },
    compile: SyncHook {
    
    },
    make: AsyncParallelHook {
    
    },
    afterCompile: AsyncSeriesHook {
   
    },
    watchRun: AsyncSeriesHook {
    
    },
    failed: SyncHook {
    
    },
    invalid: SyncHook {
    
    },
    watchClose: SyncHook {
    
    },
    infrastructureLog: SyncBailHook {
      
    },
    environment: SyncHook {
    
    },
    afterEnvironment: SyncHook {
     
    },
    afterPlugins: SyncHook {
    
    },
    afterResolvers: SyncHook {
     
    },
    entryOption: SyncBailHook {
    
    },
    infrastructurelog: SyncBailHook {
    
    }
  },
  name: undefined,
  parentCompilation: undefined,
  outputPath: 'F:\\company\\zhejiang-complaint\\dist',
  outputFileSystem: NodeOutputFileSystem {
   
  },
  inputFileSystem: CachedInputFileSystem {
    fileSystem: NodeJsInputFileSystem {},
   
 
  },
  recordsInputPath: undefined,
  recordsOutputPath: undefined,
  records: {},
  removedFiles: Set(0) {},
  fileTimestamps: Map(0) {},
  contextTimestamps: Map(0) {},
  resolverFactory: ResolverFactory {
   
  },
  infrastructureLogger: [Function: logger],
  resolvers: {
    normal: { plugins: [Function: deprecated], apply: [Function: deprecated] },
    loader: { plugins: [Function: deprecated], apply: [Function: deprecated] },
    context: { plugins: [Function: deprecated], apply: [Function: deprecated] }
  },
  options: {
    mode: 'production',
    context: 'F:\\company\\zhejiang-complaint',
    devtool: false,
    node: {
      setImmediate: false,
      process: 'mock',
      dgram: 'empty',
      fs: 'empty',
      net: 'empty',
      tls: 'empty',
      child_process: 'empty',
      console: false,
      global: true,
      Buffer: true,
      __filename: 'mock',
      __dirname: 'mock'
    },
    output: {
     
    },
    resolve: {
    
    },
    resolveLoader: {
    
    },
    module: {
     
    },
    optimization: {
      
    },
    plugins: [
      VueLoaderPlugin {},
      [DefinePlugin],
      [CaseSensitivePathsPlugin],
      [FriendlyErrorsWebpackPlugin],
      [MiniCssExtractPlugin],
      [OptimizeCssnanoPlugin],
      [HashedModuleIdsPlugin],
      [NamedChunksPlugin],
      [HtmlWebpackPlugin],
      [PreloadPlugin],
      [PreloadPlugin],
      [CopyPlugin],
      [ForkTsCheckerWebpackPlugin],
      [vConsolePlugin],
      [HotHashWebpackPlugin],
      [WebpackBarPlugin],
      ConsoleLogOnBuildWebpackPlugin {}
    ],
    entry: { main: [Array] },
    cache: false,
    target: 'web',
    performance: {
      maxAssetSize: 250000,
      maxEntrypointSize: 250000,
      hints: 'warning'
    },
    infrastructureLogging: { level: 'info', debug: false }
  },
  context: 'F:\\company\\zhejiang-complaint',
  requestShortener: RequestShortener {
    currentDirectoryRegExp: /(^|!)F:\/company\/zhejiang\-complaint/g,
    parentDirectoryRegExp: /(^|!)F:\/company\//g,
    buildinsAsModule: true,
    buildinsRegExp: /(^|!)F:\/company\/zhejiang\-complaint\/node_modules\/webpack/g,
    cache: Map(0) {}
  },
  running: true,
  watchMode: false,
  _assetEmittingSourceCache: WeakMap { <items unknown> },
  _assetEmittingWrittenFiles: Map(0) {},
  watchFileSystem: NodeWatchFileSystem {
  
    },
    watcherOptions: { aggregateTimeout: 200 },
    watcher: EventEmitter {
    
    }
  },
  webpackbar: WebpackBarPlugin {
   
  },
  dependencies: undefined
} 00000
webpack run+++++

在 run 阶段我们可以获取到很多信息,也提供了插件的很多可能。

compilation

Compilation对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 Compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息,简单来讲就是把本次打包编译的内容存到内存里。Compilation 对象也提供了插件需要自定义功能的回调,以供插件做自定义处理时选择使用拓展。
简单来说,Compilation的职责就是构建模块和Chunk,并利用插件优化构建过程。
和 Compiler 用法相同,钩子类型不同,也可以在某些钩子上访问 tapAsync 和 tapPromise。
打印 compilation
在这里插入图片描述
源码有点多,看不动了~

Compiler 和 Compilation 的区别

Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译,只要文件有改动,compilation就会被重新创建。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值