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


    defineProps、defineEmits、defineExpose组件通信

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

    <script setup>
    	console.log('LiuQing')
    </script>
    
    里面的代码会被编译成组件 setup() 函数的内容。
    这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    官方解释:

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

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

    <template>
      name: {{ name }} <br />
      age: {{ age }}
    </template>
    
    <script setup>
    	import { ref } from 'vue'
    	const name = ref('LiuQing')
    	const age = ref(18)
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    页面:
    在这里插入图片描述
    不需要主动暴露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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    子组件

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

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

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    其实我们使用的时候完全可以不导入。
    目前页面:
    在这里插入图片描述
    因为我们目前还没有给子组件传入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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    写法完全和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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    子组件使用:

    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

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

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    父组件:

    <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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

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

    子组件暴露,父组件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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

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

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其实就是起到了 我们之前接收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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

    // 使 v-model 必填
    const model = defineModel({ required: true })
    
    // 提供一个默认值
    const model = defineModel({ default: 0 })
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    defineOptions

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

    <script setup>
    defineOptions({
      inheritAttrs: false
    })
    // ...setup 逻辑
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

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

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

    这个 $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>
    
    • 1
    • 2
    • 3

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

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

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

    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    leetcode - 1838. Frequency of the Most Frequent Element
    网工内推 | base郑州,上市公司,最高15薪,五险一金全额缴
    Python----用户界面猜数字游戏()、tkinter库函数
    学生宿舍管理系统(前端java+后端Vue)实现-含前端与后端程序
    LeetCode【100】单词拆分
    如何设计鞋材出库入账管理系统
    “蔚来杯“2022牛客暑期多校训练营3
    一种通过nacos动态配置实现多租户的log4j2日志物理隔离的设计
    java毕业设计“传情旧物”网站(附源码、数据库)
    计算机毕业设计JavaSneaker’sHome设计网站(源码+系统+mysql数据库+lw文档)
  • 原文地址:https://blog.csdn.net/qq_43291759/article/details/127884800