vue3项目优化方案

做 Vue3 开发的小伙伴应该都遇到过这个问题:当列表数据达到几百甚至几千条时,页面轻则加载缓慢,重则滚动卡顿,用户体验直接拉胯,甚至还会被产品追着改需求。

其实长列表的性能问题,核心无非就是DOM 元素过多、渲染压力过大、无用操作频繁这几个原因。今天就给大家分享 7 个可直接落地的 Vue3 长列表优化技巧,从核心方案到细节优化,全覆盖,看完就能抄作业,让你的长列表滚动丝滑到极致!

🚀 核心方案:虚拟列表,从根源减少 DOM

这是优化长列表最有效的方法,没有之一!普通列表会一次性渲染所有数据,哪怕有 10000 条,DOM 元素就有 10000 个,浏览器根本扛不住;而虚拟列表的核心思路是只渲染当前视口中的内容,不在视口的内容直接不渲染,DOM 数量能从几千上万骤减到几十,性能直接拉满。

Vue3 生态有现成的虚拟列表组件vue-virtual-scroller,开箱即用,步骤超简单:

1. 安装依赖

javascript 复制代码
npm install vue-virtual-scroller

2. 组件中引入并使用

javascript 复制代码
<template>
  <RecycleScroller
    class="scroller"
    :items="longList"
    :item-size="50"
    key-field="id"
  >
    <template v-slot="{ item }">
      <div class="list-item">{{ item.content }}</div>
    </template>
  </RecycleScroller>
</template>

<script setup>
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
// 模拟10000条长列表数据
const longList = Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  content: `Item ${i}`
}))
</script>

<style scoped>
.scroller {
  height: 500px;
  overflow-y: auto;
}
.list-item {
  height: 50px;
  line-height: 50px;
  border-bottom: 1px solid #eee;
}
</style>

哪怕列表有 10000 条数据,这个组件会自动计算可视区域,DOM 数量始终保持在几十左右,滚动起来毫无压力。

📸 按需加载:懒加载,减轻初始渲染压力

如果长列表里包含大量图片或复杂子组件,哪怕用了虚拟列表,初始加载时一次性处理所有资源也会让页面卡顿。这时候懒加载就派上用场了 ------ 元素进入视口时才加载内容,完美避开初始加载的性能瓶颈。

Vue3 中用vue3-lazyload插件就能轻松实现图片懒加载,还能自定义加载中和加载失败的占位图:

1. 安装插件

javascript 复制代码
npm install vue3-lazyload

2. 全局引入(main.js)

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import VueLazyload from 'vue3-lazyload'

createApp(App)
  .use(VueLazyload, {
    loading: '/loading.gif', // 加载中占位图
    error: '/error.png'     // 加载失败占位图
  })
  .mount('#app')

3. 组件中使用

javascript 复制代码
<template>
  <div v-for="item in longList" :key="item.id" class="list-item">
    <img v-lazy="item.imageUrl" alt="列表图片">
    <p>{{ item.content }}</p>
  </div>
</template>

这样图片只有滚动到可视区域时才会发起请求加载,页面初始加载速度直接翻倍。

🧹 精简响应式:markRaw,拒绝无用的性能开销

Vue3 的响应式系统虽然强大,但不是所有数据都需要做响应式处理!比如长列表中的静态数据、只展示不修改的数据,强行做响应式会让 Vue 额外进行依赖追踪,增加性能开销。

这时候用markRaw标记数据,就能让 Vue 跳过响应式处理,直接减少底层的性能消耗:

javascript 复制代码
import { markRaw } from 'vue'
// 标记后,数据不再是响应式,Vue不会追踪其变化
const staticList = markRaw(Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  content: `Static Item ${i}`
})))

小贴士:此方法仅适用于纯静态数据,若数据需要后续修改,切勿使用!

🔧 组件优化:让列表项 "轻装上阵"

如果列表项组件写得过于复杂,哪怕数据量不大,也会让渲染变慢。针对列表项组件,从这 3 个点优化,立竿见影:

1. 拆分组件

把复杂的列表项拆分成多个小组件,比如将 "图片区、文字区、操作区" 分开,减少单个组件的渲染压力,也更易维护。

2. v-once 标记静态内容

v-once指令标记列表项中的静态内容,让 Vue 只渲染一次,后续不再更新:

javascript 复制代码
<template>
  <div class="list-item">
    <!-- 静态内容,只渲染一次 -->
    <p v-once>{{ item.staticContent }}</p>
    <!-- 动态内容,正常响应式 -->
    <p>{{ item.dynamicContent }}</p>
  </div>
</template>

3. 事件委托替代单独绑定

如果列表项有大量点击、hover 等事件,不要给每个项单独绑定事件,用事件委托把事件绑定到父容器上,减少事件监听的数量。

