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

Vue3里computed和emit该怎么理解与运用?

terry 11小时前 阅读数 80 #SEO
文章标签 Vue3computed emit

不少刚接触Vue3的同学,对computedemit这两个知识点既好奇又有点懵:计算属性到底咋用?子组件咋给父组件传数据?今天咱们用问答的方式把这些事儿掰扯清楚。

computed在Vue3里扮演啥角色?

你可以把computed理解成“聪明的响应式计算器”——它基于依赖的响应式数据做计算,还会“偷懒”:只有依赖变化时才重新计算,否则复用之前的结果。

举个生活例子:做用户信息展示时,firstNamelastName是响应式数据,要拼接成fullName,如果用method写函数返回拼接结果,每次页面渲染(哪怕其他无关数据变了)都会重新执行函数;但用computed的话,只有firstNamelastName变化时,fullName才会重新计算,性能更优。

在代码里,Vue3的组合式API选项式API写法不同:

  • 组合式(灵活,适合复杂逻辑):
    import { ref, computed } from 'vue'
    const firstName = ref('张')
    const lastName = ref('三')
    const fullName = computed(() => `${firstName.value}${lastName.value}`)
  • 选项式(结构清晰,适合简单场景):
    export default {
      data() { return { firstName: '张', lastName: '三' } },
      computed: {
        fullName() { return this.firstName + this.lastName }
      }
    }

写computed时要避开哪些坑?

computed好用,但用错了也会踩雷,这几个细节得注意:

别在computed里搞“副作用”

computed设计是纯计算逻辑,只负责根据依赖返回新值,不能在里面修改其他响应式数据(比如调接口、改ref/reactive的值),要是有副作用需求,得用watch
反面例子(别学!):

const count = ref(0)
const doubleCount = computed(() => {
  count.value *= 2 // 坏例子:在computed里改其他响应式数据
  return count.value
})

异步操作别往computed里塞

computed依赖的是同步的响应式数据,异步操作(比如setTimeout、调接口)没法让computed感知到依赖变化,结果会失控,如果要处理异步逻辑,结合watch或者单独写函数更稳。

emit怎么实现子向父传数据?

emit是Vue里子组件给父组件“发消息”的核心方式,原理是“自定义事件”:子组件触发事件,父组件监听事件并执行逻辑。

步骤拆解(以组合式API为例):

  1. 子组件声明事件:用defineEmits声明要触发的事件(类似“告诉父组件:我可能发这些事件哦”)。

    <script setup>
    const emit = defineEmits(['toggle']) // 声明事件叫toggle
    const handleClick = () => {
      emit('toggle', 123) // 触发事件,传参123
    }
    </script>
  2. 父组件监听事件:用@事件名绑定回调函数,接收子组件传的数据。

    <ChildComponent @toggle="onToggle" />
    <script setup>
    const onToggle = (data) => {
      console.log('子组件传的数据:', data) // 这里能拿到123
    }
    </script>

选项式API写法:

子组件里声明emits选项,触发时用this.$emit

export default {
  emits: ['toggle'], // 声明事件
  methods: {
    handleClick() {
      this.$emit('toggle', 123)
    }
  }
}

computed和emit结合能解决哪些实际场景?

单独用computedemit已经很实用,结合起来能玩出更多花样,举两个常见场景:

场景1:TodoList的完成状态控制

父组件存所有todo数据,子组件TodoItem负责单个任务的显示和交互。

  • 子组件用computed:根据todoisDone状态,动态计算样式(比如完成的加删除线)。
  • 子组件用emit:用户点击“完成”按钮时,触发toggle事件,把当前todo的ID传给父组件。
  • 父组件接收emit后,更新对应todoisDone状态。

代码简化版:

// 子组件TodoItem
<script setup>
const props = defineProps(['todo'])
const emit = defineEmits(['toggle'])
// computed计算样式
const todoStyle = computed(() => ({
  textDecoration: props.todo.isDone ? 'line-through' : 'none'
}))
// 点击触发emit
const handleDone = () => {
  emit('toggle', props.todo.id)
}
</script>
// 父组件TodoList
<script setup>
const todos = ref([{ id: 1, text: '学习Vue', isDone: false }])
const handleToggle = (todoId) => {
  todos.value = todos.value.map(todo => 
    todo.id === todoId ? { ...todo, isDone: !todo.isDone } : todo
  )
}
</script>
<template>
  <TodoItem 
    v-for="todo in todos" 
    :key="todo.id" 
    :todo="todo" 
    @toggle="handleToggle" 
  />
</template>

场景2:表单输入的格式化传递

子组件是手机号输入框,要实时格式化(13800138000”变成“138 0013 8000”),再把格式化后的值传给父组件。

  • 子组件用computed:监听输入框的原始值,实时生成格式化后的字符串。
  • 子组件用emit:把格式化后的值通过input事件传给父组件。

这样父组件拿到的始终是“漂亮”的格式化数据,不用自己再处理。

Vue3对computed和emit做了哪些升级?

对比Vue2,Vue3在这两个功能上的开发体验和类型支持都有明显提升:

computed的灵活性

Vue2只有选项式API的computed选项;Vue3新增组合式API的computed函数,能和refreactive等更自由地组合,比如在自定义Hook里封装计算逻辑,复用性更强。

emit的“一等公民”地位

Vue2里子组件emit事件容易和原生事件混淆(比如click),且类型推导弱;Vue3里defineEmits编译时宏,不仅强制要求声明事件(避免未知事件警告),还能结合TypeScript做精确的类型推导:

// 子组件用TS声明emit类型
const emit = defineEmits<{
  (e: 'change', value: string): void; // 事件名change,传string类型
  (e: 'submit', id: number): void;    // 事件名submit,传number类型
}>()

看完这些,你应该对Vue3的computedemit有更具体的感知了:computed负责“聪明计算”,emit负责“消息传递”,结合起来能优雅解决很多组件通信和数据处理问题,下次写代码时,不妨试试用它们简化逻辑~

(如果还有细节没吃透,评论区随时喊我,咱们再唠!)

版权声明

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

热门