Vue3基础(26)___defineProps、defineEmits、defineExpose、defineModel组件通信、defineOptions

简介: 本文介绍了Vue 3中`<script setup>`语法糖的使用,以及如何在Vue 3的组合式API中使用`defineProps`、`defineEmits`、`defineExpose`和`defineModel`进行组件通信和属性暴露。同时,还解释了`defineOptions`的作用,它可以用来配置组件的选项,例如禁用属性的自动继承。

defineProps、defineEmits、defineExpose组件通信

在使用这个之前,我们需要知道setup的语法糖写法,因为上面的三个api需要在这个语法糖中才能使用:

<script setup>
    console.log('LiuQing')
</script>

里面的代码会被编译成组件 setup() 函数的内容。
这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行。

官方解释:

<script setup> 
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。
相比于普通的 <script> 语法,它具有更多优势:
1、更少的样板内容,更简洁的代码。
2、能够使用纯 TypeScript 声明 props 和自定义事件。
3、更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)4、更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)

顶层的绑定会被暴露给模板:

<template>
  name: {
   {
    name }} <br />
  age: {
   {
    age }}
</template>

<script setup>
    import {
    ref } from 'vue'
    const name = ref('LiuQing')
    const age = ref(18)
</script>

页面:
在这里插入图片描述
不需要主动暴露return。

使用组件:

<template>
  name: {
   {
    name }} <br />
  age: {
   {
    age }}
  <Children2/>
</template>

<script setup>
import Children2 from './components/Parent/Children2.vue'
import {
    ref } from 'vue'
const name = ref('LiuQing')
const age = ref(18)
</script>

子组件:

<template>我是子组件哦</template>

页面:
在这里插入图片描述

defineProps、defineEmits、defineExpose这三个api我们可以直接使用并不需要在vue中单独暴露出来

defineProps

<template>
  我是子组件哦
  <p>子组件得到的name:{
   {
    props.name }}</p>
  <p>子组件得到的age:{
   {
    props.age }}</p>
</template>
<script setup>
import {
    defineProps, defineEmits, defineExpose } from 'vue'
const props = defineProps({
   
  name: {
   
    type: String,
    default: "六卿",
  },
  age: {
   
    type: Number,
    default: 279
  }
})
</script>

其实我们使用的时候完全可以不导入。
目前页面:
在这里插入图片描述
因为我们目前还没有给子组件传入name、age,所以使用的默认值。
在父组件传入name、age:

<template>
  name: {
   {
    name }} <br />
  age: {
   {
    age }}
  <Children2 :name="name" :age="age" />
</template>

<script setup>
import Children2 from './components/Parent/Children2.vue'
import {
    ref } from 'vue'
const name = ref('LiuQing')
const age = ref(18)
</script>

写法完全和v2一致,使用v-bind或者:即可达到效果。
当前页面:
在这里插入图片描述
已经收到了传入的值。

defineEmits

defineProps为了解决子组件接受父组件传入的值,那defineEmits就是子组件给父组件传值,当然这个传值是以回调函数的形式传送。
使用的前提是需要父组件传入回调函数,子组件使用defineEmits接受,触发事件。

<template>
  name: {
   {
    name }} <br />
  age: {
   {
    age }}
  <Children2 :name="name" :age="age" @changeName="changeName" />
</template>

<script setup>
import Children2 from './components/Parent/Children2.vue'
import {
    ref } from 'vue'
const name = ref('LiuQing')
const age = ref(18)
const changeName = (newName) => {
   
  name.value = newName
}
</script>

子组件使用:

<template>
  我是子组件哦
  <p>子组件得到的name:{
   {
    props.name }}</p>
  <p>子组件得到的age:{
   {
    props.age }}</p>
  <button @click="changeName('六卿')">改变name为六卿</button>
</template>
<script setup>
import {
    defineProps, defineEmits, defineExpose } from 'vue'
const props = defineProps({
   
  name: {
   
    type: String,
    default: "六卿",
  },
  age: {
   
    type: Number,
    default: 279
  }
})

const emits = defineEmits(['changeName'])
const changeName = (name) => {
   
  console.log("改变name")
  emits('changeName', name)
}
</script>

页面:
在这里插入图片描述
点击后页面:
在这里插入图片描述

defineExpose

用于父组件要使用子组件中的方法或者属性的时候,子组件暴露给父组件,父组件配合ref使用:

子组件:

<template>
  我是子组件哦
  <p>子组件得到的name:{
   {
    props.name }}</p>
  <p>子组件得到的age:{
   {
    props.age }}</p>
  <button @click="changeName('六卿')">改变name为六卿</button>
  ------------------------------------------<br />
  count :{
   {
    count }}
