+++
vue3性能优化
前言
基本完成集市新前端, 准备部署了. 在测试环境里发现移动端评分过低, 就找了一些视频来学vue的性能优化.
1. 使用异步组件(按需/懒加载)
做集市前端的时候为了适配移动端用了pc端没有用到的组件, 当然也有pc端用到了但是移动端没有用到的组件, 虽然这样做页面能正常加载, 但是会增长加载时间. 这个时候就需要用到异步组件了.
<template>
<div>
<button @click="showA=!showA">
渲染组件A
</button>
<ComponentA v-if="showA" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import ComponentA from '@/components/ComponentA.vue'
const showA = ref(false)
</script>
<template>
<div>
<button @click="showA=!showA">
渲染组件A
</button>
<ComponentA v-if="showA" />
</div>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
const ComponentA = defineAsyncComponent(()=>import('@/components/ComponentA.vue'))
const showA = ref(false)
</script>
对于一般组件的话, 在开发者工具里刷新一下, 再点击渲染组件A, 会发现网络里不会多一个对组件的请求. 在过去的组件里可以看到已经对这个组件请求过了.
对于异步组件的话, 不点击是不会拿到这个组件的, 点击之后会发现多了一个对组件的请求. 这样子的话就不会在一开始就加载所有组件, 只有需要用的时候才会加载组件.
对于刚才的移动端和pc端使用组件不同, 就可以用这个解决. 移动端/pc端用不到的组件就不会加载, 就算用到了也可以用到的的时候再加载.
defineAsyncComponent还提供了一些选项可以使用
const ComponentA = defineAsyncComponent({
loader: () => import('@/components/ComponentA.vue'),// 这里传入一个返回promise的函数就可以了, import()返回的就是promise
loadingComponent: LoadingComponent, // 替换为你的加载组件
delay: 200, // 延迟显示加载组件的时间(毫秒)
errorComponent: ErrorComponent, // 替换为你的错误组件
timeout: 3000 // 超时时间(毫秒)
})
有加载组件的时候它会先展示, 0.2s后替换为异步组件, 如果加载好了的话. 当3s过去后还没加载好或者promise拒绝了, 那就渲染错误组件.
2. 使用shallowRef
对于一些不需要改变或者只有少数情况才需要改变的较大的响应式变量使用shallowRef而不是ref.
<template>
<div id="root">
<button @click="loadData">加载!</button>
<span>{{ data.length }}</span>
</div>
</template>
<script setup>
import { ref, shallowRef } from 'vue'
// const data = ref([])
const data = shallowRef([])
const getData = () => {
const result = []
for(let i = 0; i < 10000000; ++i){
result.push({
id: i,
name: `name${i}`,
address: {
city: `city${i}`,
street: `street${i}`,
}
})
}
return result
}
const loadData = () => {
const time = Date.now()
data.value = Object.freeze(getData())
console.log('loadData time:', Date.now() - time)
}
</script>
在这份代码里如果分别尝试shallowRef和ref,Object.freeze会发现, shallowRef加载数据只花了4230ms, ref花了31398ms. 在性能里应该可以发现, 循环使用的时间没有设置成响应式变量的时间长.
在vue中, ref会将一个对象转为深层响应式变量, shallowRef只会转为浅层
const data = ref([])
data.value.push(1) // 会被检测到
const shallowData = shallowRef([])
shallowData.value.push(1) // 不会检测到
shallowData.value = [...shallowData.value,1] // 这才会被检测到变化
这里Object.freeze让对象不可改变, 也能优化性能. ref+Object.freeze消耗的时间是14827ms, shallowRef+Object.freeze消耗的时间是3684ms.
这里是比较极端的情况, 不过确实是一个可以优化的地方, 对于大型数据在可以使用shallowRef的情况下优先考虑shallowRef.
这次前端最大的优化基本只有异步组件, 首屏时间减少了一两秒, 移动端性能评分总算打上90了. 其它的只是一些小的地方, 甚至有些人觉得毫无意义的东西, 不过还是写下来吧.
检查代码
减少重复代码, 去除无用的代码/第三方库.
在开发者工具中有个叫覆盖率的工具, 使用它可以查看未使用但是加载了的js/css, 可以根据这个来检查有没有可去除的代码.
图片压缩
用一些在线平台, 比如https://bigconvert.11zon.com/zh-cn/image-to-webp/或者工具将图片转为webp格式可以有效的减小体积.
没多少意义的优化
对于不需要响应式变量的地方, 比如说某种情况下的搜索栏、输入框, 只关注里面的数据而不关注它的变化, 那么没有必要使用v-model来绑定里面的数据.
当然, 使用v-model一般情况下不会有任何问题, 基本不会造成性能问题. 如果跟原生一样表单提交反而有点诡异.