通过Vue看性能优化
在聊Vue优化之前,我们必须先知道一个共识:这些脱颖而出的前端框架自身已经做了很多性能优化。我们要做的无非有两件事:了解知晓已经做的的和具体场景的再优化。
重要的事情说三遍
- Web的性能优化无非就两个方面,一方面是首屏加载性能,另一方面是更新性能。
- Web的性能优化无非就两个方面,一方面是首屏加载性能,另一方面是更新性能。
- Web的性能优化无非就两个方面,一方面是首屏加载性能,另一方面是更新性能。
先来了解vue的多种使用场景
根据vue的官方说法,用vue的考虑性能优化的时候要根据vue使用的场景,那我们就先来看看Vue的多种使用场景。
作为独立的JS脚本:
这个怎么理解,如果到具体场景,笔者记得在之前写项目的时候要封装一个request.js
的脚本文件,在当时为了响应式存储token,就引入了vue的ref,并且相应拦截器模块,是引入了Element
的提示窗。- 作为
Web Components
组件:这个大家就非常熟悉了,.vue
后缀名的文件通常是指用vue书写的组件,可以来拿复用,这也是现代前端框架的显著特性。 作为单页面应用SPA
: 单页面SPA,通常只是只有一个HTML文件组成的页面,抽象的说,一些应用在前端需要丰富的交互性、较深的会话和复杂的状态逻辑,如果原生开发就会显得吃力,这对于逻辑代码的设计非常不友好(当然不排除你是大佬)。全栈/SSR
: SSR的最大作用就是上面说的极大的缩短了首屏的加载和SEO优化。比如说原先,浏览器先会加载HTML,然后加载JS,解析完JS之后才会把内容渲染在页面上,这就大大的增加了FCP时间。SSR的原理就是直接在服务端把Vue应用拼接成一个HTML字符串,最后返回给浏览器,就可以实现直接渲染。- 还应用在静态页面(此处指不需要更改数据,纯纯展示)的SSG,可以大致理解为所有内容在服务端渲染。
如果不局限在浏览器,根据官网解释:
可能这就是“渐进式”框架的多用之处,通吃?野心磅礴?那回归正题,这么多应用也就相应带来了不同的使用场景和优化问题,我们主要来讨论Vue在Web的性能优化。
Vue做的性能优化
From SPA to SSR
正如上面所说,选择正确的架构很重要。SPA首屏加载速度会比较慢,可选用服务端渲染的方式来加快渲染,在服务端通过字符串的拼接返回给客户端一个完整的HTML文件,就能大大缩短首屏加载速度。
另外Vue对于必须是SPA的主页面给的建议是:单独部署。或者对于静态页面,建议SSG方式部署。
从文件体积做的Tree-shaking优化
借助这个思路,扩展一下:如果我在框架里面把没有用到的文件(不仅仅局限于JS文件)压缩不加载就能做到较少文件体积的作用。这无疑优化了加载速度。
那么Tree-Shaking具体是怎么做的呢?
-
对于代码模块的优化
- 现代打包工具如Vite,可以把Vue的许多API做Tree-Shake优化。比如,书写且声明的组件,没有用到就不打包它。直接替你移除了没用的模块代码。
-
对于依赖包的优化
-
我去看了Vue官文,大概是这么解释的:
- 一些包在引入过程中可能会过大,这个无疑拉慢了加载速度。Tree-shaking会对引入的包中但是没有用到的 API 给剔除掉(不打包)。
-
从文件结构做的代码分割优化
和懒加载一个思路,用到即加载。
- Vue在这方面做优化是靠构建工具(Vite、Webpack)完成的,思路概述:页面加载时需要的功能可以理解加载,额外需要的块只在需要的时候才加载,从这方面提高了Web性能。
- 对于Vue-Router,Vue官方比较推荐使用异步组件作为路由组件。可以通过
defineAsyncComponents
API实现懒加载。
import { defineAsyncComponent } from 'vue'
// 会为 Foo.vue 及其依赖创建单独的一个块
// 它只会按需加载
//(即该异步组件在页面中被渲染时)
const Foo = defineAsyncComponent(() => import('./Foo.vue'))
大型虚拟列表
之前的文章讲过,原生JS在直接渲染十万条数据的时候都会卡住或者速度非常慢,更不用说框架。我们可是使用虚拟列表来实现,思路是:只渲染当前被查看的内容。
Vue针对虚拟列表的优化大概是提供了现有社区库:
减少响应性能开销
从Vue3开始,对于数据的响应式原理,虽然从definProxy换成了Proxy让速度变得更快,但是当遇到很多数据时,对于性能的开销仍不可小觑。于是Vue便提出了新的API来应对这种场景。
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)
]
使用shallow过的响应式数据,只会在更改跟数据的时候触发更新。
转载自:https://juejin.cn/post/7380210630887555106