JS 加载性能Tree Shaking优化详解

目录
  • 正文
  • 什么是 Tree Shaking
  • 寻找 Tree Shaking 的机会
  • 防止 Babel 将 ES6 模块转换为 CommonJS 模块
  • 留意 side effects
  • 只导入你需要的
  • 更复杂的情况
  • 总结

正文

随着 web 应用复杂性增加,JS 代码文件的大小也在不断的攀升,截住 2021年9月,在 httparchive 上有统计显示——在移动设备上 JS 传输大小大约为 447 KB,桌面端 JS 传输大小大约为 495 KB,注意这仅仅是在网络中传输的 JS 文件大小,JS 的实际大小要比传输大小大很多。

上图是下载和运行JavaScript的过程。

即使 JS 的传输大小被压缩为 300 KB,但仍然有 900 KB 的 JS 代码需要被浏览器解析、编译和执行。图像一旦被下载,浏览器只需要花费相对琐碎的解码时间,与图像不同的是,JS 必须被解析、编译,最终执行,这使得处理 JS 比处理其他类型的资源更耗时。

上图是浏览器解析/编译 170 KB的 JS 的处理成本与同等大小的 JPEG 的解码时间。JS 引擎的性能在不断被改进,改进网站 JS 性能也是开发者要做的事情。Code Splitting 是优化 JS 性能的技术之一,但是它不能减少应用程序的 JS 代码的总大小,在这里我们使用 Tree Shaking 来减小 js 代码的大小。

什么是 Tree Shaking

您可以将应用程序想象成一棵树。您实际使用的源代码和库表示树中绿色的活叶子,死代码表示秋天时树上棕色的枯叶,为了除掉枯叶,你必须摇动树,让它们掉下。Tree Shaking 是指消除死代码,下面通过一个应用程序演示了这个概念。使用 ES6 静态模块语法导入依赖项:

// 在这里导入了所有的数组处理方法
import arrayUtils from "array-utils";

在应用最初的时候,依赖项可能很少,随着功能逐渐增加,依赖项也会增加,更糟糕的是,旧的依赖项不再使用,但可能不会从代码库中删除,最终的结果是,应用程序带有大量未使用的 JS 代码,Tree Shaking 解决了这个问题,它通过分析我们在文件中使用的 ES6 静态模块语句来分析哪些模块被导入了:

// 只导入部分方法
import { unique, implode, explode } from "array-utils";

这个导入示例与前一个示例的区别在于,本示例只导入模块的特定部分,而不是从“array-utils”模块导入所有内容。

寻找 Tree Shaking 的机会

为了便于说明,这里有一个使用 webpack 的单页应用程序示例来演示 Tree Shaking 是如何工作的。界面如下:

这个程序打包之后的代码被分为两文件,如下:

在任何应用程序中,你需要从静态导入语句寻找 Tree Shaking 的机会,在示例程序中(FilterablePedalList.js)你将看到这样一行导入语句:

import * as utils from "../../utils/utils";

在文件中这样的导入语句应该引起你的注意,它的意思是:从 utils 模块导入所有内容。问题是:你真的用到所有的内容了吗?现在我们来检查 FilterablePedalList.js 中究竟使用了 utils 模块中的那些方法,通过检索发现只使用了 utils.simpleSort:

if (this.state.sortBy === "model") {
  // Simple sort gets used here...
  json = utils.simpleSort(json, "model", this.state.sortOrder);
} else if (this.state.sortBy === "type") {
  // ..and here...
  json = utils.simpleSort(json, "type", this.state.sortOrder);
} else {
  // ..and here.
  json = utils.simpleSort(json, "manufacturer", this.state.sortOrder);
}

我们现在开始做 Tree Shaking 优化

防止 Babel 将 ES6 模块转换为 CommonJS 模块

在大型应用中 Babel 是不可或缺的工具,但是它会让 Tree Shaking 变得困难。如果你正在使用 babel-preset-env,它会自动为你将 ES6 模块转换为更广泛兼容的 CommonJS 模块,即:用 require 代替 import。对于 CommonJS 模块而言做 Tree Shaking 优化非常困难,这是因为 CommonJS 模块是动态的,在构建阶段 bundlers 不容易分析出 CommonJS 模块导出了什么和导入了什么。为了避免 babel-preset-env 将 ES6 模块转换成 CommonJS 模块,我们可以这么做:

{
  "presets": [
    ["env", {
      "modules": false
    }]
  ]
}

在你的 Babel -preset-env 配置中简单指定"modules": false 就可以让 Babel 按照我们想要的方式运行,这允许 webpack 分析你的依赖树并摆脱那些未使用的依赖。

留意 side effects

当函数修改了它作用域之外的内容,我们就认为这个函数有 side effects。side effects 也适用于ES6模块,在你做 Tree Shaking 的时候你需要留意你的模块是否有 side effects(副作用),如果模块接受可预测的输入,并输出同样可预测的输出,而不修改其自身范围之外的任何内容,我们就认为这个模块没有 side effects,对于没有 side effects 的模块你可以放心的做 Tree Shaking。在这里我举两个 side effects 例子:

import './index.module.scss';
import './assets/shoot.svg';

如果在某个模块中出现了上面这样的 import 语句,则认为这个模块有 side effects,这时在做 Tree Shaking 的时候你需要小心一些。默认情况下,webpack 在做 Tree Shaking 的时候,会认为 index.module.scss 与 assets/shoot.svg 没有被用到,所以它们会被移除,如果不想被移除,可以告诉 webpack 它们是 side effects:

{
  "name": "webpack-tree-shaking-example",
  "version": "1.0.0",
  "sideEffects": [
    "./index.module.scss",
    "./assets/shoot.svg"
  ]
}

在项目的 package.json 中配置 sideEffects 字段。sideEffects 字段也能为 false,这表示项目中不存在有 side effects 的模块。

只导入你需要的

现在我们已经告诉 babel 不要将 ES6 模块转成 CommonJS 模块,现在我们需要对导入语法做一点微调,只从 utils 模块中引入我们需要的函数吗,在本指南的例子中,我们只需要 simpleSort:

import { simpleSort } from "../../utils/utils";
if (this.state.sortBy === "model") {
  json = simpleSort(json, "model", this.state.sortOrder);
} else if (this.state.sortBy === "type") {
  json = simpleSort(json, "type", this.state.sortOrder);
} else {
  json = simpleSort(json, "manufacturer", this.state.sortOrder);
}

现在我们已经完成了 Tree Shaking 的工作,下面是 Tree Shaking 之前 webpack 打包生成的 js 包大小:

                 Asset      Size  Chunks             Chunk Names
js/vendors.16262743.js  37.1 KiB       0  [emitted]  vendors
   js/main.797ebb8b.js  20.8 KiB       1  [emitted]  main

下面是 Tree Shaking 之后 webpack 打包生成的 js 包大小:

                 Asset      Size  Chunks             Chunk Names
js/vendors.45ce9b64.js  36.9 KiB       0  [emitted]  vendors
   js/main.559652be.js  8.46 KiB       1  [emitted]  main

main 的文件大小下降比较明显,这是因为 webpack 移除了不需要的 utils 方法。

更复杂的情况

在有些情况你按照上面的步骤进行 Tree Shaking,但是 webpack 还是将模块的所有内容都打包到最终的 Chunk 中了,例如:lodash。

// 仍然会导入所有的内容
import { sortBy } from "lodash";
// 这只会导入 sortBy
import sortBy from "lodash/sortBy";

如果你想要使用第一种写法,那么你还需要安装 babel-plugin-lodash。如果你使用了第三方库,你可以看一下这个库的导出是否使用了 ES6 语法,如果它的导出用的是 CommonJS 语法,例如:module.exports,那么 webpack 不能对它进行 Tree Shaking 优化。有些插件,例如:webpack-common-shake,提供了对 CommonJS 模块进行 Tree Shaking 的能力,但是它有一些限制

总结

为了确保构建工具可以成功地优化你的应用程序,应该避免依赖 CommonJS 模块,并在整个应用程序中使用 ES6 模块语法。

以上就是JS 加载性能Tree Shaking优化详解的详细内容,更多关于JS 加载Tree Shaking的资料请关注我们其它相关文章!

(0)

相关推荐

  • webpack的tree shaking的实现方法

    webpack的tree shaking util.js export const a = () => { console.log("a123456方法"); }; export const b = () => { console.log("b123456方法"); }; main.js import {a} from './utils'; a(); sideEffects 一般而言,上述代码,在 webpack 进行 tree shaking 能够不打

  • 浅析webpack 如何优雅的使用tree-shaking(摇树优化)

    1.什么是tree-shaking webpack 2 的到来带来的最棒的新特性之一就是tree-shaking .tree-shaking源自于rollup.js,先如今,webpack 2也有类似的做法. webpack 里的tree-shaking的到来不得不归功于es6规范的模块.为什么这么说,如今的前端模块规范很多,比较出流行的比如commonJS , AMD , es6 ,我简单的说一下commonJS和es6模块的区别. commonJS 模块 commonJS的模块规范在Node

  • webpack4 CSS Tree Shaking的使用

    本次课程的代码目录(如下图所示): 什么是tree-shaking webpack 2 的到来带来的最棒的新特性之一就是tree-shaking .tree-shaking源自于rollup.js,先如今,webpack 2也有类似的做法. webpack 里的tree-shaking的到来不得不归功于es6规范的模块.为什么这么说,如今的前端模块规范很多,比较出流行的比如commonJS , AMD , es6 ,我简单的说一下commonJS和es6模块的区别. 1. CSS 也有 Tree

  • 浅谈Webpack4 Tree Shaking 终极优化指南

    几个月前,我的任务是将我们组的 Vue.js 项目构建配置升级到 Webpack 4.我们的主要目标之一是利用 tree-shaking 的优势,即 Webpack 去掉了实际上并没有使用的代码来减少包的大小.现在,tree-shaking 的好处将根据你的代码库而有所不同.由于我们的几个架构决策,我们从公司内部的其他库中提取了大量代码,而我们只使用了其中的一小部分. 我写这篇文章是因为恰当地优化 Webpack 并不简单.一开始我以为这是一种简单的魔法,但后来我花了一个月的时间在网上搜索我遇到

  • tree shaking对打包体积优化及作用

    目录 背景 有啥用? 实践 前置准备 打包 sideEffects 副作用 sideEffects的使用 优化体积 背景 大家平时在查 webpack构建体积优化 ,可能都会查到 tree-shaking 这个东西,很多人看到这个东西,就会把它背下来,用来应付以后面试官可能会问到的情况. 但是,又有多少人去真的了解一下 tree-shaking 呢?自己去实践一下看 tree-shaking 到底起了哪些作用?对于我们的打包体积的优化又有多少呢? 有啥用? Tree Shaking中文含义是摇树

  • JS 加载性能Tree Shaking优化详解

    目录 正文 什么是 Tree Shaking 寻找 Tree Shaking 的机会 防止 Babel 将 ES6 模块转换为 CommonJS 模块 留意 side effects 只导入你需要的 更复杂的情况 总结 正文 随着 web 应用复杂性增加,JS 代码文件的大小也在不断的攀升,截住 2021年9月,在 httparchive 上有统计显示——在移动设备上 JS 传输大小大约为 447 KB,桌面端 JS 传输大小大约为 495 KB,注意这仅仅是在网络中传输的 JS 文件大小,JS

  • Three.js加载外部模型的教程详解

    1.  首先我们要在官网: https://threejs.org/ 下载我们three.js压缩包,并将其中的build文件夹下的three.js通过script标签对的src属性导入到我们的页面中 2.  创建three.js核心对象 Scene(场景) Camera(相机) Light(光源) Mesh(模型) Renderer(渲染器) 最后一步就是渲染显示在我们的页面上了renderer.render(scene,camera) 3.  OBJ模型的导入 <script type=&quo

  • 微信小程序JS加载esmap地图的实例详解

    一.在微信小程序里显示室内三维地图 需要满足的两个条件 调用ESMap室内地图需要用到小程序web-view组件,想要通过 web-view 调用ESMap室内地图需要满足以下 2 个条件: 1. 小程序是企业主体,微信 web-view 组件不对个人类型的小程序开放. 2. 您需要有一个自己的域名,在嵌入网页的时候需要在微信后台验证域名(只有自己域名下的网页才能被正确地显示哦,不能随便找一个公开链接). 二.具体实现步骤 1.域名验证: 由于微信平台的规定,web-view 指向的地址,必须是

  • Vue使用Three.js加载glTF模型的方法详解

    前言 Three.js是一个跨浏览器的脚本,使用JavaScript函数库或API来在网页浏览器中创建和展示动画的三维计算机图形,基于WebGL实现,对WebGL进行了进一步的封装,简化了多数复杂的接口. Three.js支持包括 .obj..gltf等类型的模型结构.glTF(GL传输格式)是Khronos的一个开放项目,它为3D资产提供了一种通用的.可扩展的格式,这种格式既高效又与现代web技术高度互操作. obj格式的模型只支持顶点.法线.纹理坐标和基本材质,而glTF模型除上述所有内容外

  • BootstrapTable加载按钮功能实例代码详解

    1      html <!--工具栏--> <div id="toolbar" class="btn-group"> <div style="float:left;margin-right: 10px"> <button class="btn btn-danger"onclick="openModal('add',0,'')">增加</button&g

  • PyTorch加载自己的数据集实例详解

    数据预处理在解决深度学习问题的过程中,往往需要花费大量的时间和精力. 数据处理的质量对训练神经网络来说十分重要,良好的数据处理不仅会加速模型训练, 更会提高模型性能.为解决这一问题,PyTorch提供了几个高效便捷的工具, 以便使用者进行数据处理或增强等操作,同时可通过并行化加速数据加载. 数据集存放大致有以下两种方式: (1)所有数据集放在一个目录下,文件名上附有标签名,数据集存放格式如下: root/cat_dog/cat.01.jpg root/cat_dog/cat.02.jpg ...

  • AngularJs动态加载模块和依赖注入详解

    废话不多说,进入正题... 首先我们看下文件结构: Angular-ocLazyLoad --- demo文件夹 Scripts --- 框架及插件文件夹 angular-1.4.7 --- angular 不解释 angular-ui-router --- uirouter 不解释 oclazyload --- ocLazyload 不解释 bootstrap --- bootstrap 不解释 angular-tree-control-master --- angular-tree-cont

  • vue+webpack实现异步加载三种用法示例详解

    1.第一例 const Home = resolve => { import("@/components/home/home.vue").then( module => { resolve(module) } } 注:(上面import的时候可以不写后缀) export default [{ path: '/home', name:'home', component: Home, meta: { requireAuth: true, // 添加该属性可以判断出该页面是否需要

  • JavaScript无阻塞加载和defer、async详解

    无阻塞加载 把js放在head里,浏览器是怎么去执行它的呢,是按顺序加载还是并行加载呢?在旧的浏览器下,都是按照先后顺序来加载的,这就保证了加载的js依赖不会发生问题.但是少部分新的浏览器已经开始允许并行加载js了,也就是说可以同时下载js文件,但是还是按先后顺序执行文件的. 下载是异步的没问题,但是每个javascript执行的时候还是同步的,就是先出现的script标签一定是先执行,即使是并行下载它是最后一个下载完成的,除非标有defer的script标签.任何javascript在执行的时

  • JavaScript前端图片加载管理器imagepool使用详解

    前言 imagepool是一款管理图片加载的JS工具,通过imagepool可以控制图片并发加载个数. 对于图片加载,最原始的方式就是直接写个img标签,比如:<img src="图片url" />. 经过不断优化,出现了图片延迟加载方案,这回图片的URL不直接写在src属性中,而是写在某个属性中,比如:<img src="" data-src="图片url" />.这样浏览器就不会自动加载图片,等到一个恰当的时机需要加载

随机推荐