除了防抖和节流,还有哪些 JS 性能优化手段?

防抖和节流大家都用过,但 JS 性能优化其实不止这两种。在日常开发里,有很多实用的方法,可以让页面更顺畅。

于是我整理了几个常用的 vue 性能优化写法,自己用起来也很顺手。

1. 避免模板中的重复计算

我以前喜欢直接在模板中写一些计算逻辑,虽然它能快速实现需求,但如果不小心,可能会影响性能。尤其是当组件频繁更新时,重复计算的开销就会显现出来。

基础写法:

html 复制代码
<template>
  <div v-for="item in list" :key="item.id">
    商品:{{ item.name }}
    价格:{{ formatPrice(item.price) }}
  </div>
</template>

<script setup>
import { ref } from "vue"

const list = ref([
  { id: 1, name: "键盘", price: 1999 },
  { id: 2, name: "鼠标", price: 299 },
  { id: 3, name: "显示器", price: 3999 }
])

const formatPrice = (price) => {
  return "¥" + (price / 100).toFixed(2)
}
</script>

问题 :每次渲染时,formatPrice 会重新计算,假如列表有几百个条目,这个性能损耗就会变得明显。

优化写法:

可以使用 Vue3 的 computed 属性,提前计算好需要的数据,避免每次都执行复杂的计算。

html 复制代码
<template>
  <div v-for="item in formattedList" :key="item.id">
    商品:{{ item.name }}
    价格:{{ item.priceText }}
  </div>
</template>

<script setup>
import { ref, computed } from "vue"

const list = ref([
  { id: 1, name: "键盘", price: 1999 },
  { id: 2, name: "鼠标", price: 299 },
  { id: 3, name: "显示器", price: 3999 }
])

const formattedList = computed(() => {
  return list.value.map(item => ({
    ...item,
    priceText: "¥" + (item.price / 100).toFixed(2)
  }))
})
</script>

优化效果 :只有 list 数据发生变化时,formattedList 才会重新计算,避免了每次渲染时都重新格式化价格,提升了性能。

2. 合理使用 v-if 和 v-show

在 Vue3 中,v-ifv-show 都可以控制组件或元素的显示,但如果用得不对,也会影响性能。

场景示例:

假设有一个选项卡组件,用户频繁切换内容:

html 复制代码
<template>
  <button @click="tab = 1">Tab 1</button>
  <button @click="tab = 2">Tab 2</button>

  <div v-if="tab === 1">内容 1</div>
  <div v-if="tab === 2">内容 2</div>
</template>

<script setup>
import { ref } from "vue"
const tab = ref(1)
</script>

问题 :每次切换 tab 时,v-if 都会销毁和重建对应 DOM,如果内容复杂或包含子组件,性能开销明显。

优化写法:使用 v-show

html 复制代码
<template>
  <button @click="tab = 1">Tab 1</button>
  <button @click="tab = 2">Tab 2</button>

  <div v-show="tab === 1">内容 1</div>
  <div v-show="tab === 2">内容 2</div>
</template>

优化效果v-show 只是切换元素的 CSS display,不会频繁销毁和重建 DOM,适合频繁切换但数量有限的场景

如果是初次渲染开销大、切换不频繁,用 v-if;如果是频繁切换,用 v-show。合理选择可以明显提高性能。

3. 懒加载:先渲染用户可见部分

懒加载是一种非常有效的技术,可以让页面内容只有在用户滚动到该部分时才加载,提升首屏加载速度,减少不必要的请求。

基础写法:

html 复制代码
<template>
  <img :src="imageSrc" />
</template>

<script setup>
const imageSrc = "https://example.com/large-image.jpg"
</script>

问题:直接在组件加载时就请求了图片,导致首屏加载时间增加。

优化写法:图片懒加载

html 复制代码
<template>
  <img v-lazy="imageSrc" />
</template>

<script setup>
import { ref } from "vue"
import { useIntersectionObserver } from "@vueuse/core"

const imageSrc = ref("https://example.com/large-image.jpg")

// 使用 @vueuse/core 的 useIntersectionObserver 来懒加载图片
useIntersectionObserver(imageRef, () => {
  imageRef.src = imageSrc.value
})
</script>

