本篇文章主要介绍父子组件传值,组件的数据双向绑定。
1. 基础父子组件传值
父子组件传值,这是Vue组件传值最常见,最基础的传值方法。
父组件向子组件传值使用Props,子组件定义期望接收传值的名字、类型、默认值等等,父组件Props值的改变会自动同步到子组件。
子组件向父组件传值使用事件触发,即使用$emit
注册事件,在父组件触发,这个事件可以携带值,而且一个事件的触发本身就是在传递信息。
代码实现:
//父组件
<template>
<div id="parent">
父组件的text:{{text}}
<Child :text="text" @changeText="prentEvent"></Child>
</div>
</template>
<script>
export default {
components: {
Child: () => import('@/components/Child')
},
data() {
return {
text: 'a'
}
},
methods: {
prentEvent(value) {
this.text = value
},
}
</script>
//子组件
<template>
<div class="child">
<p>子组件text:{{text}}</p>
<button @click="changeParent">改变父组件text</button>
</div>
</template>
<script>
export default {
name: 'Child',
props: {
text: {
type: String,
required: true
}
},
data() {
return {}
},
methods: {
changeParent() {
this.$emit('changeText', 'b')
},
},
}
</script>
由于父组件的Props改变会自动传递到子组件,所以会同步变化。
2. 父子组件传值的双向绑定
上述例子有点双向绑定的意思,但这并不是双向绑定,Vue的数据都是单向流动的,而且Vue中从来就没有任何的双向绑定,那些双向绑定只是语法糖而已。
我们都知道v-model
可以用在 input
标签上,实现双向绑定,其实Vue已经把input
标签进行了修改,也就是 input
标签已经是一个Vue的内置组件了,同样的内置组件还有很多,比如,用于缓存的keep-alive
,用于路由链接的router-like
,用于路由视图展示的router-view
等等。
实现一个自定组件的v-model
之前,我们先实现一个双向绑定,代码如下:
//Com1
<template>
<div class="com1">
Com1:
<input type="text" :value="value" @input="changeSelf" />
<Com2 :value="value" @input="changes"></Com2>
</div>
</template>
<script>
export default {
name: 'Com1',
components: {
Com2: () => import('./Com2')
},
data() {
return {
value: ''
}
},
methods: {
changes(values) {
this.value = values
},
changeSelf(e) {
this.value = e.target.value;
},
},
}
</script>
//Com2
<template>
<div class="com2">
Com2:
<input type="text" :value="value" @input="Com2Input" />
</div>
</template>
<script>
export default {
name: 'Com2',
props: {
value: {
type: String,
required: true,
}
},
methods: {
Com2Input(e) {
this.$emit('input', e.target.value)
}
},
}
</script>
我们没用Vue的
v-model
同样可以实现数据的双向绑定。我们如何改造成v-model
呢?只能改变父组件Com1,子组件不能更改,代码如下:
//Com1
<template>
<div class="com1">
Com1:
<!--下面这行改变是为了精简代码,使用input标签的v-model--!>
<!-- <input type="text" :value="value" @input="changeSelf" /> -->
<input type="text" v-model="value" />
<!-- <Com2 :value="value" @input="changes"></Com2> -->
<Com2 v-model="value"></Com2>
</div>
</template>
<script>
export default {
name: 'Com1',
components: {
Com2: () => import('./Com2')
},
data() {
return {
value: ''
}
},
methods: {
// changes(values) {
// this.value = values
// },
// changeSelf(e) {
// this.value = e.target.value;
// },
},
}
</script>
由此我们实现了Com2这个组件的v-model
,改变的是取消了Com2的事件监听@input
,Propsvalue
,换来了v-model
,这个简化的过程就是语法糖。v-model
会自动处理那些注释掉的操作,即v-model
会自动处理一个组件的input
事件和value
Props,让两者绑定。
input的v-model已经实现,我们来说一说自定义组件的v-model,v-model
会检测组件的input
事件和value
Props,这显然是不行的,自定义组件需要的不一定是这两个东西,幸好子组件里还有一个属性mode
,可以用来改变v-model
默认监听的Props和event,代码如下:
export default {
name: 'Com2',
model: {
//默认监听的事件和prop
// event: 'input',
// prop: 'value',
//自定义的监听子组件触发事件和传进去的prop
event: 'change',
prop: 'datas'
},
//其他代码
}
这时在父组件使用子组件的时候,在子组件使用v-model
就会自动处理change
事件和datas
Props,这样就可以随意定义想要的双向绑定属性了,但是一个组件只能有一个v-model
。
在Vue 2.3.0 中加入了.sync
修饰符,也是用来处理双向绑定的,封装流程和v-model
类似,在子组件需要这样操作,代码如下:
//在需注册事件的地方这样写
this.$emit('update:datas', newDatas)
//在父组件调用这个子组件的时候这样写
<Com3
v-bind:datas="datas"
v-on:update:datas="datas = $event"
></Com3>
//使用.sync 修饰符简化为
<Com3 :datas.sync="datas"></Com3>
//子组件的事件注册不改动
这样同样可以实现双向绑定,一个组件可以有多个.sync
修饰符双向绑定。