vue通過子元件修改父元件prop的多種實現方式
阿新 • • 發佈:2021-09-23
目錄
- 前言
- 常用方式
- 1. 通過父元件on監聽子元件emit事件實現修改prop
- 2. 通過父元件sync修飾符 + 子元件emit事件實現修改prop
- 取巧方式
- 3.通過data實現修改prop
- 4.通過計算屬http://www.cppcns.com性computed實現修改prop
前言
實際工作專案開發中,很經常會有父元件先傳遞值給子元件,再由子元件渲染展示的場景,下面我總結一下目前工作中遇到和用過的一些方式,也算是給大家一個實現方式和思路參考,如有理解不對or有其他方法歡迎在評論區留言指導~
常用方式
推薦,遵循prop單向傳遞的規則,基本資料型別和引用資料型別均可。
1. 通過父元件on監聽子元件emit事件實現修改prop
原理:
- 給子元件中的input標籤繫結value屬性賦值prop中的值,因為是用value而不是v-model,所以修改input值的時候並不會影響到prop。
- 然後給input繫結input事件,每次輸入內容都會觸發input事件,在事件裡通過this.$emit(‘父要監聽的事件名',修改後的值)去將值向上傳遞。
- 父元件中通過on監聽子元件剛剛觸發的emit事件,將事件傳遞過來的值更新到父元件的data中。
- 父元件data中的值更新後,因為有通過prop傳遞給子元件,所以子元件也會同步更新prop值並渲染檢視層。
父元件程式碼如下:
<template> <div style="background-color: skyblue;"> <h3>通過父元件on監聽子元件emit事件實現修改prop</h3> <div>父obj:{{ obj }}</div> <div>父msg:{{ msg }}</div> <!-- 父元件呼叫子元件時通過on監聽子元件觸發的emit事件,以接收值並更新用 --> <emitChild :obj="obj" :msg="msg" @update-obj="updateObj" @update-msg="updateMsg" /> </div> </template> <script> import emitChild from './components/emitChild' export default { name:NihYmXE'emitUpdate',components: { emitChild },data () { return { obj: { name: 'zhangsan',age: 18 },msg: 'hello' } },methods: { // 監聽子元件觸發的事件並更新data中的obj updateObj (key,newVal) { this.obj[key] = newVal },// 監聽子元件觸發的事件並更新data中的msg updateMsg (newVal) { this.msg = newVal } } } </script>
子元件程式碼如下:
<template>
<div style="background-color: pink;">
<div>
<span>修改name:</span>
<!-- 這裡繫結值用value,因為input在這主要用作展示prop資料,實際修改不在子元件,子元件只是作為修改觸發源 -->
<!-- 繫結input事件,作為觸發源的入口 -->
<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: ''
}
NihYmXE },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事件實現修改prop
原理:
- 給子元件中的input標籤繫結value屬性賦值prop中的值,因為是用value而不是v-model,所以修改input值的時候並不會影響到prop。
- 然後給input繫結input事件,每次輸入內容都會觸發input事件,在事件裡通過this.$emit(‘父要監聽的事件名',修改後的值)去將值向上傳遞。
- 父元件呼叫子元件傳遞prop時,在prop後加上.sync即可將事件傳遞過來的值更新到父元件的data中,因為sync其實就相當於執行了@監聽子觸發的事件名 = "父data中的屬性名 = emit傳遞的值(即$event)"這一段程式碼。
- 父元件data中的值更新後,因為有通過prop傳遞給子元件,所以子元件也會同步更新prop值並渲染檢視層。
父元件程式碼如下:
<template> <div style="background-color: skyblue;"> <h3>通過父元件sync修飾符 + 子元件emit事件實現修改prop</h3> <div>父obj:{{ obj }}</div> <div>父msg:{{ msg }}</div> <!-- 父元件呼叫子元件傳遞prop時,在prop後加上.sync即可 --> <syncChild :obj.sync="obj" :msg.sync="msg" /> <!-- sync其實就相當於執行了 @監聽子觸發的事件名 = "父data中的屬性名 = emit傳遞的值(即$event)" 這一段程式碼 --> <!-- 效果相當於下面的程式碼,所以父元件methods中不需要再定義修改data資料的方法 --> <!-- <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 },msg: 'hello' } } } </script>
子元件程式碼如下:
<template> <div style="background-color: pink;"> <div> <span>修改name:</span> <!-- 這裡繫結值用value,因為input在這主要用作展示prop資料,實際修改不在子元件,子元件只是作為修改觸發源 --> <!-- 繫結input事件,作為觸發源的入口 --> <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.lodash.com/ import { cloneDeep } from 'lodash' export default { name: 'emitUpdateChild',default: '' } },data () { return { // 這裡通過深拷貝將prop的obj複製了一份,主要為了: // 1.區分2個物件(引用型別)用的不是同一個記憶體地址 // 2.保留物件中所有的值,方便子元件渲染/修改/回傳用 childObj: cloneDeep(this.obj) } },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>
取巧方式
主要針對引用資料型別,繞過了對於prop的檢測機制,具體看專案規範是否允許這樣寫。
3.通過data實現修改prop
前提:只有引用資料型別可以實現
原理:
- 將父元件傳入的prop直接賦值給子元件的data,此時prop和data兩邊的變數指向的都是同一個記憶體地址,所以修改data等於修改prop。
- vue2開始不允許直接修改prop,但此時我們表面修改的是data不是prop,因此vue不會丟擲錯誤,相當於繞過了vue對於不允許修改prop的檢測機制。
父元件程式碼如下:
<template> <div style="background-color: skyblue;"> <h3>通過賦值到data實現修改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 },msg: 'hello' } } } </script>
子元件程式碼如下:
<template> <div style="background-color: pink;"> <div> <span>修改name:</span> <!-- 這裡因為我們直接把prop賦值給data了,所以可以直接用v-model雙向繫結修改資料 --> <input type="text" v-model="dataObj.name"> </div> <div> <span>修改age:</span> <input type="text" v-model="dataObj.age"> </div> <div> <span>修改msg:</span> <!-- 這裡提供另一種修改基本資料型別的思路,可以通過watch偵聽器實現 --> <input type="text" v-model="dataMsg"> </div> </div> </template> <script> export default { name: 'dataChild',data () { return { // 引用資料型別直接賦值時是淺拷貝,只會複製記憶體地址,修改其中一個會影響另一個 dataObj: this.obj,// 基本資料型別直接賦值時是複製值,修改其中一個不會影響另一個 dataMsg: this.msg } },watch: { // 這裡偵聽daNihYmXEta中複製過來的dataMsg,當修改時執行emit通知父元件進行更新 dataMsg (newVal) { this.$emit('update:msg',newVal) } } } </script>
4.通過計算屬性computed實現修改prop
前提:只有引用資料型別可以實現
原理:
- 在子元件中直接通過計算屬性computed監聽父元件傳入的prop,此時計算屬性computed和prop兩邊的變數指向的都是同一個記憶體地址,所以修改計算屬性computed等於修改prop。
- vue2開始不允許直接修改prop,但此時我們表面修改的是計算屬性computed不是prop,因此vue不會丟擲錯誤,相當於繞過了vue對於不允許修改prop的檢測機制。
父元件程式碼如下:
<template> <div style="background-color: skyblue;"> <h3>通過計算屬性監聽實現修改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 },msg: 'hello' } } } </script>
子元件程式碼如下:
<template> <div style="background-color: pink;"> <div> <span>修改name:</span> <!-- 這裡因為我們直接通過計算屬性computed監聽prop了,所以可以直接用v-model雙向繫結修改資料 --> <input type="text" v-model="computedObj.name"> </div> <div> <span>修改age:</span> <input type="text" v-model="computedObj.age"> </div> <div> <span>修改msg:</span> <!-- 這裡提供另一種修改基本資料型別的思路,可以通過計算屬性computed的setter實現 --> <input type="text" v-model="computedMsg"> </div> </div> </template> <script> export default { name: 'computedChild',computed: { computedObj () { // 這裡直接return引用資料型別obj,此時computedObj相當於obj // 所以是淺拷貝,只會複製記憶體地址,修改其中一個會影響另一個 return this.obj },computedMsg: { get () { // 這裡prop每次更新時,都會觸發計算屬性的getter方法獲取最新的值 // 直接return基本資料型別時是複製值,修改其中一個不會影響另一個 return this.msg },set (newVal) { // 這裡利用計算屬性的setter方法,監聽到值修改時觸發emit通知父元件更新值 this.$emit('update:msg',newVal) } } } } </script>
到此這篇關於vue通過子元件修改父元件prop的幾種實現方式的文章就介紹到這了,更多相關vue子元件修改父元件prop內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!