webpack原理,使用,优化

1 webpack的原理

1. 原理

webpack整体是一个事件驱动架构,所有的功能都以Plugin(插件)的方式集成在构建流程中, 通过发布订阅事件来触发各个插件执行。webpack核心使用tapable来实现Plugin(插件)的注册和调用,Tapable是一个事件发布(tap)订阅(call)库。

2. webpack的流程

webpack原理,使用,优化_第1张图片

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

webpack是我们在日常开发中使用的构建工具,它的打包速度直接影响开发效率,因此优化webpack的打包是很重要的,下面使用我们的一个比较大型的项目来做实验。
webpack原理,使用,优化_第2张图片
上图是采用常规webpack配置,并未采用优化手段打包,打包时间100s…

采用webpack4.0提供的优化手段:

  • 分包,使用 SplitChunks 抽取公有代码,重点是缓存组cacheGroups的配置,主要是用于过滤模块的test的设置,test可以是直接的正则表达式,也可以是过滤函数,可以使用test来配置一些公共代码的抽取,这里我们对node_modules下的npm包和src下的shared/compentes一些公用部分打成一个基础包。
optimization.splitChunks = {
        cacheGroups: {
            vendor: {
                test(mod, chunks) {
                    return (
                        (mod.context.includes("node_modules")  ||
                        /src[\\/]\w+[\\/](shared|components)|src[\\/]shared/.test(mod.context)
                    )
                },
                chunks: "all", 
                name: "vendor"
            }
        }
    }

webpack原理,使用,优化_第3张图片
增加公共代码的抽取主要是对代码进行分块打包,并不能对打包速度有特别大的提升,还是在90—100之间。

  • happypack,多线程编译,加快编译速度(主要是加快loader的编译速度,此外,babel的配置文件关于env的配置也需要使用babel/preset-env进行优化,具体可参见前端项目工程化工具webpack,gulp,babel的使用和原理中babel部分),解决webpck单线程的弊端,项目中配置了一个happypack函数对babel-loader速度进行优化:
const happypack = (webpackConfig) =>{
		const HappyPack = require("happypack")
		const os = require("os")
		const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
		let babelOptions = {}
		let rules = webpackConfig.module.rules.reduce((rs, rule) => {
			if (rule.loader === "babel-loader") {
				babelOptions = rule.options
				rs.push({
					test: /\.(js|mjs|jsx|ts|tsx)$/,
					exclude: /(node_modules|bower_components)/,
					loader: "happypack/loader?id=happyBabel"
				})
			} else {
				rs.push(rule)
			}
			return rs
		}, [])
		webpackConfig.module.rules = rules
		webpackConfig.plugins.push(
			new HappyPack({
				// id 标识符,要和 rules 中指定的 id 对应起来
				id: "happyBabel",
				threadPool: happyThreadPool,
				// 需要使用的 loader,用法和 rules 中 Loader 配置一样
				// 可以直接是字符串,也可以是对象形式
				loaders: [{ loader: "babel-loader", options: babelOptions }]
			})
		)
	}

webpack原理,使用,优化_第4张图片
增加了happypack之后,打包速度快了点,已经从100秒降到70秒了。。

  • babel-loader配置的优化
    在babel-loader转译文件时候非常耗费时间,我们需要对这个过程进行优化,参考官网(https://webpack.docschina.org/loaders/babel-loader/),主要对options进行设置,经过测试,设置cacheDirectory(当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程(recompilation process)。如果设置了一个空值 (loader: ‘babel-loader?cacheDirectory’) 或者 true (loader: ‘babel-loader?cacheDirectory=true’),loader 将使用默认的缓存目录 node_modules/.cache/babel-loader,如果在任何根目录下都没有找到 node_modules 目录,将会降级回退到操作系统默认的临时文件目录。)
    在刚刚的happypack函数中增加cacheDirectory:
const happypack = (webpackConfig) =>{
		const HappyPack = require("happypack")
		const os = require("os")
		const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
		let babelOptions = {
			cacheDirectory: true
		}
		let rules = webpackConfig.module.rules.reduce((rs, rule) => {
			if (rule.loader === "babel-loader") {
				babelOptions = rule.options
				rs.push({
					test: /\.(js|mjs|jsx|ts|tsx)$/,
					exclude: /(node_modules|bower_components)/,
					loader: "happypack/loader?id=happyBabel"
				})
			} else {
				rs.push(rule)
			}
			return rs
		}, [])
		webpackConfig.module.rules = rules
		webpackConfig.plugins.push(
			new HappyPack({
				/*
				 * 必须配置
				 */
				// id 标识符,要和 rules 中指定的 id 对应起来
				id: "happyBabel",
				threadPool: happyThreadPool,
				// 需要使用的 loader,用法和 rules 中 Loader 配置一样
				// 可以直接是字符串,也可以是对象形式
				loaders: [{ loader: "babel-loader", options: babelOptions }]
			})
		)
	}

webpack原理,使用,优化_第5张图片
打包时间瞬间降到了29秒,感动。。。

  • DllPlugin,DllReferencePlugin:动态链接库
    先运行一个dll配置文件,预先把一些固定模块编译,它会在第一次编译的时候将配置好的需要预先编译的模块编译在缓存中,第二次编译的时候,解析到这些模块就直接使用缓存,而不是去编译这些模块(DllPlugin);将预先编译好的模块关联到当前编译中,当 webpack 解析到这些模块时,会直接使用预先编译好的模块(DllReferencePlugin),,这块暂时没在项目使用。。。

参考文献:
1.https://lequ7.com/2019/04/26/javascript/webpack-yuan-li-shu-li/
2.https://juejin.im/post/5c763885e51d457380771ab0
3. https://juejin.im/entry/5b0e3eba5188251534379615
4. webpack原理都不会

你可能感兴趣的:(web前端,前端部署,webpack)