</template>
<script setup>
import {
    defineProps, defineEmits, defineExpose, ref } from 'vue'
const props = defineProps({
   
  name: {
   
    type: String,
    default: "六卿",
  },
  age: {
   
    type: Number,
    default: 279
  }
})
const emits = defineEmits(['changeName'])
const changeName = (name) => {
   
  console.log("改变name")
  emits('changeName', name)
}
const count = ref(100)
const changeCount = () => {
   
  count.value = --count.value
}
defineExpose({
   
  count,
  changeCount
})
</script>

父组件:

<template>
  <button @click="changeCount">父组件按钮__改变count的值</button>
  name: {
   {
    name }} <br />
  age: {
   {
    age }}
  <Children2 ref="child" :name="name" :age="age" @changeName="changeName" />
</template>

<script setup>
import Children2 from './components/Parent/Children2.vue'
import {
    ref } from 'vue'
const name = ref('LiuQing')
const age = ref(18)
const changeName = (newName) => {
   
  name.value = newName
}
const child = ref(null)
const changeCount = () => {
   
  console.log(child.value.count, 'child.value.count')
  console.log(child.value.changeCount, 'child.value.changeCount')
  child.value.changeCount()
}
</script>

页面:
在这里插入图片描述

子组件暴露,父组件ref接受。

defineModel

从 Vue 3.4 开始,推荐的实现方式是使用 defineModel() 宏:

<!-- Child.vue -->
<script setup>
const model = defineModel()

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

<template>
  <div>parent bound v-model is: {
   {
    model }}</div>
</template>

父组件可以用 v-model 绑定一个值:

<!-- Parent.vue -->
<Child v-model="count" />

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

它的 .value 和父组件的 v-model 的值同步;
当它被子组件变更了,会触发父组件绑定的值一起更新。
这意味着你也可以用 v-model 把这个 ref 绑定到一个原生 input 元素上,在提供相同的 v-model 用法的同时轻松包装原生 input 元素:

<script setup>
const model = defineModel()
</script>

<template>
  <input v-model="model" />
</template>

其实就是起到了 我们之前接收props和自动触发updata的方法。针对上面例子自己可以watch一下 然后出发相关逻辑

底层机制

defineModel 是一个便利宏。编译器将其展开为以下内容:

一个名为 modelValue 的 prop,本地 ref 的值与其同步;
一个名为 update:modelValue 的事件,当本地 ref 的值发生变更时触发。
在 3.4 版本之前,你一般会按照如下的方式来实现上述相同的子组件:

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

<template>
  <input
    :value="props.modelValue"
    @input="emit('update:modelValue', $event.target.value)"
  />
</template>

如你所见,这显得冗长得多。然而,这样写有助于理解其底层机制。

因为 defineModel 声明了一个 prop,你可以通过给 defineModel 传递选项,来声明底层 prop 的选项:

// 使 v-model 必填
const model = defineModel({
    required: true })

// 提供一个默认值
const model = defineModel({
    default: 0 })

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

defineOptions

如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false。
从 3.3 开始你也可以直接在

<script setup>
defineOptions({
   
  inheritAttrs: false
})
// ...setup 逻辑
</script>

最常见的需要禁用 attribute 继承的场景就是 attribute 需要应用在根节点以外的其他元素上。通过设置 inheritAttrs 选项为 false,你可以完全控制透传进来的 attribute 被如何使用。

这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问到。

<span>Fallthrough attribute: {
   {
    $attrs }}</span>

这个 $attrs 对象包含了除组件所声明的 props 和 emits 之外的所有其他 attribute,例如 class,style,v-on 监听器等等。

有几点需要注意:

和 props 有所不同,透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像 foo-bar 这样的一个 attribute 需要通过 $attrs[‘foo-bar’] 来访问。

像 @click 这样的一个 v-on 事件监听器将在此对象下被暴露为一个函数 $attrs.onClick。

现在我们要再次使用一下之前小节中的 组件例子。有时候我们可能为了样式,需要在 元素外包装一层

<div class="btn-wrapper">
  <button class="btn">click me</button>
</div>

我们想要所有像 class 和 v-on 监听器这样的透传 attribute 都应用在内部的 上而不是外层的

上。我们可以通过设定 inheritAttrs: false 和使用 v-bind=“$attrs” 来实现:

<div class="btn-wrapper">
  <button class="btn" v-bind="$attrs">click me</button>
</div>

在这里插入图片描述

在这里插入图片描述

目录
相关文章
|
5天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
109 64
|
5天前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
|
19天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
19天前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
27天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
56 7
|
27天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
46 1
|
27天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
49 1
|
12天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
28 1
vue学习第四章
|
12天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
25 1
vue学习第九章(v-model)
|
12天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
27 1
vue学习第十章(组件开发)