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组件。

扫描二维码关注公众号,回复: 17297712 查看本文章

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

具体来说,在 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
今日推荐