Vue 3 父子组件传递数据的几种通信方式 (Prop、自定义事件、v-model...)
对比 Vue 2 的相关变化- `v-on` 的 `.native` 修饰符已被移除。- Vue 3 现在提供一个 `emits` 选项,和现有的 `props` 选项类似。`v-bind` 的 `.sync` 修饰符和组件的 `model` 选项已移除父组件向子组件传递数据( Prop )子组件向父组件传递数据( 自定义事件 )父子组件双向数据绑定( v-model )其他方式( 插槽、Prov
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 沿着组件链逐级传递下去,可能会很麻烦。
对于这种情况,可以使用一对 provide
和 inject
。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。父组件有一个 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 的内容,请参考 官方文档 。
更多推荐
所有评论(0)