vue通過子組件修改父組件prop的多種實現(xiàn)方式
前言
實際工作項目開發(fā)中,很經(jīng)常會有父組件先傳遞值給子組件,再由子組件渲染展示的場景,下面我總結一下目前工作中遇到和用過的一些方式,也算是給大家一個實現(xiàn)方式和思路參考,如有理解不對or有其他方法歡迎在評論區(qū)留言指導~
常用方式
推薦,遵循prop單向傳遞的規(guī)則,基本數(shù)據(jù)類型和引用數(shù)據(jù)類型均可。
1. 通過父組件on監(jiān)聽子組件emit事件實現(xiàn)修改prop
原理:
- 給子組件中的input標簽綁定value屬性賦值prop中的值,因為是用value而不是v-model,所以修改input值的時候并不會影響到prop。
- 然后給input綁定input事件,每次輸入內(nèi)容都會觸發(fā)input事件,在事件里通過this.$emit(‘父要監(jiān)聽的事件名', 修改后的值)去將值向上傳遞。
- 父組件中通過on監(jiān)聽子組件剛剛觸發(fā)的emit事件,將事件傳遞過來的值更新到父組件的data中。
- 父組件data中的值更新后,因為有通過prop傳遞給子組件,所以子組件也會同步更新prop值并渲染視圖層。

父組件代碼如下:
<template>
<div style="background-color: skyblue;">
<h3>通過父組件on監(jiān)聽子組件emit事件實現(xiàn)修改prop</h3>
<div>父obj:{{ obj }}</div>
<div>父msg:{{ msg }}</div>
<!-- 父組件調(diào)用子組件時通過on監(jiān)聽子組件觸發(fā)的emit事件,以接收值并更新用 -->
<emitChild
:obj="obj"
:msg="msg"
@update-obj="updateObj"
@update-msg="updateMsg"
/>
</div>
</template>
<script>
import emitChild from './components/emitChild'
export default {
name: 'emitUpdate',
components: {
emitChild
},
data () {
return {
obj: {
name: 'zhangsan',
age: 18
},
msg: 'hello'
}
},
methods: {
// 監(jiān)聽子組件觸發(fā)的事件并更新data中的obj
updateObj (key, newVal) {
this.obj[key] = newVal
},
// 監(jiān)聽子組件觸發(fā)的事件并更新data中的msg
updateMsg (newVal) {
this.msg = newVal
}
}
}
</script>
子組件代碼如下:
<template>
<div style="background-color: pink;">
<div>
<span>修改name:</span>
<!-- 這里綁定值用value,因為input在這主要用作展示prop數(shù)據(jù),實際修改不在子組件,子組件只是作為修改觸發(fā)源 -->
<!-- 綁定input事件,作為觸發(fā)源的入口 -->
<input type="text" :value="obj.name" @input="updateObj($event, 'name')">
</div>
<div>
<span>修改age:</span>
<input type="text" :value="obj.age" @input="updateObj($event, 'age')">
</div>
<div>
<span>修改msg:</span>
<input type="text" :value="msg" @input="updateMsg($event.target.value)">
</div>
</div>
</template>
<script>
export default {
name: 'emitUpdateChild',
props: {
obj: {
type: Object,
default: () => {}
},
msg: {
type: String,
default: ''
}
},
methods: {
// 通知父組件更新obj
updateObj ($event, key) {
// 接收輸入的值,和obj中對應需要更新值的屬性,然后回傳給父組件
// 父組件就可以知道子組件需要更新obj中哪個屬性的值
this.$emit('update-obj', key, $event.target.value)
},
// 通知父組件更新msg
updateMsg (newVal) {
this.$emit('update-msg', newVal)
}
}
}
</script>
2. 通過父組件sync修飾符 + 子組件emit事件實現(xiàn)修改prop
原理:
- 給子組件中的input標簽綁定value屬性賦值prop中的值,因為是用value而不是v-model,所以修改input值的時候并不會影響到prop。
- 然后給input綁定input事件,每次輸入內(nèi)容都會觸發(fā)input事件,在事件里通過this.$emit(‘父要監(jiān)聽的事件名', 修改后的值)去將值向上傳遞。
- 父組件調(diào)用子組件傳遞prop時,在prop后加上.sync即可將事件傳遞過來的值更新到父組件的data中,因為sync其實就相當于執(zhí)行了@監(jiān)聽子觸發(fā)的事件名 = "父data中的屬性名 = emit傳遞的值(即$event)"這一段代碼。
- 父組件data中的值更新后,因為有通過prop傳遞給子組件,所以子組件也會同步更新prop值并渲染視圖層。

父組件代碼如下:
<template>
<div style="background-color: skyblue;">
<h3>通過父組件sync修飾符 + 子組件emit事件實現(xiàn)修改prop</h3>
<div>父obj:{{ obj }}</div>
<div>父msg:{{ msg }}</div>
<!-- 父組件調(diào)用子組件傳遞prop時,在prop后加上.sync即可 -->
<syncChild :obj.sync="obj" :msg.sync="msg" />
<!--
sync其實就相當于執(zhí)行了
@監(jiān)聽子觸發(fā)的事件名 = "父data中的屬性名 = emit傳遞的值(即$event)"
這一段代碼
-->
<!-- 效果相當于下面的代碼,所以父組件methods中不需要再定義修改data數(shù)據(jù)的方法 -->
<!--
<syncChild
:obj="obj"
:msg="msg"
@update-obj="obj = $event"
@update-msg="msg = $event"
/>
-->
</div>
</template>
<script>
import syncChild from './components/syncChild'
export default {
name: 'syncUpdate',
components: {
syncChild
},
data () {
return {
obj: {
name: 'zhangsan',
age: 18
},
msg: 'hello'
}
}
}
</script>
子組件代碼如下:
<template>
<div style="background-color: pink;">
<div>
<span>修改name:</span>
<!-- 這里綁定值用value,因為input在這主要用作展示prop數(shù)據(jù),實際修改不在子組件,子組件只是作為修改觸發(fā)源 -->
<!-- 綁定input事件,作為觸發(fā)源的入口 -->
<input type="text" :value="childObj.name" @input="updateObj($event, 'name')">
</div>
<div>
<span>修改age:</span>
<input type="text" :value="childObj.age" @input="updateObj($event, 'age')">
</div>
<div>
<span>修改msg:</span>
<input type="text" :value="msg" @input="updateMsg($event.target.value)">
</div>
</div>
</template>
<script>
// 這里引入lodash工具庫的cloneDeep深拷貝方法
// 官方文檔地址 https://www.lodashjs.com/
import { cloneDeep } from 'lodash'
export default {
name: 'emitUpdateChild',
props: {
obj: {
type: Object,
default: () => {}
},
msg: {
type: String,
default: ''
}
},
data () {
return {
// 這里通過深拷貝將prop的obj復制了一份,主要為了:
// 1.區(qū)分2個對象(引用類型)用的不是同一個內(nèi)存地址
// 2.保留對象中所有的值,方便子組件渲染/修改/回傳用
childObj: cloneDeep(this.obj)
}
},
methods: {
// 通知父組件更新obj
updateObj ($event, key) {
// 接收輸入的值,和childObj中對應需要更新值的屬性
// 然后更新childOBj后,回傳給父組件
// 父組件直接把拿到的childObj更新到data的obj即可
this.childObj[key] = $event.target.value
this.$emit('update:obj', this.childObj)
},
// 通知父組件更新msg
updateMsg (newVal) {
this.$emit('update:msg', newVal)
}
}
}
</script>
取巧方式
主要針對引用數(shù)據(jù)類型,繞過了vue對于prop的檢測機制,具體看項目規(guī)范是否允許這樣寫。
3.通過data實現(xiàn)修改prop
前提:只有引用數(shù)據(jù)類型可以實現(xiàn)
原理:
- 將父組件傳入的prop直接賦值給子組件的data,此時prop和data兩邊的變量指向的都是同一個內(nèi)存地址,所以修改data等于修改prop。
- vue2開始不允許直接修改prop,但此時我們表面修改的是data不是prop,因此vue不會拋出錯誤,相當于繞過了vue對于不允許修改prop的檢測機制。

父組件代碼如下:
<template>
<div style="background-color: skyblue;">
<h3>通過賦值到data實現(xiàn)修改prop</h3>
<div>父obj:{{ obj }}</div>
<div>父msg:{{ msg }}</div>
<dataChild :obj="obj" :msg.sync="msg" />
</div>
</template>
<script>
import dataChild from './components/dataChild'
export default {
name: 'dataUpdate',
components: {
dataChild
},
data () {
return {
obj: {
name: 'zhangsan',
age: 18
},
msg: 'hello'
}
}
}
</script>
子組件代碼如下:
<template>
<div style="background-color: pink;">
<div>
<span>修改name:</span>
<!-- 這里因為我們直接把prop賦值給data了,所以可以直接用v-model雙向綁定修改數(shù)據(jù) -->
<input type="text" v-model="dataObj.name">
</div>
<div>
<span>修改age:</span>
<input type="text" v-model="dataObj.age">
</div>
<div>
<span>修改msg:</span>
<!-- 這里提供另一種修改基本數(shù)據(jù)類型的思路,可以通過watch偵聽器實現(xiàn) -->
<input type="text" v-model="dataMsg">
</div>
</div>
</template>
<script>
export default {
name: 'dataChild',
props: {
obj: {
type: Object,
default: () => {}
},
msg: {
type: String,
default: ''
}
},
data () {
return {
// 引用數(shù)據(jù)類型直接賦值時是淺拷貝,只會復制內(nèi)存地址,修改其中一個會影響另一個
dataObj: this.obj,
// 基本數(shù)據(jù)類型直接賦值時是復制值,修改其中一個不會影響另一個
dataMsg: this.msg
}
},
watch: {
// 這里偵聽data中復制過來的dataMsg,當修改時執(zhí)行emit通知父組件進行更新
dataMsg (newVal) {
this.$emit('update:msg', newVal)
}
}
}
</script>
4.通過計算屬性computed實現(xiàn)修改prop
前提:只有引用數(shù)據(jù)類型可以實現(xiàn)
原理:
- 在子組件中直接通過計算屬性computed監(jiān)聽父組件傳入的prop,此時計算屬性computed和prop兩邊的變量指向的都是同一個內(nèi)存地址,所以修改計算屬性computed等于修改prop。
- vue2開始不允許直接修改prop,但此時我們表面修改的是計算屬性computed不是prop,因此vue不會拋出錯誤,相當于繞過了vue對于不允許修改prop的檢測機制。

父組件代碼如下:
<template>
<div style="background-color: skyblue;">
<h3>通過計算屬性監(jiān)聽實現(xiàn)修改prop</h3>
<div>父obj:{{ obj }}</div>
<div>父msg:{{ msg }}</div>
<computedChild :obj="obj" :msg.sync="msg" />
</div>
</template>
<script>
import computedChild from './components/computedChild'
export default {
name: 'computedUpdate',
components: {
computedChild
},
data () {
return {
obj: {
name: 'zhangsan',
age: 18
},
msg: 'hello'
}
}
}
</script>
子組件代碼如下:
<template>
<div style="background-color: pink;">
<div>
<span>修改name:</span>
<!-- 這里因為我們直接通過計算屬性computed監(jiān)聽prop了,所以可以直接用v-model雙向綁定修改數(shù)據(jù) -->
<input type="text" v-model="computedObj.name">
</div>
<div>
<span>修改age:</span>
<input type="text" v-model="computedObj.age">
</div>
<div>
<span>修改msg:</span>
<!-- 這里提供另一種修改基本數(shù)據(jù)類型的思路,可以通過計算屬性computed的setter實現(xiàn) -->
<input type="text" v-model="computedMsg">
</div>
</div>
</template>
<script>
export default {
name: 'computedChild',
props: {
obj: {
type: Object,
default: () => {}
},
msg: {
type: String,
default: ''
}
},
computed: {
computedObj () {
// 這里直接return引用數(shù)據(jù)類型obj,此時computedObj相當于obj
// 所以是淺拷貝,只會復制內(nèi)存地址,修改其中一個會影響另一個
return this.obj
},
computedMsg: {
get () {
// 這里prop每次更新時,都會觸發(fā)計算屬性的getter方法獲取最新的值
// 直接return基本數(shù)據(jù)類型時是復制值,修改其中一個不會影響另一個
return this.msg
},
set (newVal) {
// 這里利用計算屬性的setter方法,監(jiān)聽到值修改時觸發(fā)emit通知父組件更新值
this.$emit('update:msg', newVal)
}
}
}
}
</script>
到此這篇關于vue通過子組件修改父組件prop的幾種實現(xiàn)方式的文章就介紹到這了,更多相關vue子組件修改父組件prop內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Vue3項目中配置TypeScript和JavaScript的兼容
在Vue3開發(fā)中,常見的使用JavaScript(JS)編寫代碼,但也會有調(diào)整編寫語言使用TypeScript(TS)的需求,因此,在Vue3項目設置中兼容TS和JS是刻不容緩的重要任務,2023-08-08
Vue中的watch是什么以及watch和computed的區(qū)別
這篇文章主要介紹了Vue中的watch是什么以及watch和computed的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
vue 中基于html5 drag drap的拖放效果案例分析
本文通過三個案例給大家介紹了vue 中基于html5 drag drap的拖放效果 ,需要的朋友可以參考下2018-11-11

