如何优化 Vue App?

大家好,这里是大家的林语冰。坚持阅读,自律打卡,每天一次,进步一点

免责声明

本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考。英文原味版请传送 Optimizing A Vue App

本期共享的是 ------ 在构建 Web App 时优先考虑性能可以改善 UX(用户体验),有助于它们被更多用户使用。本文将引导我们深度学习若干前端优化技巧,让 Vue App 尽量保持高效。

SPA(单页应用程序)在处理实时动态数据时,可以提供丰富的交互式 UX。但 SPA 也可能很笨重臃肿且性能尴尬。我们会介绍若干前端优化技巧,保持 Vue App 相对精简,并且按需交付我们需要的 JS。

粉丝请注意:我们假设您对 Vue 及其组合式 API 有基本认知,但无论您选择哪个框架,都能收获某些有用的东东。

框架的选择

我们选择的 JS 框架是 Vue,部分原因是因为它是我最得心应手的框架。此前,与 React 相比,Vue 的整体包体积更小。虽然但是,自从最近的 React 更新以来,平衡似乎已经向 React 倾斜。这并非兹事体大,因为我们会在本文中了解如何按需导入内容。这两个框架都有优秀的文档和庞大的开发者生态系统,这是另一个考虑因素。

作为演示各种优化的示例,本人构建了一个简单的 Vue App,它从 API 请求数据,并使用 D3.js 渲染若干图表。

我们使用一种人气爆棚的构建工具 Vite 来打包 App,但我们在此介绍的所有优化都适用于大家选择的任何打包程序。

诉诸构建工具树摇优化、压缩和简化

按需发送代码、且开箱即用,是一种优秀实践。Vite 构建时会删除未用的 JS 代码(tree shaking,树摇优化)。Vite 还会压缩打包结果,且可以配置为使用 Gzip/Brotli 压缩输出。

除了压缩之外,我们可以发现未使用作用域提升时,打包体积比优化和压缩的版本高出约 5 倍。因此,无论您使用哪个打包器,平心而论,您可能希望确保它尽量执行优化。

即使我们总体上交付的包较小,浏览器仍然需要时间来解析和编译 JS,这可能会导致 UX 变慢。让我们看看还能做些什么来减少浏览器的工作量。

Vue 组合式 API

Vue 3 引入了组合式 API,这是一组用于创作组件的新型 API,作为选项式 API 的竞品方案。通过专用组合式 API,我们可以按需导入 Vue 函数,而不是整个包。它还使我们能够使用组合式函数编写更多可复用代码。使用组合式 API 编写的代码更适合压缩,且整个 App 更容易 tree-shaking

粉丝请注意:如果您使用的是旧版的 Vue,您仍然可以使用组合式 API:它已向后移植到 Vue 2.7,且有一个适用于旧版的官方插件。

依赖导入

一个关键目标是减少客户端下载的初始 JS 包的大小。D3 可用于数据可视化,这是一个大型库,范围广泛。虽然但是,D3 库中有的模块我们根本不需要。如果我们检查整个 D3 包,我们可以发现我们的 App 使用了不到一半的可用模块,甚至可能没有使用这些模块中的所有功能。

保持包体积尽量小的最简单方法之一就是按需导入模块。

以 D3 的 selectAll 函数为例。我们可以从 d3-selection 模块导入我们需要的函数,而不是使用默认导入:

js 复制代码
// 优化前:
import * as d3 from 'd3'

// 优化后:
import { selectAll } from 'd3-selection'

诉诸动态导入分割代码

动态导入允许我们将模块精准导入到代码中需要的位置,而不是在文件顶部静态导入模块。

js 复制代码
// 优化前:
import { Auth } from '@aws-amplify/auth'

const user = Auth.currentAuthenticatedUser()

// 优化后:
import('@aws-amplify/auth').then(({ Auth }) => {
  const user = Auth.currentAuthenticatedUser()
})

这意味着,该模块会被分割成一个单独的 JS 包或"组块",当且仅当需要时,浏览器才会下载该模块。此外,浏览器可以缓存这些依赖,这些依赖的更改频率可能低于 App 其余部分的代码。

使用 Vue Router 路由懒加载

