Vue 3 父子组件传递数据的几种通信方式 (Prop、自定义事件、v-model...)

Vue 3 官方文档 (中文):https://v3.cn.vuejs.org/guide/introduction.html

对比 Vue 2 的一些变化

  • v-on.native 修饰符已被移除。
  • Vue 3 现在提供一个 emits 选项,和现有的 props 选项类似。
    这个选项可以用来定义一个组件可以向其父组件触发的事件。
  • 用于自定义组件时:
    • v-model prop 和事件默认名称已更改:
      prop:value -> modelValue
      事件:input -> update:modelValue
    • v-bind.sync 修饰符和组件的 model 选项已移除,可在 v-model 上加一个参数代替。
    • 现在可以在同一个组件上使用多个 v-model 绑定。
    • 现在可以自定义 v-model 修饰符。

关于 Vue 3 相对 Vue 2 变更的更多内容,请参考 官方文档

父组件向子组件传递数据( Prop )

Prop 是在组件上注册的一些自定义属性,可以通过 props 选项来定义 prop 列表。

父组件可以通过子组件中定义的 prop 向子组件传递数据。当一个值被传递给一个 prop 属性时,它就成为该组件实例中的一个 property,该 property 的值与其他 property 一样可以在模板中访问。

一个组件可以拥有任意数量的 prop,并且在默认情况下,无论任何值都可以传递给 prop。

例如,父组件将一个名字传递给子组件:

子组件: 定义 name 属性来接收传递的值

// ChildComponent.vue

data() {
  return {
    username: '',
  };
},
props: {
  name: String,
},
watch: {
  name(value) {
    this.username = value;
  },
},

注意: 这里的 name 定义为 String 类型,还可以指定为其他类型。name 的值是可以直接用的,这里是定义了一个 username 用来接收传入的 name 的最新值。

父组件: 在模板中将值传给 name

传递一个静态的值:

<ChildComponent name="zhangsan"></ChildComponent>

可以通过 v-bind 或简写 : 动态赋值:

<ChildComponent :name="username"></ChildComponent>
data() {
  return {
    username: 'zhangsan',
  };
},

注意: 所有的 prop 在父子组件之间是 单向下行绑定 ,即:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止子组件意外变更父级组件的状态,从而导致数据流向难以理解。

另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。所以不要在子组件内部改变 prop,如果改了,Vue 会在浏览器的控制台中发出警告。

关于 Prop 更多的内容,请参考 官方文档

子组件向父组件传递数据( 自定义事件 )

组件实例提供了一个自定义事件的系统。

父级组件可以像处理原生 DOM 事件一样通过 v-on@ 监听子组件实例中的任意事件。

子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件,并且通过 emits 选项来定义发出的事件列表。

例如,子组件将一个名字抛出到父组件:

子组件: 触发一个事件 update:name

<button @click="updateName"></button>
// ChildComponent.vue

data() {
  return {
    username: 'zhangsan',
  };
},
emits: ['update:name'],
methods: {
  updateName() {
    this.$emit('update:name',this.username);
  }
},

注意: 可以使用 $emit 的第二个参数来提供要抛出的值。关于 $emit 方法的更多内容,请参考 官方文档

如果在 emits 选项中定义了原生事件 (如 click ) 时,那么组件中的事件将替代原生事件侦听器。

父组件: 监听 update:name 事件

<ChildComponent ... @update:name="username = $event"></ChildComponent>
data() {
  return {
    username: '',
  };
},

注意: 当在父级组件监听这个事件的时候,我们可以通过 $event 访问到被抛出的值。这里用 username 来接收子组件抛出的名字。

如果事件处理函数是一个方法,那么抛出的值将会作为第一个参数传入这个方法。例如:

<ChildComponent ... @update:name="update"></ChildComponent>
data() {
  return {
    username: '',
  };
},
methods: {
  update(name) {
    this.username = name;
  }
},

关于自定义事件的更多内容,请参考 官方文档

父子组件双向数据绑定( v-model )

默认情况下,组件上的 v-model 使用 modelValue 作为 prop,update:modelValue 作为事件。如果想要修改这些名称,可以向 v-model 传递参数来实现。

另外,v-model 的内置修饰符也可以使用,不仅如此,还可以添加自定义修饰符来处理绑定的数据。

使用不带参数的 v-model

例如,创建一个自定义输入组件:

app.component('custom-input', {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  template: `
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    >
  `
})

在父级组件上使用该组件:

<custom-input v-model="searchText"></custom-input>

它等价于:

<custom-input
  :modelValue="searchText"
  @update:modelValue="searchText = $event"
></custom-input>

使用带参数的 v-model

例如,创建一个子组件:

app.component('ChildComponent', {
  data() {
    return {
      username: '',
    };
  },
  props: {
    name: String,
  },
  emits: ['update:name'],
  watch: {
    name(value) {
      this.username = value;
    },
  },
  template: `
    <button @click="$emit('update:name',this.username)"></button>
  `
})

在父级组件上使用该组件:

<ChildComponent v-model:name="username"></ChildComponent>

注意: 可以在单个组件实例上创建多个 v-model 绑定,每个 v-model 将同步到不同的 prop 。

使用 v-model 修饰符

v-model 的内置修饰符有 .trim.number.lazy 。除此之外,可以添加自定义修饰符,添加到组件 v-model 的修饰符将通过 prop modelModifiers 提供给组件。

例如,使用内置修饰符 .trim

<my-component v-model.trim="myText"></my-component>

关于 v-model 修饰符的更多内容,请参考 官方文档

其他方式( 插槽、Provide / Inject )

插槽

在 HTML 中向组件传递内容,可以通过使用 Vue 的自定义 <slot> 元素来实现,例如:

子组件中使用 <slot> 作为要插入内容的占位符:

app.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

在父组件中向子组件传递内容,内容将插入到 <slot> 位置:

<alert-box>
  Something bad happened.
</alert-box>

更多关于插槽的内容,请参考 官方文档

Provide / Inject

对于一些深层嵌套的组件,深层的子组件只需要父组件的部分内容,这时如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。

对于这种情况,可以使用一对 provideinject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

例如,父组件提供一个数据 user

app.component('todo-list', {
  data() {
    return {
      todos: ['Feed a cat', 'Buy tickets']
    }
  },
  provide: {
    user: 'John Doe'
  },
  template: `
    <div>
      {{ todos.length }}
      <!-- 模板的其余部分 -->
    </div>
  `
})

深层的子组件可以直接拿到数据,而不需要经过层层传递:

app.component('todo-list-statistics', {
  inject: ['user'],
  created() {
    console.log(`Injected property: ${this.user}`) // > 注入的 property: John Doe
  }
})

在这个过程中,父组件不需要知道哪些子组件使用了它 provide 的 property,子组件也不需要知道 inject 的 property 来自哪里。

注意: 默认情况下,provide/inject 绑定并不是响应式的。要想对数据的更改做出响应,就需要使用到组合式 API 。

在这里不得不插一嘴,我个人觉得 组合式 API 是真的好用!

更多关于 Provide / Inject 的内容,请参考 官方文档

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

toollong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值