vue的十二种通信方式总结

215 阅读2分钟

组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。 vue组件间的传值方式多种多样,并不局限于父子传值、事件传值这些。

文章目录

  • provide / inject
  • props (父传子)
  • $emit(子传父)
  • eventBus(全局创建Vue实例)
  • vuex (状态管理)
  • parent/parent / children / $refs (获取组件实例)
  • $attrs
  • $listeners
  • mixin
  • 路由传值 /引用数据类型值传递实现父子间数据的共享

1.provide / inject

这一组选项需要一起使用,允许一个祖先组件向其所有的后代组件注入一个依赖,不论组件层级有多深,并在其上下游关系成立的时间里始终生效。

// inject 选项应该是:一个字符串数组,或一个对象,对象的 key 是本地的绑定名

// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}
// 子组件注入 'foo' (数组形式)
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

或 (对象形式)
var Child = {
  inject: {
    foo: {
        from: 'bar', // 可选
        default: 'self defined content' // 默认值
    }
  },
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

需要注意的是: Vue 2.2.1 或更高版本中,inject注入的值会在 props 和 data 初始化之前得到

const Child = {
  inject: ['foo'],
  data () {
    return {
      bar: this.foo // 输出bar的值与foo相同
    }
  }
}
const Child = {
  inject: ['foo'],
  props: {
    bar: {
      default () {
        return this.foo
      }
    }
  }
}

-----------------------------------------------------------------------------------------------
// 注入可以通过设置默认值使其变成可选项
const Child = {
  inject: {
    foo: { // 注意: 此处key值必须是父组件中provide的属性的key
      from: 'bar', // 属性是在可用的注入内容中搜索用的 key (字符串或 Symbol), data或者props中的可搜索值
      default: 'foo' // 属性是降级情况下使用的 value, 默认值为 ‘foo’
    }
  }
}
// 与 prop 的默认值类似
const Child = {
  inject: {
    foo: {
      from: 'bar',
      default: () => [1, 2, 3] // 默认值为引用类型时,需要使用一个工厂方法返回对象
    }
  }
}

2.props (父传子)

Vue.component('props-demo-advanced', {
  props: {
    age: {
      type: Number,
      default: 0
    }
  }
})

// 父组件中注册和使用组件,并传值
<props-demo-advanced :age="age"> </props-demo-advanced>

3.$emit(子传父)

this.$emit('eventName', payload)
// 父组件 Parent
<Parent @evnetName="sayHi"></Parent>

4.eventBus(全局创建Vue实例)

vuex目录结构.webp

999.png

5.parent/parent / children / $refs (获取组件实例)

<template>
  <div class="hello">
    <ipc ref="ipcRef"></ipc>
  </div>
</template>
<script>\
import ipc from './ipc'
export default {
  name: 'HelloWorld',
  data () {
    return {
      parentVal: 'parent content'
    }
  },
  mounted () {
    console.log(this.$refs.ipcRef.$data.child1) // "child1 content"
    console.log(this.$children[0].$data.child2) // "child2 content"
  },
  components: {
    ipc
  }
}
</script>
<template>
  <div>
  </div>
</template>
<script>
export default {
  props: {
  },
  data () {
    return {
      child1: 'child1 content',
      child2: 'child2 content'
    }
  },
  mounted () {
    console.log(this.$parent.parentVal) // "parent content"
  }
}
</script>

6.$attrs

(1) 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外) (2) 当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外) 可以通过v-bind="$attrs" 将所有父作用域的绑定 (class、style、 ref 除外) 传入内部组件 注: 在创建高级别的组件时非常有用 // 根组件HelloWorld.vue 中引入 ipc.vue koa=“ipcRef” name=“go” id=“id” ref=“ref” style=“border: 1px solid red;” class=“className”

// ipc.vue 中引入 ipcChild.vue

  <div>
    <ipcChild v-bind="$attrs" selfDefine="selfDefine"></ipcChild>
  </div>
</template>
<script>
import ipcChild from './ipcChild'
export default {
  components: {
    ipcChild
  },
  mounted () {
    console.log(this.$attrs) // {id: "id", name: "go", koa: "ipcRef"}
  }
}
</script>
// ipcChild.vue中打印接收到的$attrs
<script>
export default {
  created () {
    console.log(this.$attrs) // "{"selfDefine":"selfDefine","koa":"ipcRef","name":"go","id":"id"}"
  }
}
</script>