我们的 App 使用 Vue Router 导航。与动态导入类似,我们可以懒加载路由组件,因此当且仅当用户导航到该路由时,才会导入它们及其相关依赖。

index/router.js 文件中:

js 复制代码
// 优化前:
import Home from "../routes/Home.vue";
import About = "../routes/About.vue";

// 优化后:
const Home = () => import("../routes/Home.vue");
const About = () => import("../routes/About.vue");

const routes = [
  {
    name: "home",
    path: "/",
    component: Home,
  },
  {
    name: "about",
    path: "/about",
    component: About,
  },
];

当且仅当用户单击"About"链接、并导航到该路由时,才会加载"About"路由的代码。

异步组件

除了懒加载每个路由之外,我们还可以使用 Vue 的 defineAsyncComponent 方法懒加载单个组件。

js 复制代码
const KPIComponent = defineAsyncComponent(() => import('../components/KPI.vue'))

这意味着,KPIComponent 的代码会动态导入。我们还可以提供若干在加载或错误状态时显示的组件,如果我们正在加载一个特别大的文件,这特别有用。

拆分 API 请求

我们的 App 主要涉及数据可视化,并且严重依赖于从服务器请求海量数据。其中某些请求可能慢如龟速,因为服务器必须对数据执行大量计算。在最初的原型中,我们对每个路由的 REST API 发出一个请求。不幸的是,这导致用户必须等待很久,有时高达 10 秒,在 App 成功接收数据、并开始渲染可视化之前,用户只能看到加载组件。

我们决定将 API 拆分为多个端点,并对每个小部件发出请求。虽然这可能会增加整体响应时间,但这意味着,App 可以更快使用,因为用户在等待其他页面时,会看到页面的部分内容已渲染。此外,可能发生的任何错误都会被局部化,而页面的其余部分仍然可用。

条件加载组件

现在我们可以将其与异步组件结合起来,这样当且仅当收到服务器的成功响应时,才会加载组件。这里我们正在请求数据,然后在 fetch 函数成功返回时导入组件:

此模式可以扩展到 App 中在用户交互时渲染组件的任意位置。举个栗子,当且仅当用户单击"地图"选项卡时,我们才加载地图组件及其依赖。这称为交互导入。

CSS

如果运行示例代码,我们会看到单击"位置"导航链接会加载地图组件。除了动态导入 JS 模块之外,在组件的 <style> 块中导入依赖也会延迟加载 CSS:

html 复制代码
// MapView.vue
<style>
  @import '../../node_modules/leaflet/dist/leaflet.css';

  .map-wrapper {
    aspect-ratio: 16 / 9;
  }
</style>

中止 API 请求

在一个有大量 API 请求的页面上,如果用户在所有请求完成之前就跑路了,那会怎样?我们可能不希望这些请求继续在后台运行,降低 UX。

我们可以使用 AbortController 接口,它使我们能够按需中止 API 请求。

setup 函数中,我们创建一个 controller,并将其 signal 传递到 fetch 请求参数中:

js 复制代码
setup(props) {
    const controller = new AbortController();

    const loadComponent = () => {
      return fetch(url, { signal: controller.signal })
        .then((response) => response.json())
        .then((response) => (data.value = response))
        .then(importFunction)
        .catch((e) => console.error(e))
        .finally(() => (loading.value = false));
        };
}

然后,我们使用 Vue 的 onBeforeUnmount 函数在组件卸载前中止请求:

js 复制代码
onBeforeUnmount(() => controller.abort())

如果我们在请求完成之前运行项目并导航到另一个页面,我们会看到控制台中打印的错误,表明请求已中止。

完结撒花

在构建 Web App 时优先考虑性能可以改善 UX,并有助于它们被更多用户使用。SPA 可以很好地工作,但它们也可能成为性能瓶颈。因此,让我们尝试将它们构建得更好。

本期话题是 ------ 你使用过哪些 Vue 优化的终极小技巧?

欢迎在本文下方群聊自由言论,文明共享。谢谢大家的点赞,掰掰~

《前端 9 点半》每日更新,坚持阅读,自律打卡,每天一次,进步一点

相关推荐
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax