前端学习笔记-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 动画 / 模糊缩略图)体验更好。
相关推荐
weixin_431600441 分钟前
做 Agent 会用到的 Node API(3):异步与流
前端·学习·ai·agent·ai编程
程序员黑豆6 分钟前
鸿蒙应用开发之生命周期方法完全指南
前端·harmonyos
一点一木7 分钟前
第 23 届 ChinaJoy 刚结束,我把 965 个展台做成了 3D 云展馆
前端·人工智能
wordbaby9 分钟前
前端请求缓存写了,同一个接口为什么还是打了十几次?
前端·promise
不可能掉发9 分钟前
Env Guard:让浏览器一眼分清生产、测试和开发环境
前端·javascript·chrome·测试工具·html·开源软件·个人开发
饼干哥哥16 分钟前
Codex 必改的8 个基础配置
前端·人工智能·后端
ClouGence22 分钟前
AI Agent 能写测试、跑流程,为什么回归测试还不能完全交给 AI?
前端·测试
码云之上23 分钟前
Context Engineering:让 Agent 在当前步骤看到正确的事实
前端·人工智能·前端工程化
嘟嘟071742 分钟前
从手写 HashRouter 到 React Router:逐层拆解 SPA 前端路由的完整实现
前端·javascript
70asunflower1 小时前
Triton 从 0 到 1 完整学习教程
学习·triton