Webpack4.0基础教程七:tree shaking(前端系列周刊(3))

首先,在此声明一下,这篇文章之前写的时候也是有点模模糊糊,应该有很多错的地方(后半部分),或者逻辑比较乱的地方,各位小伙伴如果读这篇文章的话,感觉不清晰的地方就不要去深究了,以免浪费时间,同时把你们带偏,之后会抽空把文章纠正更新一下,在此对那些可能被我带偏的伙伴说声抱歉,同时也希望看我文章的哥们、小姐姐们能够指出我的一些问题,或者提供一些补充和一些小demo。

foreword(前言)

这里主要声明一下,对于下面出现的treeShaking失效是由于babel转码的原因,目前最新的webpack版本4.41.2是没问题的,而下面提到的这部分是由于本篇文章前一个版本在test时使用的版本为4.1.x,这或许是当时这个webpack版本中的一个bug,目前应该已经修复,所以这部分大家可以忽略。(大家可以主要从demo2中开始浏览。)

什么是treeShaking

tree shaking
tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。
新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json 的 “sideEffects” 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 “pure(纯的 ES2015 模块)”,由此可以安全地删除文件中未使用的部分。

准备工作

  1. 目录:
    –src
    webpack.config.js

  2. webpack配置:

'use strict'

const path = require('path')

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    'babel-loader'
                ]
            }
        ]
    }
}
  1. 依赖:

webpack下如何配置treeShaking

首先先看一个小案例demo1,添加一个通用模块:

src/math.js:

export function square(x) {
 console.log('square');
  return x * x;
}
 
export function cube(x) {
  console.log('cube');
  return x * x * x;
}

接着,更新入口脚本,并使用其中一个方法:
src/index.js:

...
import { cube } from './math.js';
 
console.log(cube(2));
...

现在我们运行我们的npm脚本npm run build,并检查我们输出的bundle,如图所示:

在这里插入图片描述
注意,我们没有导入squre,但是它仍然被包含在bundle中,这个是为什么呢,为什么还会保留?小伙伴们可能会说“这还用问吗,现在根本就没有进行任何配置,自然不会帮我们进行优化”,真的是这样吗,我们继续看。

