Vue3项目性能优化(示例)
前言:
在该篇中你能收获到,在vue3项目中性能优化的常见技巧(初级、进阶、高级)以及优化策略。
初级:
合理使用 key 属性:
- 在使用v-for进行列表渲染时,为每个项指定唯一的key属性,例如 item.id ,key这个特殊的attribute主要作为Vue的虚拟Dom算法提示,有助于更高效的Dom重用。(这个就不用给示例了吧)
合理使用v-show替代v-if:
- 在需要频繁切换显示或隐藏的元素上,例如tab标签栏的切换,使用v-show,可以避免频繁的Dom重新创建和销毁,提高性能。
v-if
v-show
注意:同时监测4次 v-if与v-show的切换响应,可以看到v-show在频繁切换性能更胜一筹
合理使用computed计算属性:
注意:此次切换所耗时间比先前缩短了1s左右,这里还可以再优化(名为瞬移),进阶中会换另一个例子讲。
使用动态组件:
- 比如 Tab 界面,空间有限就不用vue3官网的例子,你们可以自己去里面的演练场试试。
<template>
<div class="home">
<div>
<button @click="activeTab = 'tab1'">Tab 1</button>
<button @click="activeTab = 'tab2'">Tab 2</button>
<button @click="activeTab = 'tab3'">Tab 3</button>
</div>
<component :is="activeTab">
<p v-if="activeTab === 'tab1'">Content for tab 1.</p>
<p v-if="activeTab === 'tab2'">Content for tab 2.</p>
<p v-if="activeTab === 'tab3'">Content for tab 3.</p>
</component>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const activeTab = ref('tab1')
</script>
使用 keep-alive 内置组件:
- 在多个组件间动态切换时缓存被移除的组件实例(当数据没有更新时,视图保持鲜活),这对性能有很大提升。keep-alive演练场
路由懒加载:
图片优化:
- 选择合适的图像格式如(Webp)
- 懒加载策略,vue2vue-lazyload跟vue3vue3-lazyload有所不同
按需加载第三方库(请不要信任不靠谱的第三方库):
- 这个看你安装的这个第三方库文档的按需引入配置就行
进阶:
灵活使用响应式:
继 ref() 和 reactive() 这两个API,Vue3新增了 shallowRef() 和 shallowReactive() 用于对大型数据结构的性能优化的API,目前国内的博客或者视频教程对此还没有讲到点上,我也没有思路如何调试验证,所幸,在stackoverflow上找到了答案,下面我将总结归纳vuejs3 - the difference between shallowReactive and shallowRef in vue3? - Stack Overflow
根据打印的结果,可以看见
当我改动了浅层x或者嵌套y.a,reactive均触发了更新。这是因为reactive会将对象进行深度包裹,使所有嵌套属性都具有响应式能力,这在大型数据结构中且只需改动浅层数据会有损性能;
当我改动了浅层x,shallowReactive也触发了更新。这是因为shallowReactive和shallowRef都不能自动解包嵌套的ref,只有shallowReactive对浅层的ref进行自动解包为内部的具体值,而不再保持ref类型,因此也不用.value去访问值,shalllowReactive只对浅层属性具有响应性;
在我的例子中,ref和shallowRef都没有触发更新
ref会对整个对象进行包装,并将其设置为一个
ref
类型的值。当修改整个对象时,才会触发更新。例如,r1.value = { x: 2 }
会触发更新,而r1.value.x = 2
则不会触发更新。
shallowRef
只包裹对象的顶层属性。当修改这些顶层属性时,会触发更新。但是,如果修改了嵌套属性,shallowRef
不会触发更新。例如,s1.value = { x: 2 }
会触发更新,而s1.value.x = 2
以及s1.value.y.a = 2
不会触发更新。需要注意的是:浅层数据结构应该只用于组件中的根级状态
使用Vue3的Teleport内置组件(瞬移/传送):
这部分是我这篇博客花最多时间的地方,可是最后测试性能对比,在使用teleport将tab页下的内容传送出去,比原来那样性能更差,而且会使得tabs栏失去粘性,不过可以手动添加粘性样式。。
我起初想法是利用teleport的特性,将tab页面下的内容放置在某个地方,使得更加灵活,盒子不用嵌套多层
但是,这无疑是增加了Dom操作。。
原来没加teleport
加了teleport
结果:渲染时间差别不大,但是增加了阻塞进程 ,teleport内置组件的用法。
虽然没能达到理想状态,但还是要勇于思考和尝试。
用自定义指令去实现防抖节流(Vue3+TS,支持所有eventType,建议收藏哈哈哈):
防抖节流一直是性能优化的有效手段,减轻服务器的负担,也可以减少后端老哥的问候;
一步步来,先看看我是在哪个文件封装的:
在src/utils/index.ts暴露modules中instruction.ts(指令)文件中的所有方法,接下来看看代码
// instruction.ts
// 防抖指令封装
export const useDebounceDirective = (delay: number) => {
return {
beforeMount(el: HTMLElement, binding: any) {
let timer: number;
el.addEventListener(binding.arg, () => {
clearTimeout(timer);
timer = setTimeout(() => {
binding.value();
}, delay);
});
}
};
};
// 节流指令封装
export const useThrottleDirective = (delay: number) => {
return {
beforeMount(el: HTMLElement, binding: any) {
let throttled = false;
el.addEventListener(binding.arg, () => {
if (!throttled) {
throttled = true;
setTimeout(() => {
binding.value();
throttled = false;
}, delay);
}
});
}
};
};
// utils/index.ts
export * from './modules/instruction';
组件中的代码
<template>
<div>
<input v-debounce:input="onInput" placeholder="输入内容"><br>
<button v-debounce:click="onClickDebounce">点击防抖按钮</button>
<button v-throttle:click="onClickThrottle">点击节流按钮</button>
</div>
</template>
<script lang="ts" setup>
function onInput() {
console.log('输入框防抖事件');
}
function onClickDebounce() {
console.log('防抖事件');
}
function onClickThrottle() {
console.log('节流事件')
}
</script>
<script lang="ts">
import { useDebounceDirective, useThrottleDirective } from "@/utils";
export default {
directives: {
debounce: useDebounceDirective(500),
throttle: useThrottleDirective(1000)
}
}
</script>
<style scoped>
</style>
亲测好用,而且很符合真实项目的需求(本来要插入有音频的视频。最后发现点击进入该博客会立刻播放音频,可能导致用户懵圈,平台还没解决该问题)
控制台打印效果
进行网络优化,让chatgpt总结一下:
缓存策略:根据具体情况设置以下 HTTP 头部字段来启用浏览器缓存和服务端缓存:
Cache-Control:用于指定资源的缓存行为。示例:
Cache-Control: public, max-age=3600
:允许公共缓存,并设置最大缓存时间为 3600 秒(1 小时)。Cache-Control: private, no-cache
:不允许公共缓存,每次都需要与服务器确认。Cache-Control: no-store
:不允许任何形式的缓存。
ETag 和 If-None-Match:用于实现基于内容的缓存验证。示例:
ETag: "abcd1234"
:将唯一的标识符分配给资源的版本,并在后续请求中发送给客户端。- 客户端发送请求时,通过
If-None-Match
头部字段将之前的 ETag 值发送给服务器,如果资源未发生变化,服务器可返回状态码 304 Not Modified。
Last-Modified 和 If-Modified-Since:用于实现基于时间的缓存验证。示例:
Last-Modified: Wed, 01 Sep 2023 12:00:00 GMT
:指示资源的最后修改时间。- 客户端发送请求时,通过
If-Modified-Since
头部字段将之前的修改时间发送给服务器,如果资源未发生变化,服务器可返回状态码 304 Not Modified。
高级:
使用CDN:
解决Internet网络拥挤的状况,提高用户访问网站的响应速度。已经有很多优秀文章了,我这里贴上一篇掘金的文章 -- 前端必需了解的CDN知识 - 掘金,有兴趣可以去学习一下。
使用webpack打包优化(vite的打包优化下次一定):
Configuration Languages | webpack 中文文档
一方面提高了开发的速度以及打包的速度,另一方面缩小了打包的体积,有助于干净的产品上线,减少服务器不必要的负担。
使用 css-minimizer-webpack-plugin 压缩css文件
optimization: {
minimizer: [
new CssMinimizerPlugin()
],
},
使用 terser-webpack-plugin 对 TypeScript 编译后的 JavaScript 代码进行压缩
optimization: {
minimizer: [
new TerserPlugin()
],
},
使用 html-webpack-plugin 简化手动创建 HTML 文件、管理文件路径和资源引用的繁琐工作
plugin: [
new HtmlWebpackPlugin({
title: 'vue3-webpack',
template: path.join(__dirname, 'public/index.html'),
minify: {
removeComments: true, // 移除注释
removeRedundantAttributes: true, // 移除多余的属性
}
})
]
使用 mini-css-extract-plugin 提取项目中的 CSS 代码并生成一个独立的 CSS 文件
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css' // 输出的 CSS 文件名
})
],
使用 Tree shaking 静态分析代码,将未使用的代码从最终打包结果中去除
注意的是,Tree shaking 只能去除 ES6 模块导入和导出的代码
module.exports = {
// ...其他配置项
optimization: {
usedExports: true
}
};
使用 代码分割 优化技术,减小初始加载时所需的资源体积,提高页面加载速度和性能
使用动态导入的方式按需加载模块
// 按需加载模块
import('./module').then(module => {
module.doSomething();
});
使用 import() 并配合 webpackPrefetch 或 webpackPreload 注释,告诉浏览器在空闲时间预先加载指定的模块
// 预加载模块
import(/* webpackPrefetch: true */ './module');
// 或者
// 预先加载模块
import(/* webpackPreload: true */ './module');
使用 SplitChunksPlugin (webpack 提供的内置插件),用于自动将公共代码提取到单独的文件中
module.exports = {
// 其他
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
学会性能监控和调试,以及在写代码的时候,多思考每一行代码可能面临的问题,有没有更优的写法,这才是最佳优化性能法。
~ 你的点赞、评论、收藏是我发布优质文章的最大动力,感谢支持 ~
更多推荐
所有评论(0)