首页 组件之间通信
文章
取消

组件之间通信

父组件向子组件传值

通过props配置向子组件传值

1.在父组件App注册子组件User时通过自定义属性值传值

1
2
3
4
5
<template>
  <div id="app">
    <User :getUserName='getUserName' name='我是爸爸' age='50'></User>
  </div>
</template>

2.子组件User中用props接受属性,该属性会被直接赋予给User组件实例,这样就可以像使用数据对象的方法一样直接使用了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
  <div>
    <div>方法名</div>
    <div>姓名</div>
    <div>年龄</div>
  </div>
</template>

<script>
export default {
  name: "User",
  props:['getUserName','name','age'],
};
</script>

子组件向父组件传值

子组件props接收函数

子组件props接收父组件的传入函数,函数在子组件中被调用,作用域却在父组件,因而子组件在函数中传入的函数就能在父组件接收,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
  <div id="app">
    // 将函数传递给子组件
    <User :getUserName="getUserName"></User>
  </div>
</template>

<script>
import User from "./views/User.vue";
export default {
  name: "App",
  components: {
    User,
  },
  methods: {
    // 父类的方法,作为媒介接收来自子组件的值
    getUserName(username){
      console.log('获取到用户名:'+username)
    }
  },
};
</script>

子组件接收函数,并将值通过参数传递给父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
  <div class="user">
    <div>用户姓名</div>
  </div>
</template>

<script>
export default {
  name: "User",
  // 接收函数
  props:['getUserName'],
  data() {
    return {
      name: 'flameking',
    };
  },
  mounted(){
    // 传入name
    this.getUserName(this.name)
  }
};
</script>

自定义事件

(一)第一种定义方式,在标签当中定义
1.首先定义我们的事件和事件被触发的回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
  <div id="app">
    // 自定义我们的事件名
    // 加上事件修饰符:once,表示事件至多触发一次
    <Home @testevent.once="getFamilyAddress"></Home>
  </div>
</template>

<script>
import Home from "./views/Home.vue";
export default {
  name: "App",
  components: {
    Home
  },
  methods: {
    // 定义回调函数,接收一个参数,address
    getFamilyAddress(address){
      console.log('家庭住址:'+address)
    }
  },
};

2.事件被注册在子组件Home当中,自然就需要在Home当中触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
export default {
  name: "Home",
  data() {
    return {
      address: "湖南 衡阳",
    };
  },
  // 触发事件testevent,并传入参数address
  mounted() {
    this.$emit("testevent", this.address);
    console.log(this);
  },
};
</script>

(二)第二种定义方式,直接获取组件实例,并注册事件
1.我们知道事件是被绑定在子组件实例中,第一种定义方式是在标签当中定义事件并与组件一同注册,现在我们优先获取子组件实例,然后注册事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<template>
  <div id="app">
    // ref配置获取当前组件实例
    <Home ref="home"/>
  </div>
</template>

<script>
import Home from "./views/Home.vue";
export default {
  name: "App",
  components: {
    Home,
  },

  methods: {
    getFamilyAddress(address,x,y,z) {
      console.log("家庭住址:",address,x,y,z);
    },
    // 接收多个参数,出为首一个参数,其余参数放在a数组中
    // getFamilyAddress(address,...a) {
    //   console.log("家庭住址:",address,a);
    // },
  },
  // 当父组件挂载完成后注册事件testevent,以及事件的回调函数getFamilyAddress
  mounted() {
    this.$refs.home.$on('testevent',this.getFamilyAddress)
    // 事件至多被触发一次
    // this.$refs.home.$once('testevent',this.getFamilyAddress)

  },
};
</script>

2.子组件如原来方式触发事件,不过有一点需要注意:由于事件是在父组件App被挂载往后才注册的,如果在子组件挂载完后里面触发事件不会有效因为App在所有子组件挂载完成后完成挂载,也就是说子组件内mounted触发的事件压根还未注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
  <div class="family">
    <div>家庭住址</div>
    // 在经过一次点击事件后触发testevent事件
    <button @click="sendFamilyAddress">send</button>
  </div>
</template>

<script>
export default {
  name: "Home",
  components: {},
  data() {
    return {
      address: "湖南 衡阳",
    };
  },
  methods: {
    // 在sendFamilyAddress方法内触发testevent事件,并传入参数多个参数
    sendFamilyAddress(){
      this.$emit("testevent", this.address,'中国','湖南','衡阳');
      console.log(this);
    }
  },
  mounted() {
  },
};
</script>

这种方式下比第一种更加灵活,主要体现在注册事件的时候,例如让事件在App组件挂载完成后3秒后完成注册

1
2
3
4
5
  mounted() {
    setTimeout(() => {
      this.$refs.home.$on('testevent',this.getFamilyAddress)
    },3000)
  },

解绑自定义事件

如果自定义事件需要解绑,那么也需要在通过事件的组件实例进行解绑,解绑总共三种方法:

  • this.$off(‘getUserAge’),解绑一个事件
  • this.$off([‘getUserName’,’getUserAge’]),解绑多个事件
  • this.$off(),解绑当前组件的所有事件

自定义事件需要注意的点

1.如果销毁当前组件实例,由于实例被销毁,绑定在它身上的所有自定义事件无效,响应式无法再起作用,即页面不会随着数据的修改而动态更新**

1
2
3
destroyComponent(){
  this.$destroy()
}

2.回调函数里面的this指向
如果自定义事件的回调函数我们不卸载methods中,而是直接以匿名函数的形式作为参数传入:

1
2
3
4
5
6
7
8
mounted() {
    this.$refs.home.$on("testevent", function (address, ...a) {
      console.log("家庭住址:", address, a);
      // 此时的this不是父组件实例对象而是子组件实例对象
      console.log(this)
    });
    this.$refs.user.$on("getUserAge", this.getUserAge);
}

如何获取到父组件的实例对象呢?

  • 使用箭头函数的形式
  • 再函数外使用变量保存父组件的实例对象,延长作用域
  • 或直接使用原来的形式

3.给组件绑定原生事件
给组件绑定原生事件有两种形式:
第一种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
  <div id="app">
    <div>我是父组件</div>
    <Home @click="callNative" ref="home" />
  </div>
</template>
<script>
import Home from "./views/Home.vue";
export default {
  name: "App",
  components: {
    Home,
  },
  methods: {
    callNative(){
      console.log('绑定原生事件')
    }
  },
};
</script>

像这种Vue会把click作为自定义事件进行处理

第二种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
  <div id="app">
    <div>我是父组件</div>
    // 通过native事件修饰符就可以调用到原生事件
    <Home @click.native="callNative" ref="home" />
  </div>
</template>
<script>
import Home from "./views/Home.vue";
export default {
  name: "App",
  components: {
    Home,
  },
  methods: {
    callNative(){
      console.log('绑定原生事件')
    }
  },
};
</script>

注意:native事件修饰符只能用在组件当中,并且事件被绑定再了组件的root元素上

兄弟组件互相通信

全局事件总线

全局事件总线与自定义事件模型对比图
全局事件总线与自定义事件模型对比图

全局事件总线将有vm担任

本文由作者按照 CC BY 4.0 进行授权