📄 备选方案:分页加载,简单粗暴易实现

如果项目场景不适合用虚拟列表(比如需要展示完整列表结构),或者懒加载的优化效果不够,分页加载就是最稳妥的备选方案 ------ 把长列表分成多页,每次只加载并渲染一页数据,从根源控制 DOM 数量。

实现一个 "加载更多" 式的分页加载,代码超简单:

javascript 复制代码
<template>
  <div>
    <div v-for="item in listData" :key="item.id" class="list-item">
      {{ item.content }}
    </div>
    <button @click="loadNextPage" v-if="hasMore">加载更多</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const page = ref(1)        // 当前页码
const pageSize = ref(20)   // 每页条数
const total = ref(1000)    // 总数据量
const listData = ref([])   // 已加载数据
const hasMore = ref(true)  // 是否还有更多数据

// 加载数据
const loadData = async () => {
  // 模拟接口请求
  const res = await fetch(`/api/list?page=${page.value}&pageSize=${pageSize.value}`)
  const data = await res.json()
  listData.value.push(...data.list)
  page.value++
  // 判断是否加载完毕
  if (listData.value.length >= total.value) {
    hasMore.value = false
  }
}

// 加载下一页
const loadNextPage = () => {
  loadData()
}

// 初始加载第一页
loadData()
</script>

分页加载的优势是实现简单、兼容性好,适合大部分中小型项目的长列表场景。

🎨 CSS 优化:避开复杂选择器,减少浏览器渲染耗时

很多开发者会忽略 CSS 对长列表的影响,其实复杂的 CSS 选择器会让浏览器花费更多时间匹配元素,尤其是长列表有大量重复元素时,这种耗时会被无限放大。

优化原则:用简单的类选择器,避免嵌套过深、避免多层后代选择器

css 复制代码
/* ❌ 不好的写法:嵌套过深,浏览器匹配耗时 */
.list-container .list-item .item-content p {
  color: #333;
}

/* ✅ 好的写法:单独类选择器,匹配速度快 */
.item-content-text {
  color: #333;
}

简单的 CSS 选择器能让浏览器的渲染引擎更快工作,间接提升列表的滚动流畅度。

⏱️ 滚动优化:requestAnimationFrame,避免频繁触发

如果长列表需要监听滚动事件(比如实现无限滚动、滚动加载),直接监听会导致事件高频触发,主线程被阻塞,进而引发滚动卡顿。

解决方法:用requestAnimationFrame包裹滚动事件的处理函数,让函数在浏览器下一次重绘前执行,避免频繁触发,减少性能损耗:

javascript 复制代码
const handleScroll = () => {
  requestAnimationFrame(() => {
    // 获取滚动相关数据
    const scrollTop = document.documentElement.scrollTop
    const clientHeight = document.documentElement.clientHeight
    const scrollHeight = document.documentElement.scrollHeight
    // 滚动到底部前100px时,加载下一页
    if (scrollTop + clientHeight >= scrollHeight - 100) {
      loadNextPage()
    }
  })
}
// 监听滚动事件
window.addEventListener('scroll', handleScroll)

小技巧 :还可以给滚动事件添加passive: true,进一步提升滚动性能:

javascript 复制代码
window.addEventListener('scroll', handleScroll, { passive: true })

✨ 最后总结:优化核心就这 3 点

其实 Vue3 长列表的优化,不用追求花里胡哨的方案,抓住核心思路,根据项目场景组合使用上述技巧即可:

  1. 减少 DOM 元素数量:虚拟列表、分页加载是核心手段;

  2. 减轻渲染压力:懒加载、拆分组件、v-once 标记静态内容;

  3. 避免不必要的操作:markRaw 精简响应式、简化 CSS 选择器、requestAnimationFrame 优化滚动。

相关推荐
Mr_Swilder2 小时前
WebGPU 基础 (WebGPU Fundamentals)
前端
张3蜂2 小时前
HTML5语义化标签:现代网页的骨架与灵魂
前端·html·html5
悟空瞎说2 小时前
我用 PixiJS 撸了个圆桌会议选座系统,从 0 到 1 踩坑全复盘
前端
码云之上2 小时前
从 SPA 到全栈:AI 时代的前端架构升级实践
前端·架构·ai编程
Irene19913 小时前
对比总结:Vue3中的 watch 和 Pinia中的 $subscribe
vue.js·pinia·watch·subscribe
小陈同学呦3 小时前
关于如何使用CI/CD做自动化部署
前端·后端
前端Ah3 小时前
记 华为鸿蒙机型小程序使用uni.createInnerAudioContext() 播放音频播放两次的问题
前端
用户221765927923 小时前
css border-left 怎么设置 border 展示为椭圆
前端
御形封灵3 小时前
纯CSS实现方块下落等待动画
前端·css