Vue2.x 組件通信方式
Vue2.x 組件通信共有12種
- props
- $emit / v-on
- .sync
- v-model
- ref
- parent
- listeners
- provide / inject
- EventBus
- Vuex
- $root
- slot
父子組件通信可以用:
- props
- $emit / v-on
- listeners ref
- .sync v-model
- parent
跨層級(jí)組件通信可以用:
- provide/inject
- EventBus
- Vuex
- listeners
- $root
Vue2.x 通信使用寫法
下面把每一種組件通信方式的寫法一一列出
1呛凶、props
父組件向子組件傳送數(shù)據(jù),這應(yīng)該是最常用的方式了
子組件接收到數(shù)據(jù)之后,**不能直接修改**父組件的數(shù)據(jù)。會(huì)報(bào)錯(cuò)揪利,所以當(dāng)父組件重新渲染時(shí),數(shù)據(jù)會(huì)被覆蓋祭往。如果子組件內(nèi)要修改的話推薦使用 computed
<template>
<child :msg="msg"></child>
</template>
// Child.vue 接收
export default {
// 寫法一 用數(shù)組接收
props:['msg'],
// 寫法二 用對(duì)象接收挡爵,可以限定接收的數(shù)據(jù)類型、設(shè)置默認(rèn)值咒唆、驗(yàn)證等
props:{
msg:{
type:String,
default:'這是默認(rèn)數(shù)據(jù)'
}
},
mounted(){
console.log(this.msg)
},
}
2届垫、sync
可以幫我們實(shí)現(xiàn)父組件向子組件傳遞的數(shù)據(jù) 的雙向綁定,所以子組件接收到數(shù)據(jù)后可以直接修改全释,并且會(huì)同時(shí)修改父組件的數(shù)據(jù)装处。
// Parent.vue
<template>
<child :page.sync="page"></child>
</template>
<script>
export default {
data(){
return {
page:1
}
}
}
</script>
// Child.vue
<template>
<el-button @click="open()">點(diǎn)擊我改變頁(yè)面數(shù)</el-button>
<div>當(dāng)前頁(yè)碼數(shù):{{currentPage}}</div>
</template>
<script>
export default {
props:["page"],
computed: {
// 當(dāng)我們?cè)谧咏M件里修改 currentPage 時(shí),父組件的 page 也會(huì)隨之改變
currentPage: {
get(){
return this.page
},
set(newVal){
this.$emit("update:page", newVal)
}
}
},
methods: {
open(){
this.currentPage = 10
},
}
}
</script>
3浸船、v-model
和 .sync 類似妄迁,可以實(shí)現(xiàn)將父組件傳給子組件的數(shù)據(jù)為雙向綁定,子組件通過 $emit 修改父組件的數(shù)據(jù)
// Parent.vue
<template>
<child v-model="value"></child>
<div>父組件:{{value}}</div>
</template>
<script>
export default {
data(){
return {
value:1
}
}
}
// Child.vue
<template>
<input :value="value" @input="handlerChange">
<div>子組件: {{value}}</div>
</template>
export default {
props:["value"],
// 可以修改事件名李命,默認(rèn)為 input
model:{
event:"updateValue"
},
methods:{
handlerChange(e){
this.$emit("input", e.target.value)
// 如果有上面的重命名就是這樣
this.$emit("updateValue", e.target.value)
}
}
}
</script>
4登淘、ref
ref 如果在普通的DOM元素上,引用指向的就是該DOM元素;
如果在子組件上项戴,引用的指向就是子組件實(shí)例形帮,然后父組件就可以通過 ref 主動(dòng)獲取子組件的屬性或者調(diào)用子組件的方法槽惫。
// Child.vue
export default {
data(){
return {
name:"小趙"
}
},
methods:{
someMethod(msg){
console.log(msg)
}
}
}
// Parent.vue
<template>
<child ref="childFlag"></child>
</template>
<script>
export default {
mounted(){
const childData = this.$refs.childFlag
console.log(childData.name) // 小趙
childData.someMethod("調(diào)用了子組件的方法")
}
}
</script>
用ref 注冊(cè)子組件,父組件可以通過this.$refs.xx.fn調(diào)用子組件里的函數(shù)辩撑,但是有時(shí)會(huì)出現(xiàn) fn 為定義的情況界斜,這是為什么呢?
vue 官網(wǎng)中ref 下有一段話 "關(guān)于 ref 注冊(cè)時(shí)間的重要說明:因?yàn)?ref 本身是作為渲染結(jié)果被創(chuàng)建的合冀,在初始渲染的時(shí)候你不能訪問它們 - 它們還不存在各薇!$refs 也不是響應(yīng)式的,因此你不應(yīng)該試圖用它在模板中做數(shù)據(jù)綁定君躺。"
也就是說 ref 只有等頁(yè)面加載完成好之后你才能調(diào)用 this.refs 的林螃,所以要等到頁(yè)面渲染之后拿才可以
解決辦法:
1、如果你在mounted里獲取this.$refs俺泣,因?yàn)閐om還未完全加載疗认,所以你是拿不到的, update階段則是完成了數(shù)據(jù)更新到 DOM 的階段(對(duì)加載回來的數(shù)據(jù)進(jìn)行處理)伏钠,此時(shí)横漏,就可以使用this.$refs了
2、如果寫在method中熟掂,那么可以使用 this.$nextTick(() => {}) 等頁(yè)面渲染好再調(diào)用缎浇,這樣就可以了
3、或者加個(gè)定時(shí)器延時(shí)加載this.$refs(這個(gè)方法還沒有試)赴肚。
拓展:
通過 on 綁定事件 (比v-on更靈活)
//父組件
<template>
<div id="app">
<Student ref="student"></Student>
</div>
</template>
<script>
import Student from './components/student.vue'
console.log(Student)
export default {
name: 'App',
components: {
Student
},
mounted () {
this.$refs.student.$on('knowSomething', this.doSomething) //給student組件綁定自定義事件
/*
this.$refs.student.$on('knowSomething', this.doSomething) //doSomething函數(shù)中的this指向
的是父組件App組件
this.$refs.student.$on('knowSomething', () =>{
這里的this指向的是父組件App組件
})
this.$refs.student.$on('knowSomething', function(){
這里的this指向的是觸發(fā)knowSomething事件的組件
})
*/
//this.$refs.student.$once('knowSomething', this.doSomething) //綁定自定義事件素跺,
但是只能觸發(fā)一次
},
methods: {
doSomething (name, something) {
alert('APP知道了學(xué)生' + name + '在' + something)
}
}
}
</script>
//子組件
<template>
<div>
<div>{{ student.name }}--{{ student.age }}</div>
<button @click="tellApp">tellApp</button>
<button @click="unbind">解綁knowSomething事件</button>
</div>
</template>
<script>
export default {
data () {
return {
student: {
name: '張三',
age: '18'
}
}
},
methods: {
tellApp () {
this.$emit('knowSomething', this.student.name, '學(xué)習(xí)')
},
unbind(){
this.$off('knowSomething') //解綁knowSomething事件
/*
this.$off(['knowSomething','a','b']) //解綁多個(gè)事件
this.$off() //解綁所有事件
*/
}
}
}
</script>
5、$emit / v-on
子組件通過派發(fā)事件的方式給父組件數(shù)據(jù)尊蚁,或者觸發(fā)父組件更新等操作亡笑。
// Child.vue 派發(fā)
<template>
<div>
<el-button @click="handleClick()">點(diǎn)擊派發(fā)</el-button>
</div>
</template>
<script>
export default {
data(){
return { msg: "這是發(fā)給父組件的信息" }
},
methods: {
handleClick(){
this.$emit("sendMsg",this.msg)
}
},
}
</script>
// Parent.vue 響應(yīng)
<template>
<child v-on:sendMsg="getChildMsg"></child>
// 或 簡(jiǎn)寫
<child @sendMsg="getChildMsg"></child>
</template>
export default {
methods:{
getChildMsg(msg){
console.log(msg) // 這是父組件接收到的消息
}
}
}
6、listeners
多層嵌套組件傳遞數(shù)據(jù)時(shí)横朋,如果只是傳遞數(shù)據(jù)仑乌,而不做中間處理的話就可以用這個(gè),比如父組件向?qū)O子組件傳遞數(shù)據(jù)時(shí)琴锭。
attrs:包含父作用域里除class和style除外的非props屬性集合晰甚。通過this.attrs 獲取父作用域中所有符合條件的屬性集合,然后還要繼續(xù)傳給子組件內(nèi)部的其他組件决帖,就可以通過 v-bind="$attrs"
listeners:包含父作用域里.native除外的監(jiān)聽事件集合厕九。如果還要繼續(xù)傳給子組件內(nèi)部的其他組件,就可以通過v?on="linteners"使用方式是相同的地回。
// Parent.vue
<template>
<child :name="name" title="1111" ></child>
</template
export default{
data(){
return {
name:"小趙"
}
}
}
// Child.vue
<template>
// 繼續(xù)傳給孫子組件
<sun-child v-bind="$attrs"></sun-child>
</template>
export default{
props:["name"], // 這里可以接收扁远,也可以不接收
mounted(){
// 如果props接收了name 就是 { title:1111 }俊鱼,否則就是{ name:"小趙", title:1111 }
console.log(this.$attrs)
}
}
<!--父組件 parent.vue-->
<template>
<child :name="name" :message="message" @sayHello="sayHello"></child>
</template>
<script>
export default {
inheritAttrs: false,
data() {
return {
name: '通信',
message: 'Hi',
}
},
methods: {
sayHello(mes) {
console.log('mes', mes) // => "hello"
},
},
}
</script>
<!--子組件 child.vue-->
<template>
<grandchild v-bind="$attrs" v-on="$listeners"></grandchild>
</template>
<script>
export default {
data() {
return {}
},
props: {
name,
},
}
</script>
<!--孫子組件 grand-child.vue-->
<template>
</template>
<script>
export default {
created() {
this.$emit('sayHello', 'hello')
},
}
</script>