修改信息

请上传图片,内容大小不得超过2MB

修改密码

相关导航

表独立兮山之上 云容容兮而在下
一名全栈·学习者
记录生活学习技术
希望本站对你有帮助

Lirous

cv调库虾

L
L
i
i
r
r
o
o
u
u
s
s
c
c
o
o
d
d
i
i
n
n
g
g
介绍一下vue中的父子组件通信,内含v-model defineModel prop defineProps defineEmits 以及vue中JavaScript表达式的介绍

组件通信

在实际开发中,父子组件通信是十分常见的 所谓通信就是数据和事件的相互传递 有父组件向子组件传递数据,子组件向父组件发出事件,还有父子组件的数据双向绑定

在vue中,我们推崇单向数据流,但这不意味着双向数据流有着何种劣势,在特定的场景下,我们也十分建议使用双向数据绑定

双向绑定

v-model

<template>
  <label>我是输入框: </label>
  <input type="text" v-model="content">

  <div class="mt-5">
    我是content: {{ content }}
  </div>
</template>

<script setup>
import { ref } from 'vue'
const content = ref("")
</script>

实现效果 v-model例子 可以使用v-model的元素 <input> <textarea> <select>

对于组件,可以使用model选项来定制v-model的作用,下面的defineModel中我们会说到

vue官方文档中关于v-model的介绍

defineModel

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

参数介绍 第一个参数是绑定名称 第二个参数是一个配置对象,用于指定绑定值的类型、是否必填及默认值

//defineModel每次只能定义一个模型
//如果有多个要申明的model要写就要写多个
const modelName = defineModel("modelName",{
	type:xx,       //值类型
	required:xx,   //bool 值是否必传 如果为true 但不传控制台会抛出警告
	default:xx,    //默认值
})

父组件代码展示

<!--parent-->
<template>
  <div>
    <label>父亲组件</label>
    <input v-model="ctx">
  </div>

  <div>
    <label>孩子组件</label>
    <Child v-model:content="ctx" />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import Child from "./Child.vue"
const ctx = ref("")
</script>

子组件代码展示

<!--child-->
<template>
    <input v-model="ctx1" />
</template>

<script setup>
const ctx1= defineModel("content", {
    type: String,
    required: false,
    default: "xxoo"
})
//这里为了更好理解,故使用了不一样的名字,以免造成误会
</script>

实现效果 defineModel实现双向绑定

代码解释 父组件声明了一个响应式ctx,并使用v-model与父组件中的input绑定 子组件中使用defineModel自定义了一个v-model,名字为content,并赋值给ctx1(上文说过了这个事一个响应式ref,这里再强调一下) 然后将ctx1与子组件的input绑定 关键点就在于父组件中的

<Child v-model:content="ctx" />

这里的意思是将该组件(父组件)中的ctx于子组件中定义的 content(defineModel的第一个参数值)进行双向绑定

由此,我们就知道了内在逻辑,父组件input绑定了ctx,子组件自定义为content的v-model绑定了也ctx,所以我们无论修改哪个输入框,另外一个输入框都会同步改变(非拼字阶段) 具体参考官方文档

更多v-model详细 具体参考官方文档

父传子

prop介绍

prop 指的是组件的属性(property)

prop和attribute的不同

"prop"全称是"property"(属性)的缩写,专指在Vue组件中定义可以从外部传入的属性值
而"attribute"一词更广义,指HTML标签上的属性。在Vue模板渲染过程中,prop实际上会被编译为相应HTML元素的attribute
在 Vue.js 中,prop 用于父组件向子组件传递数据。子组件通过声明所需要的 prop,就可以像数据属性一样访问由父组件传递的值

prop的特点

prop 有以下几个主要特点:

  1. 单向数据流:prop 是单向绑定的,当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意修改父组件的状态数据。

    "单向数据流"指的是prop的数据只能由父组件传递给子组件,子组件不能直接修改prop。但这并不意味着prop的值就是静态不变的

  2. 类型验证:我们可以为每个 prop 指定类型,如果传入的数据不符合要求,Vue 会在浏览器控制台发出警告
  3. 必传与默认值:可以指定 prop 是否必传,对于非必传的prop,我们也可以指定默认值

