前端学习笔记-vue列表处理

13 长列表 --- 用 Object.freeze / markRaw 禁用响应式

一句话理解

渲染 几千上万条只读数据 时,让 Vue 给每一条都建立响应式追踪是巨大浪费。用 Object.freeze(Vue 2)或 markRaw(Vue 3)禁止 Vue 劫持这些数据,初始化时间能大幅缩短。

为什么

假设有 1 万条数据,每条 5 个字段 = 5 万个属性。如果全部响应式,Vue 要为这 5 万个属性建立 getter/setter 和依赖收集------光初始化就要几百毫秒甚至卡顿。

而这些数据如果是只读展示(不修改、不双向绑定),完全可以跳过响应式。

代码示例

Vue 3:markRaw + 普通展示:

vue 复制代码
<template>
  <ul>
    <li v-for="item in hugeList" :key="item.id">
      {{ item.name }} - {{ item.email }}
    </li>
  </ul>
</template>

<script setup>
import { markRaw } from 'vue'

// 1 万条只读数据:markRaw 禁用响应式,初始化飞快
const hugeList = markRaw(
  Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    name: `用户${i}`,
    email: `user${i}@x.com`,
  }))
)
</script>

Vue 2:Object.freeze

js 复制代码
export default {
  data() {
    return {
      // 冻结后 Vue 跳过响应式处理,1 万条数据秒渲染
      hugeList: Object.freeze(fetchTenThousandItems()),
    }
  }
}

新人提示

  • 适用前提:数据只读、不需要修改驱动视图。如果用户要编辑某行,这条数据就不能冻结。
  • 如果列表既要大又要可滚动渲染,配合虚拟滚动(只渲染可视区域 DOM)才是终极方案。
  • 别在数据需要响应式时强行 freeze,否则改了界面不更新,会很困惑。

14 图片按需加载(懒加载)

一句话理解

一个图片画廊页面有 100 张图,别一次性全加载------滚动到可视区域时再加载,首屏快、流量省。

为什么

图片是页面体积的大头。首屏只看得到几张图,却要下载全部 100 张,既慢又费流量。懒加载让图片"进入视野才下载"。

代码示例

方案一:原生 loading="lazy"(最简单,现代浏览器都支持):

vue 复制代码
<template>
  <!-- loading="lazy" 浏览器自动懒加载,零 JS -->
  <img
    v-for="img in images"
    :key="img.id"
    :src="img.url"
    loading="lazy"
    alt="图片"
  />
</template>

方案二:IntersectionObserver(更可控):

vue 复制代码
<template>
  <img
    v-for="img in images"
    :key="img.id"
    :data-src="img.url"        <!-- 先不设 src,避免立即加载 -->
    ref="imgRefs"
    alt="图片"
  />
</template>

<script setup>
import { onMounted, onBeforeUnmount } from 'vue'
import { ref } from 'vue'

const imgRefs = ref([])
let observer = null

onMounted(() => {
  // 观察每张图片是否进入可视区域
  observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target
        img.src = img.dataset.src   // 进入视野,才真正加载
        observer.unobserve(img)      // 加载后停止观察
      }
    })
  })

  imgRefs.value.forEach(img => observer.observe(img))
})

onBeforeUnmount(() => observer?.disconnect())
</script>

方案三:第三方指令 v-lazy(VueLazyload 等库):

vue 复制代码
<template>
  <img v-lazy="img.url" v-for="img in images" :key="img.id" />
</template>

新人提示

  • 优先用原生 loading="lazy",简单够用。
  • 头图、首屏关键图不要懒加载(反而拖慢 LCP 指标)。
  • 配合占位图(loading 动画 / 模糊缩略图)体验更好。
相关推荐
倾颜1 小时前
AI 聊天刷新后记录全丢?用浏览器本地存储给 AI Chat 加一层"离线记忆"
前端·aigc
小弥儿1 小时前
GitHub今日热榜 | 2026-07-16:AI知识库与反AI味设计成今日双主线
人工智能·学习·github
梦想的旅途21 小时前
企业微信API 实现企业微信消息的自动发送?
java·服务器·前端
何时梦醒1 小时前
🤖 RAG 文档分割器(Splitter)深度学习笔记 —— 从零搭建知识库文档处理流水线
前端·javascript·人工智能
菩提树下的打坐1 小时前
CI 中的测试分层:让金字塔真正落地
学习·ci/cd
ss2731 小时前
Vue 完整版本发展史时间线(2013–2026)|版本迭代里程碑+核心特性对照表
前端·javascript·vue.js
亿元程序员1 小时前
为什么大家都那么喜欢用Shader?
前端
Codebee1 小时前
OODER Studio设计工具导入体系深度解析
前端