当前位置: 首页 > news >正文

Vue3 高级性能优化

Vue3 高级性能优化

  • 1. Lighthouse(谷歌浏览器性能分析工具)
  • 2. 代码分析插件(rollup-plugin-visualizer)
  • 3. Vite 配置优化配置
  • 4. PWA 离线存储插件(vite-plugin-pwa)
  • 5. 与 Vue 本身相关的优化
    • 5.1 页面加载优化
      • 5.1.1 包体积与 tree-shaking 优化
      • 5.1.2 代码分割
      • 5.1.3 选择合适的架构(暂时不做探究)
    • 5.2 更新优化
      • 5.2.1 Prop 稳定性(尽量让子组件的 prop 不发生改动)
      • 5.2.2 v-once(元素和组件之渲染一次,跳过更新)
      • 5.2.3 v-meno(有条件的跳过更新,3.2 以上版本可用)
      • 5.2.4 计算属性稳定性(返回对象时对比具体属性,再决定是否改变)
    • 5.3 通用优化
      • 5.3.1 v-lazy(图片懒加载)
      • 5.3.2 大型虚拟列表(只渲染视口部分)
      • 5.3.3 减少大型不可变数据的响应性开销(使用 shallowRef() 和 shallowReactive())
      • 5.3.4 避免不必要的组件抽象
  • 6. 题外话

1. Lighthouse(谷歌浏览器性能分析工具)

Lighthouse 是谷歌浏览器中 devtools 中的一个性能分析工具。
在这里插入图片描述
选择Desktop(桌面端),点击 analyze page load,即可看到页面加载的一个跑分情况。
在这里插入图片描述
主要参数介绍:
(1)First Contentful Paint(首屏内容绘制,FCP):首次内容绘制的时间,浏览器第一次绘制DOM相关的内容,也是用户第一次看到页面内容的时间。

(2)Total Blocking Time(总阻塞时间,TBT):记录了首次内容绘制到用户可交互之间的时间,这段时间内,主进程被阻塞,会阻碍用户的交互,页面点击无反应。

(3)Speed Index(速度指数):页面各个可见部分的显示平均时间,当我们的页面上存在轮播图或者需要从后端获取内容加载时,这个数据会被影响到。

(4)Largest Contentful Paint(最大内容绘制):最大内容绘制时间,页面最大的元素绘制完成的时间。

(5)Cumulative Layout Shift(计算布局偏移):计算布局偏移值得分,会比较两次渲染帧的内容偏移情况,这个值通常为0。否则,可能出现用户想点击A按钮,但下一帧中,A按钮被挤到旁边,导致用户实际点击了B按钮。

2. 代码分析插件(rollup-plugin-visualizer)

(1)下载 代码分析插件:

npm i rollup-plugin-visualizer

(2)执行 npm run build ,会自动跳出代码分析页面。
在这里插入图片描述

可以看到,因为项目使用了完整引入 ELement Plus,所以项目打包内容十分庞大,在 1.53MB 左右。

据此,我们可以将 Element Plus 修改为按需引入,方式可参照《Vue3 组件库 Element Plus》的 2.2.2 自动按需引入 小节。

在这里插入图片描述
假设我们项目只使用了el-button组件,可以看到,重新npm run build后,element-plus 的打包体积大大减小,只剩下了 18.36KB。

3. Vite 配置优化配置

vite.config.js

