如何优化 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 点半》每日更新,坚持阅读,自律打卡,每天一次,进步一点

相关推荐
Myli_ing31 分钟前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风34 分钟前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave41 分钟前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟43 分钟前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾1 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧1 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
chusheng18401 小时前
Java项目-基于SpringBoot+vue的租房网站设计与实现
java·vue.js·spring boot·租房·租房网站
asleep7012 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm2 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架