webpack 源码分析(一)

在面试中,我们总会被问到webpack相关的问题,如webpackloader,plugins是什么,有什么区别,又比如webpack的代码分割是怎么实现的,等一系列问题,很多人不能从底层彻底了解具体的实现,出于此目的,我想写一篇专栏,专门分析webpack源码的教程,其实已经有很多这种类型的文章了,但是我还想写一个专栏,我觉得市面上讲的还是有些粗糙,我要讲的更细更多一些。

废话不多说,接下来直接分析webpack源码。

平常我们一般都会使用webpack-cli来调用webpack,但其实webpack-cli,底层帮助我们做的操作就是调用了webpack的函数

webpack函数 有两个参数,第一个参数就是我们经常配置的参数 options,第二个参数其实也很简单 callback ,返回执行完的回调函数。

const webpack = (options,callback)=>{
  			let compiler;
			let watch = false;
			let watchOptions;
	const create = () => {
		if(Array.isArray(options)) {
		  compiler = createMultiCompiler(options);
		  watch = options.some(options => options.watch);
		  watchOptions = options.map(options => options.watchOptions || {});
		} else {
		  compiler = createCompiler(webpackOptions);
		  watch = webpackOptions.watch;
		  watchOptions = webpackOptions.watchOptions || {};
		}
	}
	  if(callback) {
			const { compiler, watch, watchOptions } = create();
			if (watch) {
					compiler.watch(watchOptions, callback);
			} else {
					compiler.run((err, stats) => {
						compiler.close(err2 => {
							callback(err || err2, stats);
						});
					});
			}
		return compiler;
	  } else {
		const { compiler, watch } = create();
		return compiler;
	  }
}

可以看到webpack 函数,针对options,数组和单配置传参做了处理,处理也非常的简单,核心就是createCompiler,然后执行.run方法或者watch方法。

看到这里其实就可以了解到webpack的核心代码是在createCompiler 或者说是createMultiCompiler,我们接下来看createCompiler,为什么不看后者呢,因为其实前后者基本上都一样,只不过后者是数组,多个,相当于调用多次前者罢了

const createCompiler = rawOptions => {
	const options = getNormalizedWebpackOptions(rawOptions);
	applyWebpackOptionsBaseDefaults(options);
	const compiler = new Compiler(options.context, options);
	new NodeEnvironmentPlugin({
		infrastructureLogging: options.infrastructureLogging
	}).apply(compiler);
	if (Array.isArray(options.plugins)) {
		for (const plugin of options.plugins) {
			if (typeof plugin === "function") {
				plugin.call(compiler, compiler);
			} else {
				plugin.apply(compiler);
			}
		}
	}
	applyWebpackOptionsDefaults(options);
	compiler.hooks.environment.call();
	compiler.hooks.afterEnvironment.call();
	new WebpackOptionsApply().process(options, compiler);
	compiler.hooks.initialize.call();
	return compiler;
};

我们先来看第一行代码const options = getNormalizedWebpackOptions(rawOptions);我们知道传参就是webpack.config,js配置文件,这个函数我觉得没必要往上列上去,这个函数的核心就是为了解决配置传参可以传递多种,如 webpack 配置的entry,我们知道entry:'./src/index.ts',我们还可以entry:{main:{import:'./src/index.ts'}},其实这两种方式是一致的,所以getNormalizedWebpackOptions函数就是支持多种参数的传递。

我们再来看applyWebpackOptionsBaseDefaults(options);

const applyWebpackOptionsBaseDefaults = options => {
    F(options, "context", () => process.cwd());
    applyInfrastructureLoggingDefaults(options.infrastructureLogging);
};

const F = (obj, prop, factory) => {
	if (obj[prop] === undefined) {
		obj[prop] = factory();
	}
};

const applyInfrastructureLoggingDefaults = infrastructureLogging => {
	F(infrastructureLogging, "stream", () => process.stderr);
	const tty =
		/** @type {any} */ (infrastructureLogging.stream).isTTY &&
		process.env.TERM !== "dumb";
	D(infrastructureLogging, "level", "info");
	D(infrastructureLogging, "debug", false);
	D(infrastructureLogging, "colors", tty);
	D(infrastructureLogging, "appendOnly", !tty);
};

const D = (obj, prop, value) => {
	if (obj[prop] === undefined) {
		obj[prop] = value;
	}
};

我们可以看到首先,给option.context 赋值,如果你不给传context,那我就默认选择process.pwd()当前调用路径为上下文路径。

处理options.infrastructureLogging,我们一般都不会传递这个属性,getNormalizedWebpackOptions会把它处理为空对象{},之后这个函数会把它处理成{ stream:process.stderr , level:info,debug:false,colors:false or true , appendOnly:true or false},true false 取决于process.stderr.isTTY,看输出是不是终端,如果是终端为true,否则false

接来下执行核心代码我们在下一节再说,webpack源码分析(二)

回顾一下本篇文章内容,

  1. createComiler
  2. getNormalizedWebpackOptions 处理options对象
  3. applyWebpackOptionsBaseDefaults 处理context和infrastructureLogging 给context和日志赋值

#前端##web前端##前端面试必备宝典#
前端技术专栏分享 文章被收录于专栏

1. 包含常规前端面试题解析,和源码分析 2. 从0-1深入浅出理解前端相关技术栈

全部评论

相关推荐

北斗导航Compass低仿版:没必要写这么多东西,还是尽量浓缩成一页,自我评价,git和cursor Trae这些都可以去掉。实习经历的描述最好根据star法则改一下,别这么直白
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务