现在,我们在package.json文件中添加一个配置"sideEffects": false (这个配置的意思是将入口文件涉及到的文件都标识为不含副作用,即标示代码为pure——纯的代码,以此来告知webpack,它可以安全地删除未用到的export导出,副作用的理解可以参考这篇文章:http://www.fly63.com/article/detial/1176)

在这里插入图片描述

然后输入命令npm run build进行打包操作,结果如下:

在这里插入图片描述

我擦,根本没有上面卵用,和文档上是一样的啊。莫急莫急,我们接着来。

前方高能,胆小勿入(以下黑色粗体字为本人自我臆想,切莫相信,切莫相信)

同样的,没有进行treeShaking,没有引用的函数还是包含在bundle.js文件中,按道理,如果它真的会优化,当我们设置"sideEffects": false,它便会进行优化打包,实际上它没有,那么真的没有吗?
其实上面的所有情况(包括不设置sideEffects配置的情况)webpack都会帮我们进行优化,操作treeShaking,至于为什么,这里要提一下:
还记得官方怎么说的吗?

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于
ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具
rollup。

Tree shaking必须要依赖于es6的import和export,针对export引用的文件以及真正使用到的部分进行优化处理,而我们在这之前进行了babel转码的操作,对于转码后的es5语法我们就不会使用到tree shaking。

是的,上面的纯属本人扯淡,其实事实并非如此,其实在加了sideEffects时,webpack确实已经将我们的代码进行tree shaking,我们继续来玩玩吧:

我们在index.js入口文件中添加这样几句,如:

function test() {
  return 1234;
}
 
test();

咱打下包吧,然后搜索一下1234看看,结果:

在这里插入图片描述

看到没找不到,说明webpack已经启动了tree shaking并检测出上述代码是dead code(无用代码),故而将上述代码删除。

那。。。,为哈子我们import时的代码中square函数仍然保留呢?这个嘛。。。这个确实跟es6语法有关:因为我们之前不是用了babel吗,babel把es6转化为es5的过程中,会使得转化之后的代码产生副作用,故而上述math.js中的square会存在其中。

所以呢,为了删除上面没有用到的square函数,我们需要去除掉babel的作用效果,即在webpack.config.js配置文件中注释掉babel的配置,如下:

在这里插入图片描述

接着我们重新打包文件:

在这里插入图片描述

看到没有,现在只剩下cube这个函数。(注:这次打包基于sideEffects设为false)

————————————————————————————————————
注:以上由于webpack测试版本原因会有babel的问题,目前最新版本(笔者测试版本为4.41.2)应该是没有问题的。

接下来,我们再看看几个小例子:

demo2: (sideEffects配置不设)

代码同demo1

然后,npm run build进行打包:
在这里插入图片描述
同样的square并没有被引入,所以我们可以想到,webpack默认是开启了treeShaking的。那么,默认开启的treeShaking是等同于怎样的配置呢?看下一个例子:

demo3:(sideEffects: “./src/*.*”,即表示项目所有脚本为具有副作用)

代码同demo1

在打包之前,先将上次的打包结果复制一份,然后再将代码打包,打包后的代码和前面备份的demo2的bundle进行比较,发现两份代码是一样的,也就是说,默认情况下开启的treeShaking和sideEffects标识所有脚本为具有副作用是等价的。

OK,接下来我们再看几个例子。
上面的demo1、demo2、demo3,都是将cube引入,然后执行,如果不执行cube呢,来试试看。

demo4:(sideEffects: false)

================================
math.js:
export function square(x) {
 console.log('square');
  return x * x;
}
 
export function cube(x) {
  console.log('cube');
  return x * x * x;
}
================================
index.js:
...
import { cube } from './math.js';
...
================================

打包后的结果如下:

在这里插入图片描述

在这里插入图片描述
demo5:(sideEffects配置项不设,代码同上)

代码同demo4

打包后结果和demo4一样。

这时,你可能非常怀疑,设不设sideEeffects不都是一样的吗?

非也,我们再来看一些例子:

demo6: (sideEffects: false)

================================
menu.js:
function Menu () {
    console.log('menu');
}
 
Menu.prototype.show = function () {
    console.log('menu prototype');
}
 
Array.prototype.unique = function () {
    // 将 array 中的重复元素去除
    console.log('unique');
}

export default Menu;
================================
index.js:
...
import menu from './menu.js'
...
================================

打包后,搜索“menu”和“unique”均为“找不到”。

demo7:(sideEffects不设)

代码同demo6

打包后,搜索“menu”、“menu prototype”、“unique”均能搜到。

rules(treeShaking解析规则)

基于上面的demo,我们可以得出以下几项规则:

  1. treeShaking一定会消除的代码:
  • 声明的变量未调用,则会被删除;
  • 调用某个函数时,函数仅仅声明了一些变量,并返回这个变量,或者返回的就是一个常量,这时仅仅调用函数,最终代码是会被消除的(除非对函数返回的值进行操作),另外“作为值赋值给一个变量,但这个变量无后续操作”也是会被消除的,还有一种情况就是函数内仅仅声明变量并调用,这样代码也是会被消除的;
  1. treeShaking一定不会消除的代码:(需要满足下面所有条件)
  • 声明变量并使用它(计算、打印等);
  • 对于函数,函数内需要有操作,比如:“let a = 1; a = a * a;”,或者“console.log(xxx)”,然后对函数进行调用;或者函数仅返回一个值,没有操作,这时要求函数返回的值必须被操作,比如作为另一个函数的参数输入,“作为值赋值给一个变量,但这个变量无后续操作”除外;
  1. 标识为副作用时才会保留:
  • 函数符合上述1中的描述,并对函数原型进行了自定义设置,代码将在标识副作用时全部保留;
  • 对诸如Array、Function、Object的prototype注入自定义的变量或函数时,这部分代码将会被保留;

summary(总结)

  • webpack4.x默认对treeShaking进行了支持,默认情况下标识项目目录下所有文件存在副作用;
  • treeShaking对代码的消除和保存满足上面的三点条件(个人测试,可能还有其他的条件,对这方面有了解的还望指出);
  • treeShaking标识某些代码具有副作用即“告诉程序针对符合某些条件的代码块进行保留”;

thinking(思考)

由于这是周刊文,所以首先我们来回顾一下上一篇周文中笔者给出的思考:

  • 第一个问题,debugger for chrome有什么优势呢:笔者个人认为这个插件产生的原因是chrome devtool在调试代码时的便捷性不够,比如我们要来回切换IDE和Browser,或者当我们通过浏览器下断点时,我们从souces里找文件笔者个人是觉得恶心的。debugger for chrome的出现使得代码调试更专注于代码,我们能够直接停留在断点处,然后停在问题出错处,然后直接修改代码。
  • 第二个问题,对于个人编写的模块,常用debbger下断点,能使用debugger for chrome,最好使用它,样式问题则在浏览器中编写好、调整好后,复制粘贴到代码中保存,而如果是在别人代码中修改,比如修改css样式,如果不知道某些动态样式是在哪里进行设置的,可以设置dom断点等。另外,活用chrome devtool中的application和perfomance。

本期思考:

  • 如何将treeShaking运用到实际项目中呢?(俺也不知道啊,有大大可以指点指点吗(^_^)。。。不对,按自己要把它研究出来,哼。。。)
  • 如何根据项目类型、体量等各种因素标识项目某些代码文件存在副作用?

欢迎大家阅读我关于webpack4.x的其他博文:
https://blog.csdn.net/yaodebian/category_9282168.html

last(最后)
非常感谢您能阅读完这篇文章,您的阅读是我不断前进的动力。

对于上面所述,有什么新的观点或发现有什么错误,希望您能指出。

最后,附上个人常逛的社交平台:
知乎:https://www.zhihu.com/people/bi-an-yao-91/activities
csdn:https://blog.csdn.net/YaoDeBiAn
github: https://github.com/yaodebian

个人目前能力有限,并没有自主构建一个社区的能力,如有任何问题或想法与我沟通,请通过上述某个平台联系我,谢谢!!!

发布了80 篇原创文章 · 获赞 91 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/YaoDeBiAn/article/details/103224246