一. 父组件向子组件传值
- 创建 parent 和 child 组件,并在 parent 中注册 child 组件
- 在父组件调用子组件标签中添加一个自定义属性(msg),
<template>
<div class="parent">
<Child :msg="parentMsg"></Child>
</div>
</template>
<script>
import Child from './child.vue';
export default {data() {
return {parentMsg: "我是传给子组件的值"};
},
components: {Child},
}
</script>
- 在子组件的 props 中将 parent 中自定义的属性 (msg) 添加进去, 然后就可以像 data 中定义的属性一样使用
<template>
<div class="child">
<p>{{msg}}</p>
</div>
</template>
<script>
export default {
props: {
msg:{
type:String,
default:""
}
}
}
</script>
二. 子组件向父组件传值
- 在父组件中的子标签中监听该自定义事件 (listenChildEvent) 并添加一个响应该事件的处理方法(acceptMsgFromChild)
<template>
<div class="parent">
<Child :msg="parentMsg" @listenChildEvent="acceptMsgFromChild"></Child>
</div>
</template>
<script>
import Child from './child.vue';
export default {data() {
return {parentMsg: "我是传给子组件的值"};
},
components: {Child},
methods:{acceptMsgFromChild(data){console.log(data);// 或者将接受值处理
}
}
}
</script>
- 在子组件中创建一个按钮,给按钮绑定一个点击事件
- 在响应该点击事件的函数 (sendMsgToParent) 中使用 $emit 来触发父组件的自定义事件(listenChildEvent),并传递一个参数
<template>
<div class="child">
<p>{{msg}}</p>
<button @click="sendMsgToParent"> 向父组件传值 </button>
</div>
</template>
<script>
export default {
props: {
msg:{
type:String,
default:""
}
},
methods:{sendMsgToParent(){this.$emit("listenChildEvent","this message is from child");
}
}
}
</script>
三. 父子组件使用 v -model 实现组件通信
我们在使用别人的组件库的时候,经常是通过 v -model 来控制一个组件显示和隐藏的效果,例如:弹框,下面一步一步的解开 v -model 的什么面纱
提到 v -model 首先想到的就是我们对于表单用户数据的双向数据绑定,操作起来很简洁很粗暴,例如:
<input type="text" v-model="msg">
data () {
return {msg: ''}
}
其实 v -model 是个语法糖,上面这一段代码和下面这一段代码是一样的效果:
<input type="text" :value="msg" @input="msg = $event.target.value">
data () {
return {msg: ''}
}
由此可以看出,v-model=”msg” 实则是 :value=”msg” @input=”msg = $event.target.value” 的语法糖。这里其实就是监听了表单的 input 事件,然后修改:value 对应的值。除了在输入表单上面可以使用 v -model 外,在组件上也是可以使用的,这点官网有提到,但是介绍的不是很详细,导致刚接触的小伙伴会有一种云里雾里不知所云的感觉。既然了解了 v -model 语法糖本质的用法,那么我们就可以这样实现父子组件的双向数据绑定:
以上原理实现方法,写法 1:
父组件用法:
<empty v-model="msg"></empty>
子组件写法:
// 点击该按钮触发父子组件的数据同步
<div class="share-btn" @click="confirm"> 确定 </div>
// 接收父组件传递的 value 值
// 注意,这种实现方法,这里只能使用 value 属性名
props: {
value: {
type: Boolean,
default: false
}
},
methods: {confirm () {
// 双向数据绑定父组件:value 对应的值
// 通过 $emit 触发父组件 input 事件,第二个参数为传递给父组件的值,这里传递了一个 false 值
// 可以理解为最上面展示的 @input="msg = $event.target.value" 这个事件
// 即触发父组件的 input 事件,并将传递的值‘false’赋值给 msg
this.$emit('input', false)
}
}
这种方式实现了父子组件见 v -model 双向数据绑定的操作,例如你可以试一下实现一个全局弹窗组件的操作,通过 v -model 控制弹窗的显示隐藏,因为你要在页面内进行某些操作将他显示出来,控制其隐藏的代码是写在组件里面的,当组件隐藏了对应的也要父组件对应的值改变。
以上这种方式实现的父子组件的 v -model 通信,虽可行,但限制了我们必须 popos 接收的属性名为 value 和 emit 触发的必须为 input,这样就容易有冲突,特别是在表单里面。所以,为了更优雅的使用 v -model 通信而解决冲突的问题,我们可以通过在子组件中使用 model
选项,下面演示写法 2:
父组件写法:
<empty v-model="msg"></empty>
子组件写法:
<div class="share-btn" @click="confirm"> 确定 </div>
// model 选项用来避免冲突
// prop 属性用来指定 props 属性中的哪个值用来接收父组件 v -model 传递的值
// 例如这里用 props 中的 show 来接收父组件传递的 v -model 值
// event:为了方便理解,可以简单理解为父组件 @input 的别名,从而避免冲突
// event 的值对应了你 emit 时要提交的事件名,你可以叫 aa,也可以叫 bb,但是要命名要有意义哦!!!model: {
prop: 'show',
event: 'changed'
},
props: {
// 由于 model 选项中的 prop 属性指定了,所以 show 接收的是父组件 v -model 传递的值
show: {
type: Boolean,
default: false
}
},
methods: {confirm () {
// 双向数据绑定父组件传递的值
// 第一个参数,对应 model 选项的 event 的值,你可以叫 aa,bbb,ccc,起名随你
this.$emit('changed', false)
}
}
这种实现父子组件见 v -model 绑定值的方法,在我们开发中其实是很常用的,特别是你要封装公共组件的时候。
最后,实现双向数据绑定的方式其实还有.sync,这个属性一开始是有的,后来由于被认为或破坏单向数据流被删除了,但最后证明他还是有存在意义的,所以在 2.3 版本又加回来了。
例如:父组件:
<empty :oneprop.sync="msg"></empty>
data () {
return {msg: ''}
}
<div class="share-btn" @click="changeMsg"> 改变 msg 值 </div>
props: {
oneprop: {
type: String,
default: 'hello world'
}
},
methods: {changeMsg () {
// 双向数据流
this.$emit('update:msg', 'helow world')
}
}
这样,便可以在子组件更新父组件的数据。由于 v-model
只使用一次,所以当需要双向绑定的值有多个的时候,.sync
还是有一定的使用场景的。.sync
是下面这种写法的语法糖,旨在简化我们的操作:
<empty
:msg="message"
@update:msg="message = $event"
></empty>
掌握了组件的 v -model 写法,在封装一些公共组件的时候就又轻松一些了吧。
这里再提一下:
- m.$emit(event ,[…args])这个 api,其主要作用就是用来触发当前实例上的事件。附加参数都会传给监听器回调。子组件也属于当前实例。第一个参数:要触发的事件名称。后续的参数可选:即作为参数传递给要触发的事件。文档
- 监听当前实例上的自定义事件,事件可以有 $emit 触发,也能 通过 hook 监听到钩子函数,
vm.$on(event, callback):一直监听;文档
vm.$once(event, callback):监听一次;文档
vm.$off([event, callback] ):移除监听;文档
监听 $emit 触发的自定义事件,上面已经有过用法了,监听钩子函数,在上面的定时器那块也有演示到。监听钩子函数的场景使用的不多,但是还是要知道的。
- vm.$attrs:可以获取到父组件传递的除 class 和 style 外的所有自定义属性。
- vm.$listeners:可以获取到父组件传递的所有自定义事件
例如:父组件:
<empty
:msg="message"
:title="articleTitle"
@confirm="func1"
@cancel="func2"
></empty>
就可以在子组件中获取父组件传递的属性和事件,而不用在 props 中定义。子组件简单演示如下:
created() {
const msg = this.$attrs.msg; // 获取父组件传递的 msg
this.$listeners.confirm && this.$listeners.confirm(); // 若组件传递事件 confirm 则执行},
这在我们写一些高级组件时候,会有用到的。