Vue组件通信

props/$emit

  • 父组件向子组件传值

在子组件中定义 props 选项,在父组件中通过 v-bind:props属性值 的方式传值给子组件。

<!-- Children.vue -->
<template>
  <div>
    <ul>
      <li v-for="item in list" :key="item.index">{{item.name}}</li>
    </ul>
  </div>
</template>
<script>
export default {
  props: ['list']
}
</script>

<!-- Parent.vue -->
<template>
  <Children v-bind:list="list"></Children>
</template>
<script>
import Children from './Children'
export default {
  components: {
    Children
  },
  data() {
    return {
      list: [
        { name: "HTML", index: 1 },
        { name: "CSS", index: 2 },
        { name: "JavaScript", index: 3 }
      ]
    }
  }
}
</script>
  • 子组件向父组件传值

在子组件上用 emit 来绑定一个自定义事件,emit 方法第二个参数就是要传递给父组件的值。在父组件中通过 v-on:子组件自定义事件名 的方式监听这个自定义事件并接收参数。

<!-- Children.vue -->
<template>
  <div>
    <button @click="transfer">给父组件传值</button>
  </div>
</template>
<script>
export default {
  methods: {
    transfer() {
      this.$emit("send-val", "hello world");
    }
  }
}
</script>

<!-- Parent.vue -->
<template>
  <div>
    <Children v-on:send-val="getVal"></Children>
    <p>{{text}}</p>
  </div>
</template>
<script>
import Children from './Children'
export default {
  components: {
    Children
  },
  data() {
    return {
      text: ''
    }
  },
  methods: {
    getVal(param) {
      this.text = param;
    }
  }
}
</script>

$parent/$children

$parent 可以获得当前实例的父实例,$children 可以获得当前实例的直接子组件。$children 得到的是一个数组,但子组件的顺序是不确定的。

<!-- Children.vue -->
<template>
  <div>
    <p>在子组件中得到父组件的值:{{getParentVal}}</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      childText: "child"
    }
  },
  computed: {
    getParentVal() {
      return this.$parent.parentText;
    }
  }
}
</script>

<!-- Parent.vue -->
<template>
  <div>
    <button @click="getChildrenVal">得到子组件的值</button>
    <p>在父组件中得到子组件的值:{{childText}}</p>
    <Children></Children>
  </div>

</template>
<script>
import Children from './Children'
export default {
  components: {
    Children
  },
  data() {
    return {
      parentText: "parent",
      childText: ""
    }
  },
  methods: {
    getChildrenVal() {
      this.childText = this.$children[0].childText
    }
  }
}
</script>

provide/inject

在父组件中使用 provide 提供一个对象或一个返回对象的函数,在子组件中使用 inject 注入一个变量。这种方法可以用于多层嵌套的组件之间的通信。

<!-- Children.vue -->
<template>
  <div>
    <p>在Children中得到的值:{{foo}}</p>
  </div>
</template>
<script>
export default {
  inject: {
    foo: {
      // 如果它需要从一个不同名字的 property 注入,则使用 from 来表示其源 property:
      from: 'bar',
      // 设置默认值
      default: 'foo'
    }
  }
}
</script>

<!-- Parent.vue -->
<template>
  <div>
    <p>在Parent中得到的值:{{foo}}</p>
    <Children></Children>
  </div>
</template>
<script>
import Children from './Children'
export default {
  components: {
    Children
  },
  inject: ['foo']
}
</script>

<!-- GrandParent.vue -->
<template>
  <div>
    <Parent></Parent>
  </div>
</template>
<script>
import Parent from './Parent.vue'
export default {
  components: { Parent },
  provide: {
    foo: 'hello',
    bar: 'world'
  }
}
</script>

image-20220612162321327

ref/$refs

在子组件上使用 ref,引用指向的是组件实例,在父组件上使用 $refs 可以得到这个组件实例,$refs 返回的是一个数组。

<!-- Children.vue -->
<template>
  <div></div>
</template>
<script>
export default {
  data() {
    return {
      foo: 'hello'
    }
  },
  methods: {
    bar() {
      console.log('bar');
    }
  }
}
</script>

<!-- Parent.vue -->
<template>
  <div>
    <Children ref="children"></Children>
  </div>
</template>
<script>
import Children from './Children'
export default {
  components: {
    Children
  },
  mounted() {
	const children = this.$refs['children'];
	console.log(children.foo);
	children.bar();
  }
}
</script>

eventBus

创建一个 Vue 实例(作为事件总线)并将其导出,其他组件导入这个事件总线后,可以向这个事件总线注册发送事件和接收事件,所有组件都可以通知其他组件。可以用于兄弟组件通信和父子组件通信。

<!-- EventBus.vue -->
<script>
import Vue from 'vue'
export default new Vue;
</script>

<!-- Children.vue -->
<template>
  <div>
    <button @click="sendToParent">sendToParent</button>
  </div>
</template>
<script>
import EventBus from './EventBus.vue'
export default {
  methods: {
    sendToParent() {
      EventBus.$emit('sayHello', 'hello')
    }
  }
}
</script>

<!-- Parent.vue -->
<template>
  <div>
    <Children></Children>
  </div>
</template>
<script>
import Children from './Children'
import EventBus from './EventBus.vue';
export default {
  components: {
    Children
  },
  mounted() {
	EventBus.$on('sayHello', msg => {
		console.log(msg);
	})
  }
}
</script>

移除对事件的监听:

EventBus.$off('sayHello');

$attrs/$listeners

$attrs 包含父组件中所有 attribute 绑定,不包括这三类:作为 prop 传递给子组件的 attribute、class、style,可以通过 v-bind="$attrs" 传入内部组件。$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器,可以通过 v-on="$listeners" 传入内部组件。这种方式可以用于跨级通信。

<!-- Children.vue -->
<template>
  <div>
    <p>子组件:</p>
    <p>props: {{ prop2 }}</p>
    <p>$attrs: {{ $attrs }}</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      text: 'children'
    }
  },
  inheritAttrs: false,
  props: ['prop2'],
  mounted() {
    this.$emit('method2', this.text);
  }
}
</script>

<!-- Parent.vue -->
<template>
  <div>
    <p>父组件:</p>
    <p>props: {{ prop1 }}</p>
    <p>$attrs: {{ $attrs }}</p>
    <!-- 使用v-bind="$attrs"把GrandParent中定义的attrs传入Children -->
    <!-- 使用v-on="$listeners"把GrandParent中定义的事件传入Children -->
    <Children v-bind="$attrs" v-on="$listeners"></Children>
  </div>
</template>
<script>
import Children from './Children'
export default {
  components: {
    Children
  },
  data() {
    return {
		text: 'parent'
    }
  },
  inheritAttrs: false,
  props: ['prop1'],
  mounted() {
    this.$emit('method1', this.text);
  }
}
</script>

<!-- GrandParent.vue -->
<template>
  <div>
    <Parent :prop1="name" :prop2="age" @method1="onMethod1" @method2="onMethod2"></Parent>
  </div>
</template>
<script>
import Parent from './Parent.vue'
export default {
  components: { Parent },
  data() {
    return {
      name: 'Tom',
      age: 12
    }
  },
  methods: {
    onMethod1(msg) {
      console.log(`${msg} run...`);
    },
    onMethod2(msg) {
      console.log(`${msg} run...`);
    },
  }
}
</script>

Vuex

参考:https://www.tongjiebin.cn/posts/5c4e.html

总结

父子组件通信:props/$emit、$parent/$children、provide/inject、ref/$refs

兄弟组件通信:eventBus、Vuex

跨级组件通信:provide/inject、$attrs/$listeners、Vuex