Tree shaking 的概念、作用和原理

结论先行:

Tree-shaking 也叫摇树优化,它是通过移除多余的代码,从而减小最终的打包体积和应用程序的加载时间。 

它的原理呢,是利用了 ES6 的模块化语法,也就是我们常用到的 import 和 export。通过静态分析代码之间的引用关系,来判断哪些模块未被引用,进而删除对应代码。

像webpack、Vue3底层都有用到 tree-shaking, 来优化项目的打包体积。

这种模块机制可以根据一个入口静态地构建出依赖图的数据结构,而不用实际运行代码。也就是可以在代码不运行的状态下,分析出不需要的代码。

1、文档地址

Tree Shaking | webpack 中文文档

2、Tree shaking 是什么?

在前端的性能优化中,ES6 推出了 Tree shaking(树摇)机制

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

可以理解为通过工具"摇"我们的 JS 文件,将其中用不到的代码"摇"掉,是一个性能优化的范畴。

比如在项目中引用了一个模块 Element。但其实只使用到了 Button 组件、Input 组件。那么 webpack 在打包后会消除掉以上组件之外的其他 Element组件。

从而减小打包体积,以此达到性能优化的目的。

具体来说,在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。 

也就是说,在打包的时候会删除掉未被引用的 export ;

只支持 ES6 Module 代码;

在 production 生产环境默认开启;

3、Tree shaking 的作用

Tree shaking 的作用是减少应用程序的大小和加载时间。

由于只包括实际使用的模块,因此可以避免加载不必要的代码,从而减少加载时间和网络请求,提高应用程序的性能。

同时,减少应用程序的大小也可以减少用户下载和安装应用程序的时间。 

4、在 webpack 中使用 Tree-shaking 的三步

① 找到未使用的代码

模块必须使用 ES6 的 import 导入 和 export 导出,以此找出未使用的代码。

Tree Shaking 只支持 ESM 的引入方式,不支持 Common JS 的引入方式

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

注意:如果想要做到 tree shaking,那么在引入模块时就应该避免全部引入。应该引入局部,才可以触发 tree shaking 机制。

// 导入所有内容(不会触发 tree-shaking)
import lodash from 'lodash';

// 导入命名导出 (会触发 tree-shaking)
import { debounce } from 'lodash';

// 直接导入项目 (会触发 tree-shaking)
import debounce from 'lodash/lib/debounce';

② 标记无副作用  

sideEffects: false 标记为"无副作用",可以安全地删除

也可以在 module.rules 配置选项中设置 “sideEffects”。

新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json 的 "sideEffects" 属性作为标记。向 compiler 提供提示,表明项目中的哪些文件是 "pure(纯的 ES2015 模块)",由此可以安全地删除文件中未使用的部分。

// 所有文件都无副作用,可以 tree-shaking
{
  "name": "test-demo",
  "sideEffects": false,
}

// 所有文件都有副作用,都不可以 tree-shaking
{
 "sideEffects": true
}

// 只有这些文件有副作用,所有其他文件都可以 tree-shaking,但这些文件必须保留
{
 "sideEffects": [
  "./src/file1.js",
  "./src/file2.js"
 ]
}

③ 配置环境,安全删除 

1)开发环境

在开发环境下,bundle 不会被压缩

// webpack.config.js
module.exports = {
  // ...
  mode: 'development',
  optimization: {
    usedExports: true
  }
};

 2)生产环境

通过 ES6 的 import 和 export 语法,我们已经找出需要删除的“未引用代码(dead code)”。

然而不仅仅是要找出,还要在 bundle 中删除它们。为此,我们需要将 mode 配置选项设置为production。

// webpack.config.js
module.exports = {
  // ...
  mode: 'production',
};

5、sideEffects对全局CSS的影响

对于那些直接引入到 js 文件的文件,例如全局的 css,它们并不会被转换成一个 CSS 模块。

/* reset.css */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body {
  background-color: red;
}
// main.js
import "@/assets/css/reset.css"

出现问题: 

这样的代码在打包后,打开页面你就会发现样式并没有应用上。

原因在于:上面我们将sideEffects设置为 false 后,所有的文件都会被 Tree Shaking,通过 import 这样的形式引入的 CSS 就会被当作无用代码处理掉

解决办法: 

为了解决这个问题,可以在 loader 的规则配置中,添加 sideEffects: true

告诉 Webpack 这些文件不要执行 Tree Shaking。

// webpack.config.js
module.exports = {
  // ...
    module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
        sideEffects: true
      }
    ]
  },
};

6、总结

① 使用 ES6(ES2015)模块语法中的 import 和 export 才可以触发这个机制;

这是因为 tree shaking 只能在静态 modules 下工作。ECMAScript 6 模块加载是静态的,因此整个依赖树可以被静态地推导出解析语法树。所以在ES6中使用 tree shaking 是非常容易的。 

② 在项目的 package.json 文件中,添加 "sideEffects" 属性;

③ 使用 mode 为 "production" 的配置项以启用更多优化项,包括压缩代码与 tree shaking;

④ 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的

(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档

④ tree shaking就是类似一棵树有长熟的苹果,将已经成熟的苹果摇掉减轻树的负担,这就实现了这个机制。

你可以将应用程序想象成一棵树。绿色表示实际用到的 source code(源码)和 library(库),是树上活的树叶。灰色表示未引用代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。

⑤ 需要注意的是,Tree shaking只能消除未使用的模块,对于那些被使用但只是部分使用的模块,如只使用其中几个函数的模块,Tree shaking并不能消除不被使用的函数。

针对这种情况,可以考虑使用代码分割等技术来进一步优化应用程序的性能。 

猜你喜欢

转载自blog.csdn.net/qq_38290251/article/details/134170737