VUE 组件间传值的几种实现方式

1.父组件向子组件传值 props

父组件使用v-bind绑定数据在子组件上,子组件通过props接收。缺点:组件嵌套层次太多时,传递数据比较麻烦。

父组件代码:

<template>
  <div>
    <h1>父组件</h1>
    <Child :msg="msg" />
  </div>
</template>
<script>
import Child from "./Child.vue"
export default {
  components: { Child },
  data () {
    return {
      msg: "data from Parent"
    }
  }
}
</script>
<style scoped>
</style>

子组件代码:

<template>
  <div>
    <h2>{{msg}}</h2>
  </div>
</template>
<script>
export default {
  name: "Child",
  props: {
    msg: String
  },
  data () {
    return {}
  }
}
</script>
<style scoped>
</style>

2.子组件向父组件传值 $emit(事件形式)

子组件使用 $emit派发事件,父组件使用 v-on监听事件

子组件代码:

<template>
  <div>
    <button @click="sendMsgToParent">send Msg To Parent</button>
  </div>
</template>
<script>
export default {
  name: "Child",
  data () {
    return {
      msg: "data from Child"
    }
  },
  methods: {
    sendMsgToParent () {
      this.$emit("sendMsg", this.msg)
    }
  }
}
</script>
<style scoped>
</style>

父组件代码:

<template>
  <div>
    <Child @sendMsg="getMsgFromChild" />
  </div>
</template>
<script>
import Child from "./Child.vue"
export default {
  name: "Parent",
  components: { Child },
  data () {
    return {
    }
  },
  methods: {
    getMsgFromChild (msg) {
      console.log(msg);
    }
  }
}
</script>
<style scoped>
</style>

3.使用全局事件总线(Event bus) $emit / $on

使用Vue的事件机制, 通过在Vue实例上使用 $on来监听事件,然后在其他组件 $emit触发相应的事件来传递数据。这种方式可以实现在任意两个组件间传递数据。缺点:不支持响应式。
在 main.js 中定义一个空的 Vue 实例作为事件总线:

import Vue from 'vue'
export const bus = new Vue()

子组件A的代码:

<template>
  <div>
    <button @click="sendMsgToChildB">send Msg To Parent</button>
  </div>
</template>
<script>
import { bus } from "../main"
export default {
  name: "ChildA",
  data () {
    return {
    }
  },
  methods: {
    sendMsgToChildB () {
      bus.$emit("sendMsgToChildB", "data from ChildA")
    }
  }
}
</script>
<style scoped>
</style>

子组件B的代码:

<template>
  <div>
    <h2>{{msgFromChildA}}</h2>
  </div>
</template>
<script>
import { bus } from "../main"
export default {
  name: "ChildB",
  data () {
    return {
      msgFromChildA: ""
    }
  },
  mounted () {
    bus.$on("sendMsgToChildB", (msg) => {
      this.msgFromChildA = msg
    })
  }
}
</script>
<style scoped>
</style>

4.provider / inject

祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

祖先组件的代码:

<template>
  <div>
    <ChildA />
  </div>
</template>
<script>
import ChildA from "./ChildA.vue"
export default {
  components: { ChildA },
  provide () {
    return {
      msgToChildB: "data from parent"
    }
  }
}
</script>
<style scoped>
</style>

子组件A的代码:

<template>
  <div>
    <ChildB />
  </div>
</template>
<script>
import ChildB from "./ChildB.vue"
export default {
  name: "ChildA",
  components: { ChildB }
}
</script>
<style scoped>
</style>

子组件B的代码:

<template>
  <div>
    <h2>{{msgToChildB}}</h2>
  </div>
</template>
<script>
export default {
  name: "ChildB",
  inject: ["msgToChildB"]
}
</script>
<style scoped>
</style>

5.使用 Vuex

当应用的数据状态比较复杂或者需要在多个组件之间共享时,可以使用 Vuex。可以在任何组件中访问和修改 Vuex 存储的数据,通过 mutations 来修改状态,通过 actions 来触发 mutations。这种方式可以方便地在不同的组件中进行状态管理和数据共享。
新建store文件:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    msg: "data from Vuex"
  },
  mutations: {
    updateMsg (state, payload) {
      state.msg = payload
    }
  }
})

export default store

子组件A的代码:

<template>
  <div>
    <button @click="sendDataToChild">send data to childB</button>
  </div>
</template>
<script>
import { mapActions, mapMutations } from 'vuex'
export default {
  name: "ChildA",
  methods: {
    ...mapMutations(["updateMsg"]),
    sendDataToChild () {
      this.updateMsg("data from childA")
    }
  }
}
</script>
<style scoped>
</style>

子组件B的代码:

<template>
  <div>
    <h2>{{msg}}</h2>
  </div>
</template>
<script>
import { mapState } from 'vuex'
export default {
  name: "ChildB",
  computed: {
    ...mapState(["msg"])
  }
}
</script>
<style scoped>
</style>

6.$attrs / $listeners

多级组件嵌套需要传递数据时,通常使用的方法是通过 vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此 Vue2.4 版本提供了另一种方法---- $attrs/ $listeners。

$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=" $attrs" 传入内部组件。通常配合 interitAttrs (Vue组件的interitAttrs)选项一起使用。

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

祖父元素代码:

<template>
  <div>
    <Father :name="name"
            :like="like"
            :age="24"
            work="程序员"
            @methods1="onMethods1"
            @methods2="onMethods2" />
  </div>
</template>
<script>
import Father from "./Father.vue"
export default {
  components: { Father },
  data () {
    return {
      name: "张三",
      like: "篮球",
      age: "24"
    }
  },
  methods: {
    onMethods1 (msg1) {
      console.log(`${msg1} running`);
    },
    onMethods2 (msg2) {
      console.log(`${msg2} running`);
    }
  }
}
</script>
<style scoped>
</style>

父元素代码:

<template class="childA">
  <div>
    <h2>Father name:{{ name }}</h2>
    <h2>Parent的$attrs:{{ $attrs }}</h2>
    <Child v-bind='$attrs'
           v-on="$listeners" />
  </div>
</template>
<script>
import Child from './Child.vue'
export default {
  name: "Father",
  components: { Child },
  inheritAttrs: false,//关闭自动挂在到根元素上的没有在 props 声明的属性
  props: {
    name: String
  },
  data () {
    return {
      father: "Father"
    }
  },
  created () {
    console.log(this.$attrs);//like: '篮球', age: 24, work: '程序员'}
  },
  mounted () {
    this.$emit("methods1", this.father);
  },
}
</script>
<style scoped>
.childA {
  width: 100%;
  border: 1px solid black;
}
</style>

子组件代码:

<template>
  <div>
    <h2>Child: {{ $attrs }}</h2>
  </div>
</template>
<script>
export default {
  name: "Child",
  inheritAttrs: false,
  props: {
    like: String
  },
  data () {
    return {
      child: "child"
    }
  },
  created () {
    console.log(this.$attrs);//{age: 24, work: '程序员'}
  },
  mounted () {
    this.$emit("methods1", this.child)
  }
}
</script>
<style scoped>
</style>

实现效果:
listeners

在父组件和子组件 中,需要设置 inheritAttrs: false,来禁止自动将父组件传递的属性绑定到子组件的根元素上。这样,我们就可以使用 v-bind=“$attrs” 把所有属性绑定到子元素上。使用 $emit(‘click’) 来触发祖父组件传递过来的点击事件。

7. $parent/ $children 与 ref

$parent / $children:访问父 / 子实例
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
注意:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。
弊端:这两种方法都无法在跨级或兄弟间通信。 $children 并不保证顺序,也不是响应式的。

父组件代码:

<template>
  <div>
    <h2>parentComponent:{{getMsgFromChild}}</h2>
    <ChildComponent ref="childA" />
    <h2>ref拿到的子组件的title:{{childTitle}}</h2>
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
  components: { ChildComponent },
  data () {
    return {
      msgFromParent: "msg from parent",
      getMsgFromChild: "",
      childTitle: ""
    }
  },
  mounted () {
    this.getMsgFromChild = this.$children[0]._data.msgFromChild
    //ref 拿到子组件的title
    this.childTitle = this.$refs.childA.title
    //ref 执行子组件的alert事件
    this.$refs.childA.alertChildMsg()
  },
}
</script>
<style scoped>
</style>

子组件代码:

<template>
  <div>
    <h2>childComponent: {{getmsgFromParent}}</h2>
  </div>
</template>
<script>
export default {
  name: "ChildComponent",
  data () {
    return {
      msgFromChild: "msg from child",
      getmsgFromParent: "",
      title: "这是子组件定义的数据"
    }
  },
  mounted () {
    this.getmsgFromParent = this.$parent._data.msgFromParent
  },
  methods: {
    alertChildMsg () {
      alert("子组件的alert事件")
    }
  }
}
</script>
<style scoped>
</style>

总结

常见的使用场景可以分为三类:

1. 父子间通信:
父向子传递数据通过 props,子向父传递数据通过 $emit (event) ;
$parent / $children:访问父 / 子实例;
ref 访问组件实例;
provide / inject API;
$attrs / $listeners

2. 兄弟通信:
全局事件总线(Event bus) $emit / $on;
Vuex

3. 跨级通信:
全局事件总线(Event bus) $emit / $on;
provide / inject API;
$attrs / $listeners;
Vuex

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值