7.$listeners

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器 通过 v-on="$listeners" 将父作用域的时间监听器传入内部组件

A、B、C三个组件依次嵌套, B嵌套在A中,C嵌套在B中。 借助 B 组件的中转,从上到下props依次传递,从下至上,emit事件的传递,达到跨级组件通信的效果。emit事件的传递,达到跨级组件通信的效果。attrs以及listeners的出现解决的的问题,B组件在其中传递props以及事件的过程中,不必在写多余的代码,仅仅是将 listeners 的出现解决的的问题,B 组件在其中传递props以及事件的过程中,不必在写多余的代码,仅仅是将 attrs以及$listeners 向上或者向下传递即可。

8. .sync

可以帮我们实现父组件向子组件传递的数据 的双向绑定,所以子组件接收到数据后可以直接修改,并且会同时修改父组件的数据

// Parent.vue 
<template> 
    <child :page.sync="page"></child>
</template> 
<script> 
export default { 
    data(){ 
    return { 
    page:1 
    } 
   } 
 } 
// Child.vue 
export default {
    props:["page"], 
    computed(){ 
    // 当我们在子组件里修改 currentPage 时,父组件的 page 也会随之改变
    currentPage { 
    get(){
    return this.page 
    }, 
    set(newVal){ 
    this.$emit("update:page", newVal)
    }
   } 
  } 
 }
</script>
\

9. .model

可以帮我们实现父组件向子组件传递的数据 的双向绑定,所以子组件接收到数据后可以直接修改,并且会同时修改父组件的数据

// Parent.vue 
<template> 
<child :page.sync="page"></child> 
</template> <script> 
export default { 
    data(){ 
    return { 
    page:1 
    } 
   }
  } 
  // Child.vue 
  export default { 
  props:["page"], 
  computed(){ 
  // 当我们在子组件里修改 currentPage 时,父组件的 page 也会随之改变 
  currentPage { 
  get(){ 
  return this.page
  }, 
  set(newVal){ 
      this.$emit("update:page", newVal) 
      } 
     }
    } 
   } 
      </script>

\

10. mitt

Vue3 中没有了 EventBus 跨组件通信,但是现在有了一个替代的方案 mitt.js,原理还是 EventBus 先安装 npm i mitt -S 然后像以前封装 bus 一样,封装一下

mitt.js
import mitt from 'mitt'
const mitt = mitt() 
export default mitt

然后两个组件之间通信的使用

// 组件 A 
<script setup>
import mitt from './mitt' 
const handleClick = () => { mitt.emit('handleChange') } 
</script>

//组件b
// 组件 B 
<script setup> 
import mitt from './mitt' 
import { onUnmounted } from 'vue' 
const someMethed = () => { ... } 
mitt.on('handleChange',someMethed) 
onUnmounted(()=>{ mitt.off('handleChange',someMethed) }) 
</script>

11.slot

就是把子组件的数据通过插槽的方式传给父组件使用,然后再插回来

// Child.vue 
<template>
<div> 
<slot :user="user"></slot>
</div> 
</template>
export default{ 
data(){ 
return { 
user:{ name:"沐华" } 
   } 
 } 
}\


// Parent.vue 
<template> 
<div> 
<child v-slot="slotProps"> 
{{ slotProps.user.name }}</child> 
</div> 
</template>


12.vuex

// store/index.js 
import { createStore } from "vuex" 
export default createStore({ 
state:{ count: 1 },
getters:{ getCount: state => state.count }, 
mutations:{ add(state){ state.count++ } }
})

// main.js 
import { createApp } from "vue"
import App from "./App.vue" import store from "./store" createApp(App).use(store).mount("#app")

// Page.vue // 方法一 直接使用
<template> 
<div>{{ $store.state.count }}</div> 
<button @click="$store.commit('add')">按钮</button> 
</template>

// 方法二 获取 
<script setup> 
import { useStore, computed } from "vuex" 
const store = useStore() 
console.log(store.state.count) // 1 
const count = computed(()=>store.state.count)
// 响应式,会随着vuex数据改变而改变 
console.log(count) // 1 
</script>