【webpack快速入门】如何通过webpack摇树优化做前端性能优化?

2,037 阅读7分钟

前言

大家好,我是东东吖,一名前端工程师。不知道你们有没有接手过大型、版本变更迭代多的项目,这样的项目里面会存在很多重构和废弃未调用的方法,这样会导致在编译速度慢、打包后的体积大,那么浏览器将花费更多时间去下载、解压、转换和执行它们。但是很多却是我们并不需要的代码,那怎么办呢?把废弃的代码全部删掉吗?你能保证删除的代码一定是废弃的吗?如果你是考虑项目的后期维护,可以考虑对项目做减肥计划,但是这样需要大量的资源去投入,全面的检查和测试,且有一定的风险,公司是否有这样的预算和人力去做这样一件事?如果只是考虑性能优化,不编译废弃调用的代码,我们前端是有解决方案的,那就是我们的摇树优化tree shaking

tree shaking是什么?

摇树优化:删除项目中未被引用代码,其本质是像把枯萎的叶子从树上摇下来一样,将项目中没有用的代码段抖掉以减小最后生成的文件的体积,从而优化加载,提高用户体验。摇树优化首次出现于rollup,是rollup的核心概念,后来在webpack v2里借鉴过来使用。

在前端的性能优化中,es6 推出了tree shaking机制,tree shaking就是当我们在项目中引入其他模块时,他会自动将我们用不到的代码,或者永远不会执行的代码摇掉,在Uglify阶段查出,不打包到bundle中。

首先,要明确一点:Tree Shaking 只支持 ESM 的引入方式,不支持 Common JS 的引入方式。

ESM: export + import
Common JS: module.exports + require

提示:如果想要做到tree shaking,在引入模块时就应该避免将全部引入,应该引入局部才可以触发tree shaking机制,tree shaking只支持ES6 Module代码。在production 环境默认开启。

如何配置摇树优化?

  • 开发环境
//开发环境需要结合usedExports
mode: "development", //打包的模式
optimization: {
  usedExports: true,
},
  • 生产环境:
 //在production 环境默认开启
 mode: "production", //打包的模式

值得注意的是:

开发环境为引用的代码也会被打包在bundle.js中,但会被标记未被使用,而在生产环境,webpack 对代码已经做了极致优化,发现未使用的代码都不见了,包括未调用的函数,注释的代码、return之后的代码....

在开发环境下,如果配置了 optimization.usedExports 为 true ,那么会标记未使用的代码,但并不会真正的去删除它们,如果要删除这些未使用的代码,需要配置为生产环境。

接下来,我们来验证这件事:

我们在common.jsd暴露出去三个方法,分别为use,quote,notUsed,并在里面写入了“方法”两字,方便打包后查找,并且在use方法中包含注释的代码和在retun之后的代码。

image.png

定义好方法后,我们在main,js引用use,quote,但只调用了use,没有调用quote

use:被调用的方法
quote:引入未被调用的方法
notUsed:未被引入的方法

接下来我们使用开发模式打包

image.png

我们会发现所有未被使用的代码都被打包了,包括未调用的函数,注释的代码、return之后的代码....

image.png

image.png

但是仔细查看你会发现,他们未被使用的方法被标记成了未使用,unused harmony exports quote, notUsed

image.png

我们切换为生产模式打包

image.png

我们只会搜到一个“方法”,即使用的那个方法,而无论是否引入,只要未被调用,都不会打包到bundle.js中,

image.png

同时被注释的代码return之后的代码也不会被打包。

image.png

摇树优化的副作用

sideEffects 指的是有副作用的代码,假如模块 A 中包含一些影响全局作用域(非模块作用域)的代码,如改变了全局的变量或对象的变量,模块 B 引入了模块 A ,但并未使用,在这种情况下,模块 A 就被认为是有副作用的,webpack 是不会删除模块 A 中所有未使用的代码的,它还会保留模块 A 中立即执行并对全局环境有影响的代码!

来验证一下:我们新建一个abuout.js文件,编写about方法,挂载到全局,在main.js中引入,但未真正调用, image.png

image.png

我们会发现about会被打包在bundle.js中 image.png

如果我们知道所有模块都是没有副作用的,不想让webpack打包它们该怎么办呢?

如果项目中所有模块都不会有副作用,那么可以在 webpack 的配置中将 package.json 的 sideEffects设为false,这样可以提高删除未使用代码的编译速度。

{
  "name": "your-project",
  "sideEffects": false
}

image.png

再次执行打包之后,我们会发现about方法没有被打包在bundle.js中

image.png

请注意,任何导入的文件都会受到树抖动的影响。这意味着如果您css-loader在项目中使用类似的东西并导入 CSS 文件,则需要将其添加到副作用列表中,这样它就不会在生产模式下无意中删除

如果有某些模块是有副作用的,那么可以将它的路径加入 package.json 的 sideEffects 数组选项中,这样也能提高删除未使用代码的速度。

这里我们把样式文件和我们最开始的about文件添加在副作用数组中



{
  "name": "your-project",
   "sideEffects": ["about.js", "*.css"],
}

我们先建一个index.css文件,编写样式

h1{
    color: yellowgreen;
}

image.png

在main.js中引入

image.png 当我们在 "sideEffects": false进行打包,样式没有生效

image.png

如何我们希望样式生效,或者某个模块,你并不太清楚是否有副作用,你希望webpack要打包它们,不在生产模式下无意中删除,``即:如果你觉得改模块存在副作用,希望webpack把他们进行打包,你就把他们添加到副作用的数组中。



{
  "name": "your-project",
   "sideEffects": ["about.js", "*.css"],
}

样式生效了

image.png

about模块也被进行打包了

image.png

摇树优化性能优化

最后我们再来直观的体验一下摇树优化的性能优化,我们在common.js,多编写几个未被调用的方法进行打包。

image.png 我们发现打包后的bundle.js打包后的体积有57kb

image.png

然后我们在开发环境开启摇树优化进行打包:

image.png

bundle.js打包后的体积还是有57kb

在开发环境进行摇树优化打包,bundle.js的体积并不会变小,原因是他只会标记为使用的代码,但并不会真正的清除它们。

最后我们在生产环境进行打包(默认会开启摇树优化):

image.png

bundle.js打包后的体积有15kb

一个简单的demo就从57kb减小到了15KB,如果是大型的项目,这优化效果将十分明显。

总结

我们了解到,为了利用tree shaking的优势,您必须...

  • 使用 ES2015 模块语法(即importexport)。
  • 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 模块(这是流行的 Babel 预设 @babel/preset-env 的默认行为 -有关详细信息,请参阅文档)。
  • "sideEffects"属性添加到项目package.json文件中。
  • 使用配置选项启用各种优化

通过本文,相信你对摇树优化tree shaking已经有所了解。你可以把你的应用想象成一棵树,您实际使用的源代码和库代表了这棵树的绿色、活生生的叶子,死代码代表秋天消耗掉的棕色枯叶。为了摆脱枯叶,你必须摇动树,使它们掉落。

结束

对于本文章,你有任何疑问,可在评论区留言。如果想进前端交流群(目前群里有40余人前端工程师,气氛活跃,欢迎大家加入),可以加我微信fangdongdong_25,请备注掘金哦。