vue3对组件通信做了哪些升级?

前言

  • 常网IT源码上线啦!
  • 本篇录入吊打面试官专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
  • 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
  • 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。

先别急着把自己打磨成一颗光滑的鹅卵石。先花点时间,搞清楚自己到底是什么材料。是坚硬的花岗岩,还是温润的美玉。然后,再去找到那条能让你闪闪发光的赛道。每一朵花,都有自己的花期和花香。

一、前言

vue3的组件通信对比vue2做了哪些更新,且听我一一道来。

直入正文。

关于vue3对组件通信做了哪些升级?

二、事件系统的新特性

Vue3 中,所有绑定在组件上的事件默认都是原生 DOM 事件,这与 Vue2 的行为不同:

java 复制代码
<Event1 @click="handler2"></Event1>

事件冒泡机制​​:当点击子组件内部的按钮时,事件会冒泡到根节点触发父组件绑定的事件处理器。例如以下结构:

java 复制代码
// 子组件 Event1.vue
<div class="son">
    <p>我是子组件1</p>
    <button>点击我也执行</button>
</div>

点击按钮会触发父组件的 handler2,因为事件会冒泡到根 <div> 元素。

还记得vue2吗?

这种写法自定义事件,可以通过.native修饰符变为原生DOM事件。

现在vue3不用了。

原生的DOM事件不管是放在标签身上、组件标签身上都是原生DOM事件

事件覆盖也一样。

当子组件通过 emit 触发同名事件时,会覆盖原生事件:

java 复制代码
// 子组件中
const emit = defineEmits(['click'])
emit('click') // 这会覆盖原生 click 事件

三、全局事件总线替代方案

Vue3 移除了 $on$off API,推荐使用 mitt 库实现事件总线:

java 复制代码
import mitt from 'mitt'

// 创建事件总线
const emitter = mitt()

// 发送事件
emitter.emit('eventName', payload)

// 监听事件
emitter.on('eventName', (payload) => {
  // 处理事件
})

// 取消监听
emitter.off('eventName', handler)

四、v-model 的演进

Vue3 的 v-model 实现方式:

java 复制代码
<Child1 v-model="value"></Child1>

等价于:

java 复制代码
<Child1 :modelValue="value" @update:modelValue="newValue => value = newValue"></Child1>

Vue3 支持多个 v-model 绑定:

java 复制代码
<Child1 v-model:pageNo="pageNo" v-model:pagesize="pagesize"></Child1>

Vue 3.4 引入了 defineModel 宏,简化双向绑定:

java 复制代码
<script setup>
const model = defineModel()

function update() {
  model.value++
}
</script>

<template>
  <div>父组件Dignity的 v-model 值为: {{ model }}</div>
  <button @click="update">Increment</button>
</template>

实现原理​​:

defineModel() 会被编译为 props 和 emits:

java 复制代码
const props = defineProps({ modelValue: Number })
const emit = defineEmits(['update:modelValue'])

defineModel 是一个便利宏,其返回的值是一个 ref 。它可以像其他 ref 一样被访问以及修改,不过它能起到在父组件和当前变量之间的双向绑定的作用。

响应式代理:返回的 model 实际上是一个计算属性:

java 复制代码
const model = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})

五、属性透传与组件通信

useAttrs 方法

获取未声明的 props 和事件:

java 复制代码
import { useAttrs } from 'vue'

let $attrs = useAttrs()

使用 $attrs 批量传递属性:

java 复制代码
<el-button :="$attrs"></el-button>

优先级规则​ ​:已通过 defineProps 声明的属性不会出现在 $attrs 中。

子组件暴露数据:

java 复制代码
let name = ref('Dignity_')

defineExpose({
  name
})

父组件访问子组件:

java 复制代码
const child = ref()

// 访问子组件暴露的数据
console.log(child.value.name)

子组件访问父组件:$parent

六、Pinia 状态管理

vue3的pinia相当于vue2的vuex

只有state、actions、getters

选项式 API

java 复制代码
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++
    }
  }
})

组合式 API

可以用到vue3的语法来配合pinia,computed就是getters,ref就是state,至于actions就是一个函数

java 复制代码
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useTodoStore = defineStore('todo', () => {
  const todos = ref([])
  const count = computed(() => todos.value.length)
  
  function addTodo(todo) {
    todos.value.push(todo)
  }

  return { todos, count, addTodo }
})

七、插槽

作用域插槽

可以传递数据的插槽,子组件可以将数据回传给父组件,父组件可以决定这些回传的数据是以何种结构或者外观在子组件内部去展示!

子组件实现

java 复制代码
<template>
  <ul>
    <li v-for="(item, index) in todos" :key="item.id">
      <slot :row="item" :index="index"></slot>
    </li>
  </ul>
</template>

<script setup>
defineProps(["todos"])
</script>

父组件使用

java 复制代码
<Test1 :todos="todos">
  <template v-slot="{ row, index }">
    <p :style="{ color: row.done ? 'green' : 'red' }">
      {{ row.title }}--{{ index }}
    </p>
  </template>
</Test1>

具名插槽与默认插槽

java 复制代码
<!-- 子组件 -->
<template>
  <div>
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
  </div>
</template>

<!-- 父组件使用 -->
<ChildComponent>
  <template #header>
    <h1>这是标题</h1>
  </template>
  
  <p>这是默认插槽内容</p>
</ChildComponent>

至此撒花~

后记

事件处理​​:优先使用 emits 明确组件通信接口

​状态管理​​:简单场景用 reactive/ref,复杂场景用 Pinia

​组件设计​​:

  • 使用 defineProps/defineEmits 明确组件接口

  • 通过 defineExpose 控制暴露内容

  • 合理使用插槽增强组件灵活性

性能优化:

  • 避免不必要的属性透传

  • 合理使用 computed 缓存派生状态

  • 对于大型列表,考虑作用域插槽的性能优势

我们在实际项目中或多或少遇到一些奇奇怪怪的问题。

自己也会对一些写法的思考,为什么不行🤔,又为什么行了?

最后,祝君能拿下满意的offer。

我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车

👍 如果对您有帮助,您的点赞是我前进的润滑剂。

以往推荐

别在傻傻分不清any void never unknown的场景啦

Vue性能优化:从加载提速到运行时优化

vue2和Vue3和React的diff算法展开说说:从原理到优化策略

玩转Vue插槽:从基础到高级应用场景(内含为何Vue 2 不支持多根节点)

前端哪有什么设计模式

前端仔,快把dist部署到Nginx上

多图详解,一次性啃懂原型链(上万字)

Vue-Cli3搭建组件库

VuePress搭建项目组件文档

原文链接

juejin.cn/post/751311...

相关推荐
要加油哦~5 分钟前
vue · 插槽 | $slots:访问所有命名插槽内容 | 插槽的使用:子组件和父组件如何书写?
java·前端·javascript
先做个垃圾出来………14 分钟前
split方法
前端
前端Hardy1 小时前
HTML&CSS:3D图片切换效果
前端·javascript
spionbo1 小时前
Vue 表情包输入组件实现代码及完整开发流程解析
前端·javascript·面试
全宝1 小时前
✏️Canvas实现环形文字
前端·javascript·canvas
lyc2333331 小时前
鸿蒙Core File Kit:极简文件管理指南📁
前端
我这里是好的呀1 小时前
全栈开发个人博客12.嵌套评论设计
前端·全栈
我这里是好的呀1 小时前
全栈开发个人博客13.AI聊天设计
前端·全栈
金金金__1 小时前
Element-Plus:popconfirm与tooltip一起使用不生效?
前端·vue.js·element