Vue組件之間傳值無論面試還是正常開發中,都很常用,這里主要整理一下父子傳值、兄弟傳值、爺孫傳值的幾種方法。(我盡量整理的全一點,各位大哥們多指教指教,別踩別踩!!!哎嘛。或者您們有什么新的方式,歡迎評論,我給加上,盡量盡善盡美嘛!!!)
先補充下簡寫:’ : ‘ 是 v-bind的簡寫,
’ @ ‘是v-on的簡寫
一、父子傳值
父子組件的關系可以總結為 prop 向下傳遞,事件 向上傳遞。父組件通過prop給子組件下發數據,子組件通過 事件
給父組件發送信息。
1.1 使用 props 傳值【父傳子】
props可以是數組或對象,用來接收父組件傳來的數據,在接收的數據中也可以配置一些屬性(type、default、required、validator【驗證器】函數)
<!-- 父組件 -->
<first-child :fatherMethod="fatherMethod" :sendNumber="number" :sendObj="obj"></first-child>
<!-- 子組件 -->
props: {
/* 函數 */
fatherMethod: {
type: Function, //String、Object、Array等原生構造函數中的一種 、 自定義構造函數 、 這些組成數組
default: null, //默認值,如果 prop 沒有被傳入,則換做用這個值
required: false //定義該 prop 是否是必填項(如果是true,但沒有傳,控制台會報警告)
},
/* 數字 */
sendNumber: {
type: Number,
default: 0
},
/* 字符串 */
sendObj: {
type: Object,
default: {}
}
},
1.2 使用 this.$emit(eventName,[...args]) 傳值【子傳父】
eventName:事件名稱,子組件傳遞給父組件的事件名,上面說的 @ ( v-on )【官網對其用法描述:綁定事件的監聽器】
【...args】:附加參數,它會傳給監聽器回調
<!-- 子組件 -->
this.$emit('isUseFatherMet', true)
<!-- 父組件 -->
<first-child @isUseFatherMet="isUseFatherMet"></first-child>
//子組件調用父組件的方法
isUseFatherMet(val) {
console.log('子組件傳值過來了?', val)
},
1.3 調用子組件中的方法(使用this.$refs.子組件注冊名.子組件方法)
官方文檔對 vm.$refs 的定義為:一個對象,持有注冊過 ref、attribute 的所有 DOM 元素和組件實例。我們將引用信息注冊在父組件的 $refs 對象上,對象就會指向子組件實例,以此我們可訪問子組件的方法,進行傳值調用。
<!-- 父組件 -->
<first-child ref="child1"></first-child>
//調用子組件里的方法
runChildMet() {
this.$refs.child1.showIsUse()
}
<!-- 子組件 -->
//父組件會調用這個方法
showIsUse() {
alert(true)
}
1.4 使用 vm.$on 和 vm.$emit【也可應用於兄弟組件傳值】
首先就官方文檔描述:
vm.$on(event,callback) :監聽當前實例上的自定義事件。事件可以由 vm.$emit 觸發。回調函數會接收所有傳入事件觸發函數的額外參數。
vm.$emit(eventName,[...args]):觸發當前實例上的事件。附加參數都會傳給監聽器回調。
也就是說,子組件通過 vm.$emit() 把事件名和參數傳遞給父組件,父組件使用 vm.$on() 對這個自定義事件監聽,回調函數接收到值,進行下一步操作。
用法:我們可以新建一個Bus.js文件,文件暴露一個Vue實例,之后分別在父子組件中導入進來,然后調用。
// Bus.js (把它想象成運送數據的交通工具)
// 沒有源碼,在別的項目里找的
import Vue from 'vue'
export default new Vue()
<!-- 父組件 -->
import Bus from'../utils/Bus'
Bus.$on('backDmDetail', val => {
console.log(val);
})
beforeDestroy(){
//最后不要忘了刪除傳輸事件
Bus.$off("backDmDetail")
}
<!-- 子組件 -->
import Bus from'../utils/Bus'
Bus.$emit('backDmDetail',"dsdd")
1.5 使用 $parent 和 $children 方法
$parent :在子組件中直接訪問該組件的父實例或組件,
$children:當前實例的子組件,需要注意 $children 並不保證順序,也不是響應式的。考慮使用 v-for 來生成子組件
/* 使用$parent */
<!-- 子組件 -->
useParent() {
this.$parent.isUseFatherMet('是的,傳過來了,而且我用了$parent')
}
<!-- 父組件 -->
//子組件調用父組件的方法
isUseFatherMet(val) {
console.log('子組件傳值過來了?', val)
},
二、兄弟傳值
2.1 使用 vm.$on 和 vm.$emit
就如 標題1.4 所說,導入一個公共的 Bus.js 文件,暴露一個 Vue 實例,在兄弟組件傳遞時使用 Bus.$on 和 Bus.$emit
// Bus.js (把它想象成運送數據的交通工具)
// 沒有源碼,在別的項目里找的
import Vue from 'vue'
export default new Vue()
<!-- 父組件 -->
import Bus from'../utils/Bus'
Bus.$on('backDmDetail', val => {
this.investigationIndex = this.flag
})
<!-- 子組件 -->
import Bus from'../utils/Bus'
Bus.$emit('backDmDetail',"dsdd")
注意:暴露一個 Vue 實例,在Vue2.x版本是可以這么使用,但是在Vue3.x中,沒有全局的vue,會報警告:"export 'default' (imported as 'Vue') was not found in 'vue'"
強勢大哥:呵呵,說了這么多,什么什么跟上面一樣,有啥用,那我就想用Vue3.x,不想降版本,那咋整?
卑微小弟:別急,哥,有下面的方法:使用第三方庫Mitt
npm install -S mitt //先引入依賴
//新建一個Bus.js(這車有空調,手動滑稽)
<!-- 兄弟組件1 -->
import Bus from '@/utils/Bus.js'
//給兄弟組件傳值
sendToBrother() {
Bus.emit('toBrotherVal', '我是傳向兄弟組件的值')
}
<!-- 兄弟組件2 -->
import Bus from '@/utils/Bus.js'
Bus.on('toBrotherVal', (val) => {
alert(val)
})
//使用完別忘了移除(重復發車不回收,性能耗不起啊。。。)
unmounted() {
//移除toBrotherVal
Bus.off('toBrotherVal')
}
2.2 子傳父,父再傳子
一種比較常規的使用方法,通常都是this.$emit、props等方式一起使用,不做贅述。
三、爺孫傳值
3.1 使用 provide 和 inject
provide/inject: 這對選項要一起使用, 以允許一個祖先組件向其子孫后台注入一個依賴,不管組件層次有多深,並在其上下有關系成立的時間里始終生效(Vue官方文檔)。
也就是說他們允許父子組件傳值、爺孫組件傳值。
// 爺爺組件通過provide將自己的數據以對象形式傳出去
provide() {
return {
parentValue: '我是兒他爹,也是孫他爺'
}
},
//兒子組件接收
inject: {
// 使用一個默認值使其變成可選項
parentValue: {
// 健名
from: 'parentValue', // 來源
default: '000' // 默認值( vue2.5.0++ )
}
},
//孫子組件接收
inject: {
// 使用一個默認值使其變成可選項
parentValue: {
// 健名
from: 'parentValue', // 來源
default: '000' // 默認值( vue2.5.0++ )
}
},
源代碼(ps:框架是Vue3.x/vue-cli4.54):
父組件:
點擊查看代碼
<template>
<div class="Father">
<h5>這里是父組件</h5>
<el-button @click="runChildMet">使用$refs</el-button>
<el-button @click="useChildren">使用$children</el-button>
<!-- 子組件1 -->
<first-child
:fatherMethod="fatherMethod"
:sendNumber="number"
:sendObj="obj"
@isUseFatherMet="isUseFatherMet"
ref="child1"
></first-child>
<!-- 子組件2 -->
<second-child></second-child>
</div>
</template>
<script>
import child1 from './Child1'
import child2 from './Child2'
export default {
name: 'Father',
props: {},
components: {
'first-child': child1,
'second-child': child2
},
// 父組件通過provide將自己的數據以對象形式傳出去
provide() {
return {
parentValue: '我是兒他爹,也是孫他爺'
}
},
data() {
//這里存放數據
return {
number: 12,
obj: { name: '小明' }
}
},
methods: {
fatherMethod() {
alert('這里是父組件')
},
//子組件調用父組件的方法
isUseFatherMet(val) {
alert('子組件傳值過來了?' + val)
},
//調用子組件里的方法
runChildMet() {
this.$refs.child1.showIsUse('Father')
},
useChildren() {
console.log('dsdsd', this.$children)
//返回的是一個組件集合,如果你能清楚的知道子組件的順序,你也可以使用下標來操作
for (let i = 0; i < this.$children.length; i++) {
this.$children[i].showIsUse()
}
}
}
}
</script>
<style lang="less">
.Father {
font-size: 18px;
.el-divider--horizontal {
margin: 10px 0;
}
}
</style>
兩個兒子組件:
點擊查看代碼
// Child1
<template>
<div class="Child1">
<el-divider></el-divider>
<h5>這里是Child1</h5>
<el-button @click="runProps">使用Props</el-button>
<el-button @click="runFather">使用this.$emit方法</el-button>
<div style="margin:12px 0 ">
<span>Props傳的值:</span>
<div v-if="showProps">
<span v-html="sendObj.name"></span>
<span v-html="sendNumber"></span>
</div>
</div>
<el-button @click="useParent">使用 $parent </el-button>
<el-button @click="sendToBrother">使用Bus給兄弟組件傳值</el-button>
<h5 v-html="'我是Child1,我爹傳來的值:' + parentValue"></h5>
</div>
</template>
<script>
import Bus from '@/utils/Bus.js'
export default {
name: 'Child1',
props: {
/* 函數 */
fatherMethod: {
type: Function, //String、Object、Array等原生構造函數中的一種 、 自定義構造函數 、 這些組成數組
default: null, //默認值,如果 prop 沒有被傳入,則換做用這個值
required: false //定義該 prop 是否是必填項(如果是true,但沒有傳,控制台會報警告)
},
/* 數字 */
sendNumber: {
type: Number,
default: 0
},
/* 字符串 */
sendObj: {
type: Object,
default: {}
}
},
components: {},
provide() {
return {
brotherVal: '我是Child1'
}
},
inject: {
// 使用一個默認值使其變成可選項
parentValue: {
// 健名
from: 'parentValue', // 來源
default: '000' // 默認值( vue2.5.0++ )
}
},
data() {
//這里存放數據
return {
showProps: false //是否顯示父組件傳的值
}
},
methods: {
//使用props傳值
runProps() {
this.showProps = true
this.fatherMethod()
},
//運行父組件的方法
runFather() {
this.$emit('isUseFatherMet', true)
},
//父組件會調用這個方法
showIsUse(val) {
alert('這里是Child1,是' + val + '調用的這個方法')
},
useParent() {
this.$parent.isUseFatherMet('是的,傳過來了,而且我用了$parent')
},
//給兄弟組件傳值
sendToBrother() {
Bus.emit('toBrotherVal', '我是傳向兄弟組件的值')
}
}
}
</script>
// Child2
<template>
<div class="Child2">
<el-divider></el-divider>
<h5>這里是Child2</h5>
<h5 v-html="'我是Child2,我爹傳來的值:' + parentValue"></h5>
<grond-son></grond-son>
<div>----------------------------</div>
<h5 v-html="brotherVal"></h5>
<div style="font-size:15px;padding:5px;margin-bottom:20px">
很顯然,上面兄弟組件使用 provide/inject
傳值沒有傳過來,因為官方說的是在組件之間存在上下有關系才會生效
</div>
</div>
</template>
<script>
import Bus from '@/utils/Bus.js'
import grondSon from './GrandSon.vue'
export default {
name: 'Child2',
props: {},
components: {
'grond-son': grondSon
},
data() {
//這里存放數據
return {}
},
inject: {
// 使用一個默認值使其變成可選項
parentValue: {
// 健名
from: 'parentValue', // 來源
default: '000' // 默認值( vue2.5.0++ )
},
brotherVal: {
from: 'brotherVal',
default: 'Child222'
}
},
methods: {},
mounted() {
Bus.on('toBrotherVal', (val) => {
alert('這里是Child2,傳來的值:' + val)
})
},
unmounted() {
//移除toBrotherVal
Bus.off('toBrotherVal')
}
}
</script>
孫子組件:
點擊查看代碼
<!-- 孫子組件 -->
<template>
<div class="GrandSon">
<div style="text-align:center">這里是孫子的地界,我在Child2里</div>
<h5 v-html="'我爺爺傳來的值:' + parentValue"></h5>
</div>
</template>
<script>
//這里可以導入其他文件(比如:組件,工具js,第三方插件js,json文件,圖片文件等等)
//例如:import 《組件名稱》 from '《組件路徑》';
export default {
name: 'GrandSon',
props: {},
//import引入的組件需要注入到對象中才能使用
components: {},
inject: {
// 使用一個默認值使其變成可選項
parentValue: {
// 健名
from: 'parentValue', // 來源
default: '000' // 默認值( vue2.5.0++ )
}
},
data() {
//這里存放數據
return {}
},
//計算屬性
computed: {},
//監控data中的數據變化
watch: {},
methods: {},
mounted() {}
}
</script>
四、總結
組件傳值的方式:
1、使用 props 傳值
2、使用 this.emit();
3、使用 this.$refs
4、使用 $parent 和 $children 方法
5、定義一個js文件,暴露一個vue實例,使用Bus.$on() 和 Bus.$emit()【別忘了使用Bus.$off 刪除傳值事件,ps:Vue2.x】
6、定義一個js文件,引入mitt依賴,然后使用Bus.on() 和 Bus.emit()【別忘了Bus.off 刪除傳值事件,ps:Vue3.x、Vue2.x】
7、使用 provide 和 inject
Vue組件傳值的方法有很多很多,我這邊總結的肯定也是不全的,當然無論是使用props、this.$emit還是別的什么方法,高性能、方便易懂,我認為是最重要的(我就是這么認為的!!耶穌來了也不好使),而且就技術更新迭代速度來說,本文說不定過幾年就不好用了呢,哈哈哈。安啦安啦,有更新的話,會更的......
借鑒各位大佬:
vue事件之vm.$on事件和v-on事件:https://blog.csdn.net/jaquechen/article/details/102557718
VUE3前端筆記 Mitt事務總線使用方法:https://blog.csdn.net/fuweipeng2012/article/details/113812794