Code前端首页关于Code前端联系我们

Vue3里computed和onMounted咋用?常见场景+原理一次讲清

terry 24小时前 阅读数 116 #SEO
文章标签 computed;onMounted

computed在Vue3中是做什么的?

简单说,computed 是Vue3提供的响应式计算属性API,核心作用是基于其他响应式数据,生成一个自动更新的“动态值”。

举个生活例子:点外卖时,购物车的“总价”需根据每个商品的“单价×数量”自动计算,且商品数量变化时总价得实时更新——这就是computed的典型场景。

从技术角度看,computed有两个关键特性:

  1. 依赖追踪:自动“盯住”用到的响应式数据(如ref/reactive包裹的数据),一旦这些数据变化,computed结果会自动更新。
  2. 缓存机制:若依赖数据没变化,多次访问computed结果时,会直接用之前计算的缓存值,不会重复执行计算逻辑——这比每次调用methods里的函数性能更好(methods每次调用都要重新计算)。

看段代码直观理解:

<template>
  <div>总价:{{ totalPrice }}</div>
  <button @click="cart[0].count++">增加商品1数量</button>
</template>
<script setup>
import { reactive, computed } from 'vue'
// 购物车数据(响应式)
const cart = reactive([
  { price: 10, count: 2 },
  { price: 20, count: 3 }
])
// 计算总价:依赖cart里的price和count
const totalPrice = computed(() => {
  return cart.reduce((sum, item) => sum + item.price * item.count, 0)
})
</script>

点击按钮修改cart[0].count时,totalPrice会自动重新计算——因为computed追踪到cart变化;若连续多次访问totalPrice,只要cart没动,就直接读缓存,无需重复执行reduce

computed的响应式原理是怎么实现的?

Vue3的响应式基于Proxy代理,但computed的实现更“聪明”——结合了effect(副作用)和懒执行+缓存逻辑。

简单拆解原理:

  1. 懒执行:创建computed时,不会立刻执行计算逻辑,只有访问计算属性(如模板里用{{ totalPrice }})时,才会执行回调函数算出结果。
  2. 依赖收集:执行回调时,Vue通过track方法,把computed自身注册为依赖数据(如上述cart)的“观察者”。
  3. 标记脏值(dirty):当依赖数据(cart)变化时,Vue通过trigger触发更新,把computed标记为“脏”(结果过时)。
  4. 缓存复用:下次访问computed时,先检查是否“脏”——若脏了,重新计算并更新缓存;若没脏,直接返回缓存值。

打个比方:computed像“自动更新的计算器”,只有输入(依赖数据)变化才重新算结果;否则用上次结果,省得重复干活,这种设计能大幅减少不必要计算,让页面更流畅。

onMounted钩子在什么时机执行?

onMounted是Vue3组件生命周期钩子,执行时机是:组件对应的DOM完全渲染到页面后

组件挂载分两步:先创建虚拟DOM,再把虚拟DOM渲染成真实DOM。onMounted是挂载阶段“最后一步”,此时能安全操作真实DOM(如获取元素尺寸、初始化依赖DOM的库)。

举个常见场景:用ECharts做图表,ECharts需先拿到页面上的DOM容器才能初始化图表,若在onMounted前执行,DOM未渲染,会拿到null,导致初始化失败,看代码:

<template>
  <div ref="chartRef" style="width: 600px; height: 400px;"></div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import * as echarts from 'echarts'
const chartRef = ref(null) // 初始是null,DOM渲染后才赋值
onMounted(() => {
  const dom = chartRef.value // 此时DOM已渲染,dom能拿到真实元素
  const myChart = echarts.init(dom)
  myChart.setOption({ /* 图表配置 */ })
})
</script>

对比onBeforeMount(DOM渲染前执行):此时chartRef.value还是null,拿不到DOM,无法初始化图表。

computed和onMounted能配合使用吗?有哪些场景?

当然能!onMounted负责“初始化数据/环境”,computed负责“基于数据做实时计算”,很多真实项目都会这么用。

举个博客系统例子:页面加载后(onMounted)请求文章列表,computed根据文章“点赞数”实时计算“热门文章”,代码如下:

<template>
  <h2>热门文章</h2>
  <ul>
    <li v-for="article in hotArticles" :key="article.id">{{ article.title }}</li>
  </ul>
</template>
<script setup>
import { onMounted, reactive, computed } from 'vue'
// 存储所有文章(响应式)
const articles = reactive([])
// 模拟接口请求(实际用axios/fetch)
function fetchArticles() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([
        { id: 1, title: 'Vue3入门', likes: 100 },
        { id: 2, title: 'React技巧', likes: 80 },
        { id: 3, title: 'JS进阶', likes: 120 }
      ])
    }, 1000)
  })
}
// 页面挂载后,请求文章数据
onMounted(async () => {
  const res = await fetchArticles()
  articles.push(...res)
})
// 计算属性:按点赞数降序排列,得到热门文章
const hotArticles = computed(() => {
  return [...articles].sort((a, b) => b.likes - a.likes)
})
</script>

逻辑链清晰:

  1. onMounted触发→请求文章数据→articles被填充(响应式数据变化)。
  2. hotArticles依赖articlesarticles变化时,hotArticles自动重新排序。

若后续用户给文章点赞(如加按钮修改article.likes),hotArticles也会实时更新排序——因为computed始终盯着articles的变化。

用computed容易踩哪些坑?怎么避免?

computed好用但新手易踩“陷阱”,常见问题及解决方法如下:

陷阱1:依赖非响应式数据

computed里用了普通变量(没被ref/reactive包裹),数据变化时computed不会更新。

反面例子