defineProps

defineProps用于子组件,申明一些prop,由父组件传递,支持js表达式

参数介绍

//props默认是仅仅可读的
//不同意 defineModel 的是 defineProps 可以一次声明多个prop,它接收一个对象作为参数,对象的每个键值对代表一个 prop 以及它的选项 这
const props = defineProps({
    props1: {
        type: xx,       //值类型
        required: xx,   //bool 值是否必传 如果为true 但不传控制台会抛出警告
        default: xx,    //默认值
    },
    props2: {
        // ...
    }
})

父组件

<!-- parent-->
<template>
  <Child title="essay.title" content="essay.content" :page="1" />
</template>

<script setup>
import { ref } from "vue";
import Child from "./Child.vue"
const essay = ref({
  title: "这是一篇文章",
  content: "这是文章内容",
})
</script>

子组件

<!-- child-->
<template>
    <div> 题目 {{ props.title }}</div>
    <div> 内容 {{ props.content }}</div>
    <div> 页码 {{ props.page }}</div>
    <div> 作者 {{ props.author }}</div>
</template>

<script setup>
const props = defineProps({
    title: {
        type: String,
        required: true
    },
    content: {
        type: String,
        required: true
    },
    page: {
        type: Number,
        required: true
    },
    author: {
        type: String,
        default: "罹景偓佺"
    }
})
</script>

实现效果

字符串之间渲染 我们会看见结果不符合预期,并且控制台报警提示我们的page类型错误(期待一个Number类型但传递的是一个String类型)

js表达式解决数据类型问题

在Vue的模板语法中,我们可以在指令或者一些特殊的属性前面使用 : 前缀,这样该属性后面的值就会被当作一个JavaScript表达式而不是字符串

具体参考官方文档
使用JavaScript表达式后的代码

<!-- parent-->
<template>
  <Child :title="essay.title" :content="essay.content" :page="1" />
</template>

<script setup>
import { ref } from "vue";
import Child from "./Child.vue"
const essay = ref({
  title: "这是一篇文章",
  content: "这是文章内容",
})
</script>

实现效果

使用JavaScript之后的正确渲染

defineProps的单向数据流是支持动态绑定的,也就是说JavaScript表达式子里面的响应式数据能被正确追踪,即父组件修改响应式数据的值,子组件对应的值也会修改

更多defineProps详细 具体参考官方文档

子传父

defineExpose

<!--parent-->
<template>
  <child ref="childRef"></child>
</template>

<script setup>
import { ref, onMounted } from "vue";
import child from "./child.vue";
const childRef = ref(null);

onMounted(() => {
  console.log(childRef.value.childText); //我是子组件的内容
  childRef.value.childFunc(); //触发子组件事件
});
</script>

子组件

<!--child-->
<template>
  <div>我是子组件</div>
</template>

<script setup>
const childText = "我是子组件的内容";
const childFunc = () => {
  console.log("触发子组件事件");
};

defineExpose({
  childText,
  childFunc,
});
</script>

defineExpose用于以定义对象的形式实现导出子组件的数据或方法 父组件中得到子组件的ref即可调用 tips:要注意生命周期导致ref是否为null的问题

defineEmits

defineEmits 是 Vue3 组合式 API 中用于定义组件触发事件的方式。它允许组件通过触发自定义事件,将数据传递给父组件,从而实现组件之间的通信

在子组件中,我们可以使用 defineEmits 声明将要触发的事件名称,并使用 emits 函数来触发事件并传递数据。在父组件中,可以使用 v-on 或 @ 监听子组件触发的事件,并接收传递的数据

举个例子,点击子组件中的一个按钮触发一个事件,该事件触发emits,实现子组件向父组件发送一个事件和数据,从而实现组件通信,这个是作者认为比较直观的写法,当然因人而异,只要能理解和运用就是好的

