Tree-Shaking

是什么

用于消除项目中没有被使用到的模块(引用了却没有被使用)

DEMO

demo1

  • rollup.config.js
import babel from 'rollup-plugin-babel'

export default {
  input: 'src/main.js',
  treeshake: true,
  output: {
    file: 'rollup.bundle.js',
    format: 'cjs'
  },
  plugins: [
    babel()
  ]
};
  • 主入口
import arg from './arg'
import { add } from './func'
import Clazz from './clazz'

export default function() {
  console.log(add(1, 2))
}
  • arg.js
export default 'hello world!';
  • clazz.js
export default class Clazz {
  constructor() {
    this.name = 'Clazz'
  }
  getName() {
    return this.name
  }
}
  • func.js
export function add(a, b) {
  return a + b
}

export function sub(a, b) {
  return a - b
}

打包后的代码

'use strict';
function add(a, b) {
  return a + b
}

function main() {
  console.log(add(1, 2));
}

module.exports = main;

可以发现,我们打包后的代码中,消除掉了没有用到的arg模块、clazz模块、以及func模块中的sub函数。这可以证明,函数、类、变量都是可以被Tree-shaking掉的

demo2

我们在func.js中添加一行:

export function add(a, b) {
  console.log(sub(a - b))
  return a + b
}
export function sub(a, b) {
  return a - b
}

此时add方法会调用同包内的另一个方法。
再进行打包操作,打包结果:

'use strict';
function add(a, b) {
  console.log(sub(a - b));
  return a + b
}

function sub(a, b) {
  return a - b
}

function main() {
  console.log(add(1, 2));
}
module.exports = main;

问题

在这篇文章中Tree-Shaking性能优化实践 - 原理篇 - 掘金,作者提到,类不能被消除。
这是因为作者使用了Babel对代码进行了预编译。Babel会添加含有副作用的函数,导致Tree-shaking失效。

原理

ES6模块的特点

  1. 只能在顶层引用
  2. 模块名是字符串常量
  3. 依赖关系是确定的

ES6之前的模块

  1. 可以在代码中动态引用
  2. 可能和运行状态有关,也有可能和运行状态无关,依赖关系不确定
  3. 模块名可能是变量

模块名确定、依赖关系确定,代码文件可以进行“静态分析”,这给Tree-Shaking奠定了基础。
静态分析就是不执行代码,从字面量上对代码进行分析。ES6之前的模块化,我们可以动态require一个模块,只有执行后才知道引用什么模块,这就不能通过静态分析去优化。(from: Tree-Shaking性能优化实践 - 原理篇 - 掘金)

思考

调研Tree-shaking的使用与原理可以带来两点思考

1. 代码优化

在实际写工具类代码的时候,不要使用一个对象并将方法挂载在这个对象上,然后export default这个对象的写法。最好采用export function依次输出函数方法。这样在打包工具进行分析时可以更好的进行Tree-Shaking
书写函数的时候要减少函数的副作用,尽量少在函数体内使用函数体外的变量,立即执行函数内少用函数体外的变量。
在制作模块的时候尽量将代码进行拆分,尽量不要在模块内直接写会带来副作用的操作。

2. 打包优化

经过以上的调研,我们发现使用babel会给代码带来一定的副作用,因此我们可以尝试先将代码进行打包,之后再将打包好后的代码使用babel转译一下。
推荐使用rollup工具进行打包。然而目前rollup的生态系统还不是那么完善,而且rollup工具还存在一定的功能限制,所以更多的还是使用webpack工具进行打包。
另外,也可以了解一下webpack中的sideEffects属性Webpack 中的 sideEffects 到底该怎么用? · Issue #41 · kuitos/kuitos.github.io · GitHub

****

Thanks for Reading

Jiahao.Zhang
NetEase Inc. Hangzhou 
        Front-End Developer