<script setup>
import { computed } from 'vue'
let count = 0 // 普通变量,非响应式
const double = computed(() => count * 2)
function add() {
  count++ // double不会更新!因count非响应式
}
</script>

解决:把普通变量改成响应式(用ref):

const count = ref(0) // count变为响应式,变化时double会更新

陷阱2:在computed里写异步操作

computed的回调必须同步返回值,不能用async/await搞异步,若要处理异步逻辑,得换watch或在setup里结合ref

反面例子(错误用法):

const asyncComputed = computed(async () => {
  const res = await fetch('/api/data')
  return res.data // 不行!computed不支持异步回调
})

解决思路:用watch监听触发异步的条件,把结果存在ref里,再让computed依赖这个ref

const data = ref(null)
watch(someTrigger, async () => {
  const res = await fetch('/api/data')
  data.value = res.data
})
const computedData = computed(() => {
  return data.value ? transform(data.value) : null
})

陷阱3:过度依赖computed导致逻辑臃肿

若多个computed嵌套或计算逻辑太复杂,代码会难以维护。

解决:把复杂逻辑拆成多个小computed,或用methods做辅助计算(虽methods无缓存,但可读性优先时可用)。

把“过滤”和“排序”拆成两个computed

// 先过滤
const filteredList = computed(() => {
  return list.value.filter(item => item.status === 'active')
})
// 再排序
const sortedList = computed(() => {
  return [...filteredList.value].sort((a, b) => a.time - b.time)
})

onMounted里操作DOM要注意什么?

onMounted能拿到真实DOM,但这些细节要注意,否则易出bug:

注意1:动态渲染的DOM(v-if控制)

若DOM是用v-if控制显示的,onMounted执行时,要确保该DOM已被渲染

反面例子

<template>
  <div v-if="show" ref="boxRef">我是动态DOM</div>
  <button @click="show = !show">切换</button>
</template>
<script setup>
import { onMounted, ref } from 'vue'
const show = ref(false)
const boxRef = ref(null)
onMounted(() => {
  console.log(boxRef.value) // 初始show是false,DOM未渲染→输出null
})
</script>

解决:用watch监听show变化,在showtrue时操作DOM:

watch(show, (newVal) => {
  if (newVal) {
    // show为true时,DOM已渲染,可安全操作boxRef.value
    console.log(boxRef.value) 
  }
})

注意2:避免在onMounted里做耗时操作

onMounted里有大量循环、DOM操作或复杂计算,会阻塞页面渲染,让用户觉得“页面加载慢”。

解决:把耗时逻辑拆成异步任务(如用setTimeout包装,或用Web Worker),让页面先渲染,再后台执行逻辑。

拓展:computed的get和set怎么用?

默认computed只有“获取值”的逻辑(getter),但也能定义“设置值”的逻辑(setter),实现双向响应

比如做“搜索过滤”功能:输入框绑定searchTextcomputedfilteredList既能根据searchText过滤列表,也能通过设置filteredList修改searchText,代码如下:

<template>
  <input v-model="searchText" placeholder="搜索">
  <ul>
    <li v-for="item in filteredList" :key="item.id">{{ item.name }}</li>
  </ul>
  <button @click="resetSearch">重置搜索</button>
</template>
<script setup>
import { ref, computed } from 'vue'
const list = ref([
  { id: 1, name: '苹果' },
  { id: 2, name: '香蕉' },
  { id: 3, name: '橙子' }
])
const searchText = ref('')
// 带setter的computed
const filteredList = computed({
  get() {
    return list.value.filter(item => item.name.includes(searchText.value))
  },
  set(newValue) {
    // 当给filteredList赋值时,触发setter,更新searchText
    searchText.value = newValue
  }
})
function resetSearch() {
  filteredList.value = '' // 触发setter,searchText被设为'',列表恢复全部内容
}
</script>

点击“重置搜索”按钮时,filteredList.value = ''会触发setter,把searchText设为,进而让getter重新计算,列表显示所有内容——这就是computed setter的妙用:让计算属性既能“读”又能“写”。

onMounted和其他生命周期钩子有啥区别?

Vue3的生命周期钩子很多,onMounted的独特性在于只在组件第一次渲染完成后执行,后续组件更新(如数据变化导致重新渲染)时,不会再触发onMounted,而是触发onUpdated

举个对比表格(简化版):

钩子名 执行时机 典型场景
onBeforeMount 组件挂载前(DOM未渲染) 提前准备数据(少用,一般用setup)
onMounted 组件挂载后(DOM已渲染) 初始化DOM相关逻辑(图表、地图)
onBeforeUpdate 数据变化后,DOM更新前 记录DOM更新前的状态
onUpdated 数据变化后,DOM更新完成后 调整DOM样式(比如滚动位置)

举个定时器例子:

<script setup>
import { onMounted, onUnmounted } from 'vue'
let timer = null
onMounted(() => {
  timer = setInterval(() => {
    console.log('每1秒执行')
  }, 1000)
})
onUnmounted(() => {
  clearInterval(timer) // 组件销毁时清除定时器,避免内存泄漏
})
</script>

onMounted里启动定时器,onUnmounted(组件销毁时)清除定时器——这是常见的“生命周期配对”写法。


computed是“响应式的智能计算器”,基于依赖数据生成动态结果;onMounted是“DOM就绪后的初始化入口”,处理依赖真实DOM的逻辑,两者单独使用解决不同问题,配合起来能打造更灵活的交互(如数据请求+实时计算)。

实际开发中,要注意computed的依赖必须是响应式数据、避免异步操作;onMounted要关注DOM渲染时机、别塞太多耗时逻辑,吃透这些细节,Vue3的响应式和生命周期就能用得更顺手~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门