修改props之sync修饰符
修改props
1.传统方式
Vue父传子可以使用props,子组件接收到后就可以使用了。但是如果子组件中需要对props进行修改,是不能在子组件中直接进行修改的,需要使用 $emit()
触发父组件的方法并传值过去,在父组件中接收传过来的数据然后进行修改。
父组件:
<template>
<div class="app">
<!--
方式1:
监控子组件是否触发changeTitle方法,触发则执行handleTitle事件
从而实现子组件点击按钮修改父组件数据
-->
<info :title="title" @changeTitle="handleTitle"></info>
</div>
</template>
<script>
import Info from './components/Info.vue'
export default {
name: 'App',
data() {
return {
title: '初始标题',
}
},
methods: {
handleTitle(title) {
this.title = title
},
},
components: {
Info,
},
}
</script>
子组件:
<template>
<div class="info">
<div class="title">{{ title }}</div>
<div class="content"></div>
<div class="buttons">
<button @click="changeTitle">修改标题</button>
</div>
</div>
</template>
<script>
export default {
name: 'Info',
props: {
title: {
type: String,
},
},
methods: {
// 触发自定义事件changeTitle并传值
changeTitle(){
this.$emit('changeTitle', '新的标题')
},
},
}
</script>
效果:
2.代码简化
可以看到,父组件中是创建了 handleTitle
方法来进行修改的。而这是可以简化的。
父组件:
将父组件中的第8行代码(子组件标签)简单修改一下,然后删除父组件 methods
中的 changeTitle()
方法。
没错,这样写就不需要在父组件的 methods
中另写一个方法用于修改了,父组件直接一行代码搞定。
<!--
方式2:
vue内置了 @update:变量名="变量名=$event" 的方法,可以实现与方式1同样的功能
该方法并不需要在父组件的methods中定义修改方法
$event接收通过$emit传过来的参数,并通过赋值语句实现父组件title的修改
-->
<info :title="title" @update:title="title = $event"></info>
子组件:
子组件中 $emit()
的触发方法名修改为 update:title
changeTitle() {
// 这里的触发方法必须是 update:变量名 的方式,这是固定写法
this.$emit('update:title', '新的标题')
},
效果和之前一样。
3.使用.sync修饰符
你以为这就简化完成了?不不不,它还可以更简化。
子组件需要修改父组件 props
时,使用 .sync
修饰符可以不必单独在父组件中写修改方法。只需要在子组件中 $emit
触发方法 update:变量名
并传值,即可完成修改。
在 2.
的基础上,使用 .sync
修饰符进行改写,代码更短
父组件:
<!--
方式3:
此方式是方式2的简写形式
使用 .sync 修饰符即可实现 @update:变量名="变量名=$event" 一样效果
-->
<info :title.sync="title"></info>
子组件:
与 2.2
一样,保存不变。
最后效果与前两个方式的效果是一样的,均能成功修改。
4.若props是对象呢
若传的 props
是一个对象,里面有多个值。
4.1参考方式1
那么按照 1.
的做法,直接将对象 doc
传过去,子组件接收,使用 doc.属性名
直接使用。
这当然没问题。但是这样,你就需要在父组件中为每一个属性写一个修改方法,很显然,代码量有点大。
4.2参考方式2
根据 2.
的做法,将需要用到的 doc
中的属性分别传过去,子组件分别接收。
父组件:
<template>
<div class="app">
<!--
方式4:
与方式2一样,但是若此时传的是一个对象,若用方式2的写法,代码将会很长...
毕竟每一个需要修改的变量都需要这样写一遍,变量越多,代码也就越长。
-->
<info
:title="doc.title"
:content="doc.content"
@update:title="doc.title = $event"
@update:content="doc.content = $event"
></info>
</div>
</template>
<script>
import Info from './components/Info.vue'
export default {
name: 'App',
data() {
return {
doc: {
title: '初始doc标题',
content: '初始doc内容',
},
}
},
components: {
Info,
},
}
</script>
子组件:
<template>
<div class="info">
<div class="title">{{ title }}</div>
<div class="content">{{ content }}</div>
<div class="buttons">
<button @click="changeTitle">修改标题</button>
<button @click="changeContent">修改内容</button>
</div>
</div>
</template>
<script>
export default {
name: 'Info',
// 直接接收doc对象不可行
// 那就分别接收doc对象中每一个需要用到的属性
props: {
title: {
type: String,
},
content: {
type: String,
},
},
methods: {
// 子组件触发默认方法 update:变量名 并传值
changeTitle(){
this.$emit('update:title', '新的标题')
},
changeContent() {
this.$emit('update:content', '新的内容')
},
},
}
</script>
效果:
很好,效果满足预期。但是,如果要传的数据很多怎么办?总不能每一项都写一遍吧。父组件就传了两个数据,代码就长这样了:
<info
:title="doc.title"
:content="doc.content"
@update:title="doc.title = $event"
@update:content="doc.content = $event"
></info>
多了还得了?
4.3参考方式3
那试试 3.
的方法,也是传 doc
对象过去,然后使用 .sync
修饰符如何?
但事实上, <info :doc.sync="doc"></info>
的写法是错误的,子组件通过 props
接收不到这个 doc
对象,会报错哒。
难道无解了?并没有,思路对了,写法错了...
绑定对象内多个属性,应使用如下语法: v-bind.sync = object
父组件:
<info v-bind.sync="doc"></info>
子组件:
与 4.2
一样,保持不变。
就这样,又是一行简短的代码,效果与 4.2
并无不同。
注意:
虽然父组件这里看着传的是 doc
对象,但是在子组件中可看到props接收的是 doc
对象中的指定属性,而不是直接接收 doc
对象本身。
因为该方式可以看做是将 doc
对象解构(es6语法)然后分别将解构得到的每一项属性传给了子组件。
所以子组件props接收到的不是一个 doc
对象,而是一个个通过 doc
解构后得到的属性。
本文系作者 @枫雨 原创发布在枫林幻境站点。未经许可,禁止转载。
全部评论 1
dsapr
FireFox Windows 10