总结 Tree-Shaking

什么是 Tree-Shaking

用来在打包编译成 bundle 时消除 ES6 Module 语法中未使用到的代码和模块。
What is tree shaking? 🌲

Tree-Shaking 的作用

用于跨文件代码的 DCE 优化。
依赖 ES6 module 特性。
消除未被 import 的 export 模块。
对 Function 函数模块的处理效果好。
无法直接处理 Class 模块。

什么是 Dead Code Elimination

DCE 用来消除不能执行的代码 Dead Code。
javascript 常用 uglify 来 DCE 。
只能清除单文件里的 Dead Code (只读不写,结果用不到,不被执行)

应该怎么实践 Tree-Shaking

三方库按需加载。

import map from "lodash/map";
import { Input, Button } from "atnd"; // 需要使用 babel 按需加载 plugin

按需加载的实现
不要写奇怪含有副作用的代码。副作用wiki
良好的模块拆分和设计才是关键。

CommonJS 和 ES6 Module

CommonJS require 动态加载,执行后才知道结果。
ES6 Module import 静态分析,不需要执行即可通过字面量对代码分析。

打包工具的 Tree-Shaking

Rollup 只处理函数和顶层 import / export导入的变量
Rollup 结果比 Webpack 更优化点

Dead Code

参考
Dead code problem

  • 死区代码,被赋值但在后续执行中未使用的变量(Dead store)
function func(a, b) {
    var x;
    var i = 300;
    while (i--) {
        x = a + b; // Dead store
    }
}
  • 曾经使用但现在未使用到的代码(Oxbow code)
function temp() {
    function private_calculation() {
        return 22;
    }
    this.calc = function () {
        // private_calculation()
        return 22;
    }
}
  • 无法到达的代码: 执行不了的判断,执行不到的循环,return 后的代码(Unreachable code)
// 执行不了的判断
function temp() {
    var a = 2;
    if (a > 3) {
         return 3; // Dead Code
    }
}
// return 后的代码
function temp() {
    return 1;
    var a = 2; // Dead Code
}
// 执行不到的循环
function temp() {
    var a = 2;
    while (false) {
         return 3; // Dead Code
    }
}
  • 无用的 <script src="" > 资源链接

Webpack Rollup 配置 Tree-Shaking

Rollup 配置

import babel from 'rollup-plugin-babel'
import uglify from 'rollup-plugin-uglify'

export default {
  entry: 'src/main.js',
  dest: 'rollup.bundle.js',
  format: 'cjs',
  treeshake: true,
  plugins: [
    babel(),
    uglify()
  ]
}

Webpack 配置

const webpack = require('webpack')
const path = require('path')

module.exports = {
  entry: path.join(__dirname, 'src/main.js'),
  output: {filename: 'webpack.bundle.js'},
  module: {
    rules: [
      {
        test: /\.js$/,
        loaders: 'babel-loader'
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: 'production'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      beautify: false,
      comments: false,
      compress: {
        warnings: false,
        collapse_vars: true,
        reduce_vars: true
      }
    }),
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
}

总结

Tree-Shaking 是用来在打包编译前消除 es6 module 中未使用的代码模块的功能。
打包工具用 webpack 和 rollup 来实现,只能对 export 导出的函数代码和模块进行优化。
相比 DCE Tree-Shaking 属于广义的 DCE,传统 DEC 只能清除单文件中不可执行不可到达的代码,Tree-Shaking 是跨文件依赖于 es6 module 导入静态分析的。
commonjs 规范属于动态导入,代码不执行到 require 是无法知道执行的结果的,es6 module 的 import 属于静态分析,通过导入的字面量即可分析模块代码。

推荐阅读更多精彩内容