父组件

<!--parent-->
<template>
  <Child @foo="handelFoo"></Child>
</template>

<script setup>
import Child from "./Child.vue"
const handelFoo = (parms) => {
  console.log(parms) //这是子组件传过来的参数
}
</script>
  • 子组件
<!--child-->
<template>
    <button @click="funcFoo">子组件emit发送事件</button>
</template>

<script setup>
const emits = defineEmits(["foo"])
const funcFoo = () => {
    emits("foo", "这是子组件传过来的参数")
}
</script>

在这个例子中,我们点击子组件触发funcFoo函数,然后funcFoo函数中调用emits向父组件发送一个事件foo并传递相应数据,同时父组件监听这个foo事件,已经获得其传递的数据

!!!说到这里,你可能还没觉得这一功能有多么强大,但当我们实际使用之后,你一定会被该功能的强大和便利所震惊的

emits不同的使用方法

1.数组形式

defineEmits(['eventName', 'eventName2'])
  1. 对象形式
defineEmits({
  eventName: null, // 没有验证函数
  eventName2(payload) { // 验证函数
    return payload instanceof Object
  }
})
  1. 对象形式,同时定义 emits 选项
const emits = defineEmits({
  _,
  eventName: null,
  eventWithValidation(payload) {
    // 做一些验证
    return true
  }
})

我的建议 1.在大多数情况下,使用数组形式的 defineEmits 就已经足够了,它简单直接,易于理解和维护 2.如果需要对事件参数进行验证,我们完全可以在触发事件的地方或事件处理函数中进行验证,而不需要将验证逻辑放在 defineEmits 中。这样可以更好地遵循关注点分离的原则 3.使用对象形式定义验证函数的方式,主要是为了在极端情况下防止意外触发事件。但是在实际开发中,我们通常更倾向于信任开发人员不会错误地触发事件。过度使用验证函数可能会导致代码过于臃肿和难以维护

更多define详细 具体参考官方文档

使用props和emit实现v-model

接下来我们要使用props和emit来实现一个伪双向绑定的功能

以下是代码演示

父组件

<!-- parent-->
<template>
  <div>实现逻辑 父组件内容更新 传递给子组件 实现同步</div>
  <div>实现逻辑 子组件内容更新 传递给父组件 实现同步</div>
  <div>
    <label>父组件</label>
    <input v-model="content">
  </div>
  <div>
    <label>子组件</label>
    <Child :modelValue="content" @update:modelValue="handelUpdate" />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import Child from "./Child.vue"
const content = ref("")
const handelUpdate = (ret) => {
  content.value = ret
}
</script>

子组件

<!-- child-->
<template>
    <input @input="update" :value="props.modelValue" />
</template>

<script setup>
const emits = defineEmits(["update:modelValue"])
const props = defineProps({
    modelValue: {
        type: String,
        required: true,
    }
})
const update = (event) => {
    emits("update:modelValue", event.target.value)
}
</script>

实现效果 使用props和emits实现的动态绑定

  • 代码解释

    我们在父组件申明了一个content响应式数据,并使用prop传递的子组件,实现单向绑定,现在修改父组件的内容子组件能做出同步变化
    子组件中定义了emits,同时监听了input元素的input事件,触发input事件时update函数会调用emits向父组件发送一个update:modelValue事件和当前输入框内容的值
    父组件中监听子组件的update:modelValue事件,接收到子组件中输入框的值,然后修改content的值,实现子组件和父组件的单向绑定,从而实现伪造的双向绑定

注意的点,为什么不在子组件修改prop呢?
原因很简单,prop默认是只读的,vue官方的设计理念也是让prop作为一个单向的数据流,所以我们在子组件的update函数中不是之间修改modelValue的值(虽然我们可以将该props设为可写,但这种做法是不推荐的,也是不专业的)

参考文档: 组件 v-model

defineModel props emits defineExpose

于2024-09-23 20:04:55更新
评论区
0/800
登录后才可以评论
更多评论
近期文章