优化效果:只有当图片进入视口时才会开始加载,减少了首屏的资源加载,提高页面加载速度。

4. 避免过度使用 watch

在 Vue 中,watch 是监听数据变化的常用方式,但如果用得不当,会导致多次不必要的计算和请求,增加性能开销。

基础写法:

html 复制代码
<template>
  <input v-model="searchQuery" placeholder="搜索" />
</template>

<script setup>
import { ref, watch } from "vue"

const searchQuery = ref("")

watch(searchQuery, (newQuery) => {
  // 每次输入都会发请求
  fetch(`/api/search?q=${newQuery}`)
})
</script>

问题 :每次 searchQuery 变化时都会触发请求,尤其是输入法弹出时,会多次触发请求。

优化写法:使用防抖

html 复制代码
<template>
  <input v-model="searchQuery" placeholder="搜索" />
</template>

<script setup>
import { ref, watch } from "vue"

const searchQuery = ref("")
let timeout

watch(searchQuery, (newQuery) => {
  clearTimeout(timeout)
  timeout = setTimeout(() => {
    fetch(`/api/search?q=${newQuery}`)
  }, 500) // 防抖:500ms后才发送请求
})
</script>

优化效果:只有在用户停止输入 500 毫秒后才会发起请求,避免了过于频繁的请求。

5. 避免重复绑定事件

在 Vue3 中,每次渲染组件时,都会重新绑定事件。如果绑定事件的元素很多,性能开销就会加大,尤其是在动态生成的列表中。

基础写法:

html 复制代码
<template>
  <div v-for="item in list" :key="item.id">
    <button @click="handleClick(item.id)">点击</button>
  </div>
</template>

<script setup>
const handleClick = (id) => {
  console.log("点击按钮", id)
}
</script>

问题 :每个 button 都会绑定一个新的事件处理函数,造成不必要的内存开销。

优化写法:事件委托

html 复制代码
<template>
  <div @click="handleClick">
    <div v-for="item in list" :key="item.id">
      <button>{{ item.name }}</button>
    </div>
  </div>
</template>

<script setup>
const handleClick = (e) => {
  if (e.target.tagName === 'BUTTON') {
    console.log('点击按钮', e.target.innerText)
  }
}
</script>

优化效果:通过事件委托,将事件绑定到父元素上,避免了为每个按钮都单独绑定事件,大大减少了内存开销。

以上这些写法都是我平时在项目里用的,实用又好操作。如果你在开发中有自己的优化方法,也欢迎在评论区分享,我们一起讨论。

相关推荐
海兰6 小时前
【应用】基于 Next.js 16 + Python mplfinance的金融K线图与技术指标可视化平台(三)
javascript·python·金融
小磊哥er7 小时前
深入解构Claude Code - 第 7 篇 · 连接外面的世界
javascript·ai编程
小磊哥er8 小时前
深入解构Claude Code - 第 6 篇 · 终端里的图形界面
javascript·ai编程
海兰8 小时前
【应用】基于 Next.js 16 + Python mplfinance的金融K线图与技术指标可视化平台(一)
javascript·python·金融
可乐鸡翅yeah_9 小时前
hls.js 内存泄漏实战排查,直播 M3U8 长时间播放异常定位方案
开发语言·javascript·python·django·ecmascript·m3u8·m3u8在线
可乐鸡翅yeah_9 小时前
第三方接口对接 M3U8 流媒体排坑实战,解决外部服务商流兼容难题
前端·javascript·python·django·html·m3u8·m3u8在线
默_笙10 小时前
🏠 「LLM Notes」:我用 Next.js + Redis 给自己造了个笔记博客
前端·javascript
ruanCat11 小时前
Prettier 明明执行成功,Markdown 为什么还是没变?一次插件误诊复盘
前端·javascript·node.js
软件开发技术深度爱好者11 小时前
在线学习实践 TypeScript 的官方Playground(演练场)使用介绍
javascript·学习·typescript
2601_9621229712 小时前
nginx 部署前端vue项目
前端·vue.js·nginx