vue通過子組件修改父組件prop的多種實(shí)現(xiàn)方式
前言
實(shí)際工作項(xiàng)目開發(fā)中,很經(jīng)常會(huì)有父組件先傳遞值給子組件,再由子組件渲染展示的場景,下面我總結(jié)一下目前工作中遇到和用過的一些方式,也算是給大家一個(gè)實(shí)現(xiàn)方式和思路參考,如有理解不對(duì)or有其他方法歡迎在評(píng)論區(qū)留言指導(dǎo)~
常用方式
推薦,遵循prop單向傳遞的規(guī)則,基本數(shù)據(jù)類型和引用數(shù)據(jù)類型均可。
1. 通過父組件on監(jiān)聽子組件emit事件實(shí)現(xiàn)修改prop
原理:
- 給子組件中的input標(biāo)簽綁定value屬性賦值prop中的值,因?yàn)槭怯胿alue而不是v-model,所以修改input值的時(shí)候并不會(huì)影響到prop。
- 然后給input綁定input事件,每次輸入內(nèi)容都會(huì)觸發(fā)input事件,在事件里通過this.$emit(‘父要監(jiān)聽的事件名', 修改后的值)去將值向上傳遞。
- 父組件中通過on監(jiān)聽子組件剛剛觸發(fā)的emit事件,將事件傳遞過來的值更新到父組件的data中。
- 父組件data中的值更新后,因?yàn)橛型ㄟ^prop傳遞給子組件,所以子組件也會(huì)同步更新prop值并渲染視圖層。
父組件代碼如下:
<template> <div style="background-color: skyblue;"> <h3>通過父組件on監(jiān)聽子組件emit事件實(shí)現(xiàn)修改prop</h3> <div>父obj:{{ obj }}</div> <div>父msg:{{ msg }}</div> <!-- 父組件調(diào)用子組件時(shí)通過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,因?yàn)閕nput在這主要用作展示prop數(shù)據(jù),實(shí)際修改不在子組件,子組件只是作為修改觸發(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中對(duì)應(yīng)需要更新值的屬性,然后回傳給父組件 // 父組件就可以知道子組件需要更新obj中哪個(gè)屬性的值 this.$emit('update-obj', key, $event.target.value) }, // 通知父組件更新msg updateMsg (newVal) { this.$emit('update-msg', newVal) } } } </script>
2. 通過父組件sync修飾符 + 子組件emit事件實(shí)現(xiàn)修改prop
原理:
- 給子組件中的input標(biāo)簽綁定value屬性賦值prop中的值,因?yàn)槭怯胿alue而不是v-model,所以修改input值的時(shí)候并不會(huì)影響到prop。
- 然后給input綁定input事件,每次輸入內(nèi)容都會(huì)觸發(fā)input事件,在事件里通過this.$emit(‘父要監(jiān)聽的事件名', 修改后的值)去將值向上傳遞。
- 父組件調(diào)用子組件傳遞prop時(shí),在prop后加上.sync即可將事件傳遞過來的值更新到父組件的data中,因?yàn)閟ync其實(shí)就相當(dāng)于執(zhí)行了@監(jiān)聽子觸發(fā)的事件名 = "父data中的屬性名 = emit傳遞的值(即$event)"這一段代碼。
- 父組件data中的值更新后,因?yàn)橛型ㄟ^prop傳遞給子組件,所以子組件也會(huì)同步更新prop值并渲染視圖層。
父組件代碼如下:
<template> <div style="background-color: skyblue;"> <h3>通過父組件sync修飾符 + 子組件emit事件實(shí)現(xiàn)修改prop</h3> <div>父obj:{{ obj }}</div> <div>父msg:{{ msg }}</div> <!-- 父組件調(diào)用子組件傳遞prop時(shí),在prop后加上.sync即可 --> <syncChild :obj.sync="obj" :msg.sync="msg" /> <!-- sync其實(shí)就相當(dāng)于執(zhí)行了 @監(jiān)聽子觸發(fā)的事件名 = "父data中的屬性名 = emit傳遞的值(即$event)" 這一段代碼 --> <!-- 效果相當(dāng)于下面的代碼,所以父組件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,因?yàn)閕nput在這主要用作展示prop數(shù)據(jù),實(shí)際修改不在子組件,子組件只是作為修改觸發(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復(fù)制了一份,主要為了: // 1.區(qū)分2個(gè)對(duì)象(引用類型)用的不是同一個(gè)內(nèi)存地址 // 2.保留對(duì)象中所有的值,方便子組件渲染/修改/回傳用 childObj: cloneDeep(this.obj) } }, methods: { // 通知父組件更新obj updateObj ($event, key) { // 接收輸入的值,和childObj中對(duì)應(yīng)需要更新值的屬性 // 然后更新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>
取巧方式
主要針對(duì)引用數(shù)據(jù)類型,繞過了vue對(duì)于prop的檢測機(jī)制,具體看項(xiàng)目規(guī)范是否允許這樣寫。
3.通過data實(shí)現(xiàn)修改prop
前提:只有引用數(shù)據(jù)類型可以實(shí)現(xiàn)
原理:
- 將父組件傳入的prop直接賦值給子組件的data,此時(shí)prop和data兩邊的變量指向的都是同一個(gè)內(nèi)存地址,所以修改data等于修改prop。
- vue2開始不允許直接修改prop,但此時(shí)我們表面修改的是data不是prop,因此vue不會(huì)拋出錯(cuò)誤,相當(dāng)于繞過了vue對(duì)于不允許修改prop的檢測機(jī)制。
父組件代碼如下:
<template> <div style="background-color: skyblue;"> <h3>通過賦值到data實(shí)現(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> <!-- 這里因?yàn)槲覀冎苯影裵rop賦值給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偵聽器實(shí)現(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ù)類型直接賦值時(shí)是淺拷貝,只會(huì)復(fù)制內(nèi)存地址,修改其中一個(gè)會(huì)影響另一個(gè) dataObj: this.obj, // 基本數(shù)據(jù)類型直接賦值時(shí)是復(fù)制值,修改其中一個(gè)不會(huì)影響另一個(gè) dataMsg: this.msg } }, watch: { // 這里偵聽data中復(fù)制過來的dataMsg,當(dāng)修改時(shí)執(zhí)行emit通知父組件進(jìn)行更新 dataMsg (newVal) { this.$emit('update:msg', newVal) } } } </script>
4.通過計(jì)算屬性computed實(shí)現(xiàn)修改prop
前提:只有引用數(shù)據(jù)類型可以實(shí)現(xiàn)
原理:
- 在子組件中直接通過計(jì)算屬性computed監(jiān)聽父組件傳入的prop,此時(shí)計(jì)算屬性computed和prop兩邊的變量指向的都是同一個(gè)內(nèi)存地址,所以修改計(jì)算屬性computed等于修改prop。
- vue2開始不允許直接修改prop,但此時(shí)我們表面修改的是計(jì)算屬性computed不是prop,因此vue不會(huì)拋出錯(cuò)誤,相當(dāng)于繞過了vue對(duì)于不允許修改prop的檢測機(jī)制。
父組件代碼如下:
<template> <div style="background-color: skyblue;"> <h3>通過計(jì)算屬性監(jiān)聽實(shí)現(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> <!-- 這里因?yàn)槲覀冎苯油ㄟ^計(jì)算屬性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ù)類型的思路,可以通過計(jì)算屬性computed的setter實(shí)現(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,此時(shí)computedObj相當(dāng)于obj // 所以是淺拷貝,只會(huì)復(fù)制內(nèi)存地址,修改其中一個(gè)會(huì)影響另一個(gè) return this.obj }, computedMsg: { get () { // 這里prop每次更新時(shí),都會(huì)觸發(fā)計(jì)算屬性的getter方法獲取最新的值 // 直接return基本數(shù)據(jù)類型時(shí)是復(fù)制值,修改其中一個(gè)不會(huì)影響另一個(gè) return this.msg }, set (newVal) { // 這里利用計(jì)算屬性的setter方法,監(jiān)聽到值修改時(shí)觸發(fā)emit通知父組件更新值 this.$emit('update:msg', newVal) } } } } </script>
到此這篇關(guān)于vue通過子組件修改父組件prop的幾種實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)vue子組件修改父組件prop內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue3項(xiàng)目中配置TypeScript和JavaScript的兼容
在Vue3開發(fā)中,常見的使用JavaScript(JS)編寫代碼,但也會(huì)有調(diào)整編寫語言使用TypeScript(TS)的需求,因此,在Vue3項(xiàng)目設(shè)置中兼容TS和JS是刻不容緩的重要任務(wù),2023-08-08Vue中的watch是什么以及watch和computed的區(qū)別
這篇文章主要介紹了Vue中的watch是什么以及watch和computed的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06Vue-Router進(jìn)階之滾動(dòng)行為詳解
本篇文章主要介紹了Vue-Router進(jìn)階之滾動(dòng)行為詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09vue3實(shí)現(xiàn)H5表單驗(yàn)證組件的示例
本文主要介紹了vue3實(shí)現(xiàn)H5表單驗(yàn)證組件的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04基于Vue2實(shí)現(xiàn)動(dòng)態(tài)折扣表格
這篇文章主要為大家詳細(xì)介紹了如何基于Vue2實(shí)現(xiàn)動(dòng)態(tài)折扣表格,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01vue 中基于html5 drag drap的拖放效果案例分析
本文通過三個(gè)案例給大家介紹了vue 中基于html5 drag drap的拖放效果 ,需要的朋友可以參考下2018-11-11