build: {chunkSizeWarningLimit: 2000, // 警告单个文件大小限制,默认2000kbcssCodeSplit: true, //css 拆分sourcemap: false, //不生成sourcemapminify: false, //是否禁用最小化混淆,esbuild打包速度最快,terser打包体积最小。assetsInlineLimit: 5000 //小于该值 图片将打包成Base64 },

在这里插入图片描述

4. PWA 离线存储插件(vite-plugin-pwa)

(1)下载插件

npm i vite-plugin-pwa

(2)在 vite.config.js 中进行配置:

import { VitePWA } from 'vite-plugin-pwa'
VitePWA({workbox: {cacheId: "XIaoman",//缓存名称runtimeCaching: [{urlPattern: /.*\.js.*/, //缓存文件handler: "StaleWhileRevalidate", //重新验证时失效options: {cacheName: "XiaoMan-js", //缓存js,名称expiration: {maxEntries: 30, //缓存文件数量 LRU算法maxAgeSeconds: 30 * 24 * 60 * 60 //缓存有效期}}}]},
})

(3)执行 npm run build 进行打包,生成 sw.js 文件。
在这里插入图片描述

(4)全局下载 http-server:

npm i -g http-server

(5)进入打包后的 dist 文件夹,本地打开:

cd dist
http-server -p 8000

在这里插入图片描述
在这里插入图片描述

5. 与 Vue 本身相关的优化

Vue 性能优化主要是在两个方面:
(1)页面加载性能。首次访问时,应用展示出内容与达到可交互状态的速度。这通常会用 Google 所定义的一系列 Web 指标 (Web Vitals) 来进行衡量。比如First Contentful Paint(首屏内容绘制时间),详情参照 1. Lighthouse(谷歌浏览器性能分析工具) 小节;

(2)更新性能。应用响应用户输入更新的速度。比如当用户在搜索框中输入时结果列表的更新速度,或者用户在一个单页面应用 (SPA) 中点击链接跳转页面时的切换速度。

5.1 页面加载优化

5.1.1 包体积与 tree-shaking 优化

(1)按需引入。
采用现代化的打包工具,会支持自动化的tree-shaking(没用过的代码不会被打包)。所以最好许多函数库或者组件库采用按需引入的方式会更合适。

(2)避免引入过重依赖。
比如 lodash 默认是 commonjs 版本,对 tree-shaking 很不友好,所以可以使用 lodash-es (ES 版本)进行替代,支持tree-shaking。

5.1.2 代码分割

代码分割是指构建工具将构建后的 JavaScript 包拆分为多个较小的,可以按需或并行加载的文件。通过适当的代码分割,页面加载时需要的功能可以立即下载,而额外的块只在需要时才加载,从而提高性能。

像 Rollup (Vite 就是基于它之上开发的) 或者 webpack 这样的打包工具可以通过分析 ESM 动态导入的语法来自动进行代码分割:

// lazy.js 及其依赖会被拆分到一个单独的文件中
// 并只在 `loadLazy()` 调用时才加载
function loadLazy() {return import('./lazy.js')
}

使用懒加载 + 异步组件

懒加载对于页面初次加载优化极大。可与 Vue 异步组件进行搭配使用,为组件创建分离的代码块:

import { defineAsyncComponent } from 'vue'// 会为 Foo.vue 及其依赖创建单独的一个块
// 它只会按需加载
// (即该异步组件在页面中被渲染时)
const Foo = defineAsyncComponent(() => import('./Foo.vue'))

5.1.3 选择合适的架构(暂时不做探究)

如果你的用例对页面加载性能很敏感,请避免将其部署为纯客户端的 SPA,而是让服务器直接发送包含用户想要查看的内容的 HTML 代码。纯客户端渲染存在首屏加载缓慢的问题,这可以通过服务器端渲染 (SSR) 或静态站点生成 (SSG) 来缓解。查看 SSR 指南以了解如何使用 Vue 实现 SSR。如果应用对交互性要求不高,你还可以使用传统的后端服务器来渲染 HTML,并在客户端使用 Vue 对其进行增强。

不过,我们目前的学习进程都是纯客户端的SPA,其他架构的内容暂时不做探究。

5.2 更新优化

5.2.1 Prop 稳定性(尽量让子组件的 prop 不发生改动)

子组件中任意一个 prop 更新,整个组件就会更新。看个例子:

<ListItemv-for="item in list":id="item.id":active-id="activeId" />

我们切换列表项的 activeId 时,所有的 ListItem 组件都发生了更新,明显不合理。修改为:

<ListItemv-for="item in list":id="item.id":active="item.id === activeId" />

将对比逻辑放入到父组件中实现,active 这个 prop 每次修改,只会更新两个子组件。

5.2.2 v-once(元素和组件之渲染一次,跳过更新)

v-once 仅渲染元素和组件一次,并跳过之后的更新。

在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。

<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 带有子元素的元素 -->
<div v-once><h1>Comment</h1><p>{{msg}}</p>
</div>
<!-- 组件 -->
<MyComponent v-once :comment="msg" />
<!-- `v-for` 指令 -->
<ul><li v-for="i in list" v-once>{{i}}</li>
</ul>

5.2.3 v-meno(有条件的跳过更新,3.2 以上版本可用)

v-meno 缓存一个模板的子树。用来有条件地跳过某些大型子树或者 v-for 列表的更新。

在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。比如:

<div v-memo="[valueA, valueB]">...
</div>

如果 v-memo="[]",效果就和 v-once 一致。

5.2.4 计算属性稳定性(返回对象时对比具体属性,再决定是否改变)

const count = ref(0)
const isEven = computed(() => count.value % 2 === 0)watchEffect(() => console.log(isEven.value)) // true// 这将不会触发新的输出,因为计算属性的值依然为 `true`
count.value = 2
count.value = 4

上面代码中,只有 isEven 计算属性的返回值发生改变,才会导致 watchEffect 副作用的发生。

但是,如果前一个计算属性返回的是对象,则每次产生的都是一个新的引用值,会导致 watchEffect 副作用每次都会发生:

const computedObj = computed(() => {return {isEven: count.value % 2 === 0}
})

但是我们可以通过手动对比新旧属性值的方式进行优化:

const computedObj = computed((oldValue) => {const newValue = {isEven: count.value % 2 === 0}if (oldValue && oldValue.isEven === newValue.isEven) {return oldValue}return newValue
})

5.3 通用优化

5.3.1 v-lazy(图片懒加载)

<img v-lazy="imgUrl" >

5.3.2 大型虚拟列表(只渲染视口部分)

成千上万个列表项的列表会需要大量的 DOM,渲染全部会导致页面卡顿。

在大多数场景中,用户的屏幕尺寸只会展示这个巨大列表中的一小部分。我们可以通过列表虚拟化来提升性能,这项技术使我们只需要渲染用户视口中能看到的部分。

可使用的库:
(1)vue-virtual-scroller
(2)vue-virtual-scroll-grid
(3)vueuc/VVirtualList
(4)Element Plus 的 Virtualized Table 虚拟化表格、Tree V2 虚拟化树形控件 和 Virtualized Select 虚拟化选择器

5.3.3 减少大型不可变数据的响应性开销(使用 shallowRef() 和 shallowReactive())

使用 shallowRef() 和 shallowReactive() 来绕开深度响应,将所有深层级对象视为不可变的,并且只能通过替换整个根状态来触发更新:

const shallowArray = shallowRef([/* 巨大的列表,里面包含深层的对象 */
])// 这不会触发更新...
shallowArray.value.push(newObject)
// 这才会触发更新
shallowArray.value = [...shallowArray.value, newObject]// 这不会触发更新...
shallowArray.value[0].foo = 1
// 这才会触发更新
shallowArray.value = [{...shallowArray.value[0],foo: 1},...shallowArray.value.slice(1)
]

5.3.4 避免不必要的组件抽象

有些时候我们会去创建无渲染组件或高阶组件 (用来渲染具有额外 props 的其他组件) 来实现更好的抽象或代码组织。虽然这并没有什么问题,但请记住,组件实例比普通 DOM 节点要昂贵得多,而且为了逻辑抽象创建太多组件实例将会导致性能损失。

需要提醒的是,只减少几个组件实例对于性能不会有明显的改善,所以如果一个用于抽象的组件在应用中只会渲染几次,就不用操心去优化它了。考虑这种优化的最佳场景还是在大型列表中。想象一下一个有 100 项的列表,每项的组件都包含许多子组件。在这里去掉一个不必要的组件抽象,可能会减少数百个组件实例的无谓性能消耗。

6. 题外话

当然,性能优化是一个很大的话题,远远不是我们这篇文章所能够一下概括的。如果大家感兴趣,并且我后续有时间的话,可以专门就这一话题,再重新开一个专栏,和大家一起学习,深入探讨性能优化的各种基础知识和可能性。

如果大家有其他优化经验或者建议的话,也欢迎在评论区发表意见,共同学习😄


上一章 《webpack 从零构建 Vue3》

http://www.dtcms.com/a/596705.html

相关文章:

  • 含汞废水深度处理技术实践:Tulsimer® 树脂在聚氯乙烯行业的工程应用
  • 制作简单公司网站流程用帝国cms做的网站首页
  • Java 函数式编程 | 深入探讨其应用与优势
  • 福建整站优化企业车辆管理系统平台
  • 【多模态大模型面经】 Transformer 专题面经
  • 【微服务知识】SpringCloudGateway结合Sentinel实现服务的限流,熔断与降级
  • Python基础教学:Python中enumerate函数的使用方法-由Deepseek产生
  • 算法基础篇:(六)基础算法之双指针 —— 从暴力到高效的优化艺术
  • 家庭网络搭建网站做网站能赚钱吗 知乎
  • 江苏省住房与城乡建设厅网站首页广告网站建设报价
  • HarmonyOS状态管理精细化:控制渲染范围与变量拆分策略
  • win32k!ProcessKeyboardInputWorker函数和win32k!xxxProcessKeyEvent函数分析键盘扫描码和vk码
  • k均值,密度聚类,层次聚类三种聚类底层逻辑的区别
  • 基于微信小程序的茶叶茶具销售和管理系统(源码+论文+部署+安装)
  • INT303 Big Data Analysis 大数据分析 Pt.8 聚类
  • 4-ARM-PEG-Biotin(2)/Silane(2),特性与制备方法解析
  • 【成功案例】朗迪锋助力高校实验室数智化升级
  • 【开题答辩实录分享】以《证劵数据可视化分析项目设计与实现》为例进行答辩实录分享
  • 可信计算、TPM
  • SAP HANA 发展历史:内存计算如何重塑企业级数据平台
  • 存算一体架构在空间计算中的应用
  • docker swarm集群搭建,对比k8s
  • 为什么网站需要维护需要网站建设
  • 25年05月架构甄选范文“论多模型数据源”,软考高级,系统架构设计师论文
  • 重庆做网站公司哪家比较好图片设计在线
  • Ubuntu 上使用 VSCode 调试 C++ (CMake 项目) 指南
  • opencv 学习: 07 使用迭代器 (iterator) 遍历像素
  • Two Sigma 面经分享|智商检测级别的面试,逻辑与细节缺一不可
  • 【STM32项目开源】STM32单片机物联网门禁控制系统
  • Ubuntu 系统部署 PostgreSQL 主从复制 + 流复制(Streaming Replication)完整操作指南