使用 webpack 代码分割和魔术注释提升应用性能

二进制吟游诗人
• 阅读 5743

1. Web 应用性能优化的关键

关于 Web 应用性能优化,有一点是毫无疑问的:「页面加载越久,用户体验就越差」。我们几乎可以说 Web 应用性能优化的关键之处就在于:减少页面初载时,所需加载资源的「数量」和「体积」。

那么当所需加载的资源数量到达多少或资源体积小于多少,我们才可以自信地宣称我们的 Web 应用拥有出色的性能呢?下面是给出的一些参考值,该参考值考虑到了移动端与国外等多种访问环境:

  • 页面初载时,所有未压缩的 JavaScript 脚本大小:<=200KB
  • 页面初载时,所有未压缩的 CSS 资源大小:<=100KB
  • HTTP 协议下,请求资源数:<=6 个
  • HTTP/2 协议下,请求资源数:<=20 个
  • 90% 的代码利用率(也就是说,仅允许 10% 的未使用代码);

或许你会觉得这个标准有点过于苛刻了,是有一点点,但为了创建出高性能的 Web 应用,你的实际资源加载情况应该尽可能靠近这个目标。

2. 如何查看代码利用率

也许你注意到了,我们刚刚最后提到的一个指标是「代码利用率」,你可能是第一次听说这个概念,这里我解释一下它的计算方式:

代码利用率 = 你页面中实际被执行的代码 / 你页面中引入的代码 * 100%

你可能会困惑在实际开发中如何得到这个值,别担心,通过使用 Chrome 开发者工具(很遗憾,目前只有 Chrome 支持这一功能),你就可以迅速对你的 Web 应用进行分析,得到当前页面下的代码利用率状态,步骤如下:

  1. 打开 Chrome Dev Tool;
  2. 按下 Cmd + Shift + P or Ctrl + Shift + P ;
  3. 输入 Coverage,并选择第一个出现的选项(图 1);

使用 webpack 代码分割和魔术注释提升应用性能

<p style="text-align: center; color: #999;">图 1:直接输入 coverage 即可开启该看板</p>

  1. 点击面板上的 reload 按钮,查看整个应用 JavaScript 的代码利用率(图 2);

使用 webpack 代码分割和魔术注释提升应用性能

<p style="text-align: center; color: #999;">图 2:点击 reload 按钮,开始分析 JavaScript 代码利用率</p>

3. 提高代码使用率的关键技术 - 代码分割(code splitting)

3.1 什么是「代码分割」(code splitting)?

代码分割是指,将脚本中无需立即调用的代码在代码构建时转变为异步加载的过程。

在 Webpack 构建时,会避免加载已声明要异步加载的代码 ,异步代码会被单独分离出一个文件,当代码实际调用时被加载至页面。

3.2 代码分割的原理

代码分割技术的核心是「异步加载资源」,可喜的是,浏览器允许我们这么做,W3C stage 3 规范:whatwg/loader 对其进行了定义:你可以通过 import() 关键字让浏览器在程序执行时异步加载相关资源。

使用 webpack 代码分割和魔术注释提升应用性能

<p style="text-align: center; color: #999;">图 3:import() 的浏览器支持性</p>

没错,正如你所看到的, IE 浏览器目前并不支持这一特性,但这并不意味着你的异步加载功能在 IE 浏览会失效(那太可怕了 🤦‍♂️),实际上,Webpack 底层帮你将异步加载的代码抽离成一份新的文件,并在你需要时通过 JSONP 的方式去获取文件资源,因此,你可以在任何浏览器上实现代码的异步加载,并且在将来所有浏览器都实现 import() 方法时平滑过渡,cool!👍

3.3 代码分割的类型

代码分割可以分为「静态分割」和「"动态"分割」两种方式,注意这里打了引号的 "动态",因为实际上它并不意味着异步调用的代码是 "动态" 生成的,我们之后会看到 Webpack 是如何做到这一点的,在那之前,让我们先看看「静态代码分割」。

3.3.1 静态代码分割

静态代码分割是指,在代码中明确声明需要异步加载的代码

下面 👇 的代码说明了我们应该如何使用这一技术:

import Listener from './listeners.js'
const getModal = () => import('./src/modal.js') 
Listener.on(
  'didSomethingToWarrentModalBeingLoaded',
  () => {
    // Async fetching modal code from a separate chunk
    getModal().then(
      (module) => {
        const modalTarget = document.getElementById('Modal')    
        module.initModal(modalTarget)  
      })
  }
)

正如你所看到的:

每当你调用一个声明了异步加载代码的变量时,它总是返回一个 Promise 对象。

⚠️ 注意:在 Vue 中,可以直接使用 import() 关键字做到这一点,而在 React 中,你需要使用 react-loadable 去完成同样的事。

最后,让我们谈谈何时使用静态代码分割技术,这一技术适合以下的场景:

  1. 你正在使用一个非常大的库或框架:如果在页面初始化时你不需要使用它,就不要在页面初载时加载它;
  2. 加载任何临时的资源:指不在页面初始化时被使用,被使用后又会立即被销毁的资源,例如模态框,对话框,tooltip 等(任何一开始不显示在页面上的东西都可以有条件的加载);
  3. 页面路由:既然用户不会一下子看到所有页面,那么只把当前页面相关资源给用户就是个明智的做法;

好了,现在你掌握了静态代码分割技术,现在让我们看看什么是「"动态代"代码分割」技术。

3.3.2 动态代码分割

动态代码分割是指:在代码调用时根据当前的状态,「动态地」异步加载对应的代码块

下面 👇 的代码说明了它具体是如何被实现的:

const getTheme = (themeName) => import(`./src/themes/${themeName}`)
// using `import()` 'dynamically'
if (window.feeling.stylish) {
  getTheme('stylish').then((module) => {
    module.applyTheme()
  })
} else if (window.feeling.trendy) {
  getTheme('trendy').then((module) => {
    module.applyTheme()
  })
}

看到了吗,我们 "动态" 的声明了我们要异步加载的代码块,这是怎么做到的?!

答案出乎意料的简单,Webpack 会在构建时将你声明的目录下的所有可能分离的代码都抽象为一个文件(这被称为 contextModule 模块),因此无论你最终声明了调用哪个文件,本质上就和静态代码分割一样,在请求一个早已准备好的,静态的文件。

下面是一些使用 "动态" 代码分割技术的场景:

  1. A/B Test:你不需要在代码中引入不需要的 UI 代码;
  2. 加载主题:根据用户的设置,动态加载相应的主题;
  3. 为了方便 :本质上,你可以用静态代码分割代替「动态」代码分割,但是后者比前者拥有更少的代码量;

4.魔术注释

魔术注释是由 Webpack 提供的,可以为代码分割服务的一种技术。通过在 import 关键字后的括号中使用指定注释,我们可以对代码分割后的 chunk 有更多的控制权,让我们看一个例子:

// index.js
import (
  /* webpackChunkName: "my-chunk-name" */
  './footer'
)
同时,也要在 webpack.config.js 中做一些改动:
// webpack.config.js
{
  output: {
    filename: "bundle.js",
    chunkFilename: "[name].lazy-chunk.js"
  }
}

通过这样的配置,我们可以对分离出的 chunk 进行命名,这对于我们 debug 而言非常方便。

4.1 Webpack Modes

除了上面提到过的 webpackChunkName 注释外,Webpack 还提供了一些其他注释让我们能够对异步加载模块拥有更多控制权,例如下方这个例子:

import (
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: lazy */
  './someModule'
)

webpackMode 的默认值为 lazy 它会使所有异步模块都会被单独抽离成单一的 chunk,若设置该值为 lazy-once,Webpack 就会将所有带有标记的异步加载模块放在同一个 chunk 中。

4.2 Prefetch or Preload

通过添加 webpackPrefetch 魔术注释,Webpack 令我们可以使用与 <link rel="prefetch"> 相同的特性。让浏览器会在 Idle 状态时预先帮我们加载所需的资源,善用这个技术可以使我们的应用交互变得更加流畅。

import(
  /* webpackPrefetch: true */
  './someModule'
)

⚠️ 注意:请确保你的代码在未来一定会用到时,再开启该功能。

5. 小结

至此,我们讲解了所有有关 Code Splitting 的知识,并告诉你了一些神奇的「魔法注释」让你对分割后的代码有更多的掌控,希望你能将上面的技术灵活运用在你的项目中,开发出更加激动人心,如丝般顺滑的应用!

Good Luck!🙌

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Dax Dax
4年前
前端性能优化
前端性能优化1、减少资源的请求次数和大小压缩合并js和css文件,减少http请求次数和请求资源的大小;在项目中使用webpackglup等打包编译工具2、尽量使用字体图标或者svg图标代替传统的png(jpg)图渲染更快,减少代码体积,且放大不会出现变形等3、使用图片懒加载目的是减少页面第一次加载的http请求次数,实现思路:
爱丽丝13 爱丽丝13
4年前
聊聊前端性能优化
为什么要做性能优化?性能优化有多重要?网站的性能对于用户的留存率、转化率有很大的影响,直白的说,提高网站的性能可以直接提高网站带来的收益。性能优化的分类前端的性能优化主要分为两类:1.加载时优化;2.运行时优化;例如压缩文件、使用CDN加载静态资源属于加载时优化;及时的解绑事件、减少DOM的操作属于运行时优化。吉
巴拉米 巴拉米
4年前
前端性能优化
为什么要做性能优化?性能优化有多重要?网站的性能对于用户的留存率、转化率有很大的影响,直白的说,提高网站的性能可以直接提高网站带来的收益。性能优化的分类前端的性能优化主要分为两类:1.加载时优化;2.运行时优化;例如压缩文件、使用CDN加载静态资源属于加载时优化;及时的解绑事件、减少DOM的操作属于运行时优化。吉
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
3年前
KO
KOKO是一个基于Webpack开发的快速开始Web开发的脚手架工具,具有以下特性:可以当做一个Webpack配置种子来使用,无需二次配置、开箱即用自动支持多页应用(可选)Vue单文件组件的开发方式资源分块加载,内联和异步加载方式管理,低成本实现首屏优化支
Stella981 Stella981
3年前
Bypass ngx_lua_waf SQL注入防御(多姿势)
0x00前言ngx\_lua\_waf是一款基于ngx\_lua的web应用防火墙,使用简单,高性能、轻量级。默认防御规则在wafconf目录中,摘录几条核心的SQL注入防御规则:select.(from|limit)(?:(union(.?)select))(?:from\Winformation_schema\W)这边
Stella981 Stella981
3年前
JavaScript 性能优化技巧分享
JavaScript作为当前最为常见的直译式脚本语言,已经广泛应用于Web应用开发中。为了提高Web应用的性能,从JavaScript的性能优化方向入手,会是一个很好的选择。本文从加载、上下文、解析、编译、执行和捆绑等多个方面来讲解JavaScript的性能优化技巧,以便让更多的前端开发人员掌握这方面知识。什么是高性能的JavaScr
陈杨 陈杨
2个月前
鸿蒙5开发宝藏案例分享---Web开发优化案例分享
好的,老铁们!鸿蒙官方文档里其实藏着不少“硬核”性能优化案例,我之前愣是没发现,感觉错过了一个亿!特别是关于ArkWeb(方舟Web)组件加载Web页面的优化技巧,简直是提升应用流畅度的神兵利器。官方文档写得比较“正经”,我这就把它掰开了、揉碎了,加上我自