你的项目不知如何优化?教你 1 招提高代码使用率

简介: 导读:经常有同学感叹不知道怎么优化项目,大家不妨尝试下在项目中引入代码分割的方式提升性能。

默认标题_公众号封面首图_2020-05-26-0 (3).png
作者|李斌(空堂)
出品|阿里巴巴新零售淘系技术部

导读:经常有同学感叹不知道怎么优化项目,大家不妨尝试下在项目中引入代码分割的方式提升性能。

Web 应用性能优化的关键

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

那么当所需加载的资源数量到达多少或资源大小小于多少,我们才可以自信地宣称我们的 Web 应用拥有出色的性能呢?

下面是我给出的一个参考值,该参考值考虑到了移动端与国外等多种访问环境:

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

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

如何查看代码利用率

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

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

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

1、打开 Chrome Dev Tool;

2、按下 Cmd + Shift + P or Ctrl + Shift + P ;

3、输入 Coverage,并选择第一个出现的选项;

image.png

4、点击面板上的 reload 按钮,查看整个应用 JavaScript 的代码利用率;

image.png

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

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

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

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

▐ 代码分割的原理

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

image.png

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

▐ 代码分割的类型

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

静态代码分割

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

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

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)  })})
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. 路由:既然用户不会一下子看到所有页面,那么只把当前页面相关资源给用户就是个明智的做法;

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

动态代码分割

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

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

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. 为了方便 :本质上,你可以用静态代码分割代替「动态」代码分割,但是后者比前者拥有更少的代码量;

魔术注释

魔术注释是由 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 而言非常方便。

▐ Webpack Modes

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

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

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

▐ Prefetch or Preload

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

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

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

小结

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

关注「淘系技术」微信公众号,一个有温度有内容的技术社区~

公众号二维码.jpg

相关文章
|
编解码
如何让开源鸿蒙系统适配其它机型?
如何让开源鸿蒙系统适配其它机型?
844 0
|
前端开发 关系型数据库 MySQL
TDesign中后台管理系统-访问后端服务
TDesign中后台管理系统-访问后端服务
|
4月前
|
Linux API 数据安全/隐私保护
告别上下文污染!OpenClaw多Agent实战(阿里云+本地)+ 免费API配置+隔离技巧
2026年,OpenClaw的实战价值已从“单一智能体执行”升级为“多智能体协同”。很多用户在长期使用中发现,将写作、客服、自动化、资料整理等所有任务塞进一个Agent,会导致上下文串味、Token消耗激增、记忆污染等问题——简单的咨询要拖着海量无关历史对话,专业场景的回复混入日常聊天语气。
1709 8
|
关系型数据库 MySQL
Mysql union 联合查询
Mysql union 联合查询
602 0
|
7月前
|
机器学习/深度学习 弹性计算 负载均衡
阿里云服务器新人优惠购买政策详解:免费试用、优惠券、云服务器活动价格说明
本文梳理最新阿里云针对新用户推出的云产品优惠政策,包括免费试用、特定服务器价格政策、实例折扣及优惠券等内容,同时说明相关申请流程与适用范围,为新用户了解并使用优惠提供参考。
|
8月前
|
机器学习/深度学习 搜索推荐 算法
Python大数据驱动的图书推荐与分析系统:从数据到智能决策的实践探索
在信息爆炸时代,图书推荐系统利用Python大数据技术,融合用户行为、图书元数据与外部动态,构建“内容+协同+深度学习”混合模型,实现精准个性化推荐。通过三层架构与算法创新,破解冷启动、小众书推荐等难题,助力每本书找到真正读者。
590 0
|
人工智能 自然语言处理 IDE
技术赋能新维度,灵码进化新突破:通义灵码2.5新功能尝鲜及深度评测
通义灵码是阿里云推出的基于通义大模型的智能编程助手,作为首款全栈智能辅助的国产编码工具,它为开发者提供“第二大脑”,并重构团队协作效能。2.5版本新增智能体模式,支持Qwen3系列模型,具备自主决策、工程感知和记忆能力,集成3000+MCP工具。其优势包括多模式对话体验、上下文增强、全流程工具链支持及个性化记忆功能,但仍存在上下文管理、权限控制和语言支持等方面的改进空间。此次更新标志着AI辅助开发进入全链路智能化新纪元,成为开发者真正的“结对编程伙伴”。
2104 36
|
Android开发 Swift iOS开发
iOS和安卓作为主流操作系统,开发者需了解两者差异以提高效率并确保优质用户体验。
【10月更文挑战第1天】随着移动互联网的发展,智能手机成为生活必需品,iOS和安卓作为主流操作系统,各有庞大的用户群。开发者需了解两者差异以提高效率并确保优质用户体验。iOS使用Swift或Objective-C开发,强调简洁直观的设计;安卓则采用Java或Kotlin,注重层次与动画。Swift和Kotlin均有现代编程特性。此外,iOS设备更易优化,而安卓需考虑更多兼容性问题。iOS应用仅能通过App Store发布,审核严格;安卓除Google Play外还可通过第三方市场发布,审核较宽松。开发者应根据需求选择合适平台,提供最佳应用体验。
518 3
|
存储 算法 NoSQL
LeetCode第73题矩阵置零
文章介绍了LeetCode第73题"矩阵置零"的解法,通过使用矩阵的第一行和第一列作为标记来记录哪些行或列需要置零,从而在不增加额外空间的情况下解决问题。
LeetCode第73题矩阵置零
|
容器 Docker Linux
如何将当前用户添加到Docker组?
【7月更文挑战第25天】
4642 2
如何将当前用户添加到Docker组?