Vue之组件通信方法小结

props 接受父组件传递的数据 $emit 派发事件

目录
  1. 父子组件通信
    1. props和$emit
    2. $parent和$children
    3. v-model
    4. .sync
    5. ref
    6. slot-scope
  2. 祖(父)孙组件通信
    1. provide 和 inject
    2. $listeners和$attrs
  3. 其他组件通信
    1. EvenBus
    2. Vuex

父子组件通信#

props$emit#

  • props接受父组件传递的数据
  • $emit派发事件
<!-- CompA.vue -->
<template>
  <div>
    <h1>This is Comp A<h1>
    <!-- 监听自定义事件,传入数据 -->
    <CompB @count="getCount" :message="message"></CompB>
  </div>
</template>
<script>
  import CompB from './CompB'
  export default {
    components: {},
    data() {
      return {
        message: 'hello'
      }
    },
    methods: {
      getCount(val) {
        console.log(val)
      }
    }
  }
</script>
 
<!-- CompB.vue -->
<template>
  <div>
    <h2>This is Comp B</h2>
    <button @click="emitEvent">Emit Event</button>
  </div>
</template>
<script>
  export default {
    // 获取父组件传入的数据
    props: ['message'],
    methods: {
      emitEvent() {
        this.$emit('count', 123) // 派发自定义事件
      }
    }
  }
</script>

使用场景:一般使用场景。

$parent$children#

  • $parent获取父组件实例(数据和方法)
  • $children获得子组件实例的数组(数据和方法)
<!-- CompA.vue -->
<template>
  <div>
    <h1>This is Comp A<h1>
    <CompB></CompB>
  </div>
</template>
<script>
  import CompB from './CompB'
  export default {
    components: {},
    data() {
      return {
        message: 'hello'
      }
    },
    mounted() {
      console.log(this.$children[0].count) // 获取子组件数据
      this.$children[0].printCount() // 调用子组件的方法
    }
    methods: {
      printMsg() {
        console.log(this.message)
      }
    }
  }
</script>
 
<!-- CompB.vue -->
<template>
  <div>
    <h2>This is Comp B</h2>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        count: 2018
      }
    }
    mounted() {
      console.log(this.$parent.message) // 获取父组件数据
      this.$parent.printMsg() // 调用父组件的方法
    },
    methods: {
      printCount() {
        console.log(this.count)
      }
    }
  }
</script>

使用场景:需要灵活操作父组件或者子组件数组或者方法时。

v-model#

使用v-model可以在组件中实现双向绑定。

<!-- CompA.vue -->
<template>
  <div>
    <h1>This is Comp A<h1>
    <div>{{content}}</div>
    <CompB v-model="content"></CompB>
  </div>
</template>
<script>
  import CompB from './CompB'
  export default {
    components: {
      CompB
    },
    data() {
      return {
        content: 'hello'
      }
    }
  }
</script>
 
<!-- CompB.vue -->
<template>
  <div>
    <h2>This is Comp B</h2>
    <div>{{content}}</div>
    <input type="text" :value="content" @input="$emit('input', $event.target.value)"></input>
  </div>
</template>
<script>
  export default {
    props: {
      content: String
    },
    // 需要使用 model 指明绑定的对象和事件
    model: {
      prop: 'content',
      event: 'input'
    }
  }
</script>

使用场景:需要双向绑定组件的表单数据时。

.sync#

传递数据的时候,添加修饰符sync可以实现数据双向绑定。.sync是一个语法糖,相当于@update:propData="propData=$event"

注意:使用.sync会出现警告信息,双向绑定的数据后期也不好维护,不建议使用

<!-- CompA.vue -->
<template>
  <div>
    <h1>This is Comp A<h1>
    <div>{{count}}</div>
    <!-- <CompB :count="count" @update:count="count=$event"></CompB> -->
    <CompB :count.sync="count"></CompB>
  </div>
</template>
<script>
  import CompB from './CompB'
  export default {
    components: {
      CompB
    },
    data() {
      return {
        count: 1
      }
    }
  }
</script>
 
<!-- CompB.vue -->
<template>
  <div>
    <h2>This is Comp B</h2>
    <div>{{count}}</div>
    <button @click="add">add count</button>
  </div>
</template>
<script>
  export default {
    props: {
      count: Number
    }
    methods: {
      add() {
        this.count += 1
      }
    },
    watch: {
      'count'(newVal) {
        this.$emit('update:count', newVal) // 必须派发 update:propData 事件,并传入新的数据
      }
    }
  }
</script>

使用场景:需要双向绑定数据时。

ref#

ref可以注册绑定组件的,并且引用组件实例,因此可以很灵活的操作子组件的数据和方法。

<!-- CompA.vue -->
<template>
  <div>
    <h1>This is Comp A<h1>
    <!-- 注册引用信息 -->
    <CompB ref="compB"></CompB>
  </div>
</template>
<script>
  import CompB from './CompB'
  export default {
    components: {
      CompB
    },
    mounted() {
      console.log(this.$refs.compB.count) // 获取子组件数据
      this.$refs.compB..printCount() // 调用子组件的方法
    }
  }
</script>
 
<!-- CompB.vue -->
<template>
  <div>
    <h2>This is Comp B</h2>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        count: 2018
      }
    },
    methods: {
      printCount() {
        console.log(this.count)
      }
    }
  }
</script>

使用场景:需要灵活操作子组件数据和方法时。

slot-scope#

使用作用域插槽也可以使子组件和父组件进行通信。

<!-- CompA.vue -->
<template>
  <div>
    <h1>This is Comp A<h1>
    <CompB :todos="todos">
      <!-- 获得子组件插槽绑定的todo -->
      <div slot-scope="{ todo }">
        <span v-if="todo.isComplete">✓</span>
        {{ todo.text }}
     </div>
    </CompB>
  </div>
</template>
<script>
  import CompB from './CompB'
  export default {
    components: {
      CompB
    },
    data() {
      return {
        todos: [
          {
            id: 1, text: 'TypeScript', isComplete: true
          },
          {
            id: 2, text: 'JavaScript', isComplete: true
          },
          {
            id: 3, text: 'Node', isComplete: false
          }
        ]
      }
  }
  }
</script>
 
<!-- CompB.vue -->
<template>
  <div>
    <h2>This is Comp B</h2>
    <ul>
      <li
        v-for="todo in todos"
        v-bind:key="todo.id"
      >
        <slot v-bind:todo="todo">
        </slot>
      </li>
    </ul>
  </div>
</template>
<script>
  export default {
    props: {
      todos: {
        type: Array,
        default: () => []
      }
    }
  }
</script>

使用场景:子组件传递数据给父组件时。

祖(父)孙组件通信#

provide 和 inject#

只要在父组件中提供数据,孙组件不管层级多深,都可以注入数据。

// 父组件 提供数据
provide() {
  return {
    name: 'Hale'
  }
}
 
// 孙组件 注入数据
inject: ['name']

使用场景:开发插件或者组件库时使用。

$listeners$attrs#

现有父组件 A,子组件 B,孙组件 C,通过在组件 B 中绑定$attrs和监听$listeners,使得组件 A 和组件 C 之间可以通信,起到一个桥梁的作用。

<!-- CompA.vue -->
<template>
  <div>
    <h1>This is Comp A<h1>
    <CompB @count="getCount" :message="message"></CompB>
  </div>
</template>
<script>
  import CompB from './CompB'
  export default {
    components: {},
    data() {
      return {
        message: 'hello'
      }
    },
    methods: {
      getCount(val) {
        console.log(val)
      }
    }
  }
</script>
 
<!-- CompB.vue -->
<template>
  <div>
    <h2>This is Comp B<h2>
    <!-- 绑定`$attrs`和监听`$listeners`-->
    <CompC v-on="$listeners" v-bind="$attrs"></CompC>
  </div>
</template>
<script>
  import CompC from './CompC'
  export default {
    mounted() {
      console.log(this.$listeners) // 可参看$listeners
      console.log(this.$attrs) // 可查看$attrs
  },
  }
</script>
 
<!-- CompC.vue -->
<template>
  <div>
    <h3>This is Comp C</h3>
    <button @click="emitEvent">Emit Event</button>
  </div>
</template>
<script>
  export default {
    props: ['message'],
    methods: {
      emitEvent() {
        this.$emit('count', 123)
      }
    }
  }
</script>

使用场景:创建高级别的组件时。

其他组件通信#

EvenBus#

EventBus 是在非关系型组件之间的通信方式,它主要分为事件的派发和监听。因为 Vue 已经实现了事件派发机制,可以直接使用 Vue 作为中间件。

// 创建并导出Vue的实例 bus.js
import Vue form 'vue'
export default new Vue()
 
// 事件派发 在组件中引入并派发事件
import bus form '../bus'
bus.$emit('eventName', param)
 
// 事件监听 在其他组件中引入并监听事件
import bus form '../bus'
bus.$on('eventName', param => {
  // ...
})

使用场景:非复杂的状态交互但又要用到非关系型组件的通信时。

Vuex#

Vuex 是一个专门为 Vue.js 开发的状态管理库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

使用场景:复杂的状态交互的业务时。