vue 进阶-组件通信

53 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情

vue 进阶-组件通信

props 和$emit

1、props 和$emit()来进行子父之间传值

父组件与子组件传值 父组件通过标签上面定义传值 子组件通过 props 方法接受数据 子组件向父组件传递数据 子组件通过$emit 方法传递参数

Vue.component('child',{
data(){
return {
mymessage:this.message
}
},
template:`

   <div>
    <input type="text" v-model="mymessage" @input="passData(mymessage)"> </div>
  `,
  props:['message'],//设置props属性值,得到父组件传递过来的数据
  methods:{
   passData(val){
    //触发父组件中的事件,向父组件传值
    this.$emit('getChildData',val)
   }
  }
 })
 Vue.component('parent',{
  template:`
   <div>
    <p>this is parent compoent!</p>
    <child :message="message" v-on:getChildData="getChildData"></child>
   </div>
  `,
  data(){
   return {
    message:'hello'
   }
  },
  methods:{
   //执行子组件触发的事件
   getChildData(val){
    console.log(val)
   }
  }
 })

attrsattrs和listeners

第一种方式处理父子组件之间的数据传输有一个问题:如果父组件 A 下面有子组件 B,组件 B 下面有组件 C,这时如果组件 A 想传递数据给组件 C 怎么办呢? 如果采用第一种方法,我们必须让组件 A 通过 prop 传递消息给组件 B,组件 B 在通过 prop 传递消息给组件 C;要是组件 A 和组件 C 之间有更多的组件,那采用这种方式就很复杂了。Vue 2.4 开始提供了attrsattrs和listeners 来解决这个问题,能够让组件 A 之间传递消息给组件 C。

Vue.component('C',{
template:`

   <div>
    <input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)"> </div>
  `,
  methods:{
   passCData(val){
    //触发父组件A中的事件
    this.$emit('getCData',val)
   }
  }
 })
 Vue.component('B',{
  data(){
   return {
    mymessage:this.message
   }
  },
  template:`
   <div>
    <input type="text" v-model="mymessage" @input="passData(mymessage)">
    <!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
    <!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
    <C v-bind="$attrs" v-on="$listeners"></C>
   </div>
  `,
  props:['message'],//得到父组件传递过来的数据
  methods:{
   passData(val){
    //触发父组件中的事件
    this.$emit('getChildData',val)
   }
  }
 })
 Vue.component('A',{
  template:`
   <div>
    <p>this is parent compoent!</p>
    <B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
   </div>
  `,
  data(){
   return {
    message:'hello',
    messagec:'hello c' //传递给c组件的数据
   }
  },
  methods:{
   getChildData(val){
    console.log('这是来自B组件的数据')
   },
   //执行C子组件触发的事件
   getCData(val){
    console.log("这是来自C组件的数据:"+val)
   }
  }
 })

attrsattrs和on (兄弟之间传值)

Vue.component('brother1',{
  data(){
   return {
    mymessage:'hello brother1'
   }
  },
  template:`
   <div>
    <p>this is brother1 compoent!</p>
    <input type="text" v-model="mymessage" @input="passData(mymessage)">
   </div>
  `,
  methods:{
   passData(val){
    //触发全局事件globalEvent
    bus.$emit('globalEvent',val)
   }
  }
 })
 Vue.component('brother2',{
  template:`
   <div>
    <p>this is brother2 compoent!</p>
    <p>brother1传递过来的数据:{{brothermessage}}</p>
   </div>
  `,
  data(){
   return {
    mymessage:'hello brother2',
    brothermessage:''
   }
  },
  mounted(){
   //绑定全局事件globalEvent
   bus.$on('globalEvent',(val)=>{
    this.brothermessage=val;
   })
  }
 })
 //中央事件总线
 var bus=new Vue();
 var app=new Vue({
  el:'#app',
  template:`
   <div>
    <brother1></brother1>
    <brother2></brother2>
   </div>
  `
 })

vuex

Vuex 实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时,必须通过 Mutation 进行,Mutation 同时提供了订阅者模式供外部插件调用获取 State 数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action,但 Action 也是无法直接修改 State 的,还是需要通过 Mutation 来修改 State 的数据。最后,根据 State 的变化,渲染到视图上。 Vue Components:Vue 组件。HTML 页面上,负责接收用户操作等交互行为,执行 dispatch 方法触发对应 action 进行回应。 dispatch:操作行为触发方法,是唯一能执行 action 的方法。 actions:操作行为处理模块,由组件中的$store.dispatch('action 名称', data1)来触发。然后由 commit()来触发 mutation 的调用 , 间接更新 state。负责处理 Vue Components 接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台 API 请求的操作就在这个模块中进行,包括触发其他 action 以及提交 mutation 的操作。该模块提供了 Promise 的封装,以支持 action 的链式触发。 commit:状态改变提交操作方法。对 mutation 进行提交,是唯一能执行 mutation 的方法。 mutations:状态改变操作方法,由 actions 中的 commit('mutation 名称')来触发。是 Vuex 修改 state 的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些 hook 暴露出来,以进行 state 的监控等。 state:页面状态管理容器对象。集中存储 Vue components 中 data 对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用 Vue 的细粒度数据响应机制来进行高效的状态更新。 getters:state 对象读取方法。图中没有单独列出该模块,应该被包含在了 render 中,Vue Components 通过该方法读取全局 state 对象。

provide/inject

假设有两个组件: A.vue 和 B.vue,B 是 A 的子组件

// A.vue
export default {
 provide: {
  name: '浪里行舟'
 }
}

// B.vue
export default {
 inject: ['name'],
 mounted () {
  console.log(this.name); // 浪里行舟
 }
}

可以看到,在 A.vue 里,我们设置了一个 provide: name,值为 浪里行舟,它的作用就是将 name 这个变量提供给它的所有子组件。而在 B.vue 中,通过 inject 注入了从 A 组件中提供的 name 变量,那么在组件 B 中,就可以直接通过 this.name 访问这个变量了,它的值也是 浪里行舟。这就是 provide / inject API 最核心的用法。 需要注意的是:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。所以,上面 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的,仍然是 浪里行舟。

parent/parent / children 与 ref

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例 parent/parent / children:访问父 / 子实例 需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。我们先来看个用 ref 来访问组件的例子: // component-a 子组件

export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}

// 父组件

<template>
<component-a ref="comA"></component-a>
</template>

<script>
 export default {
  mounted () {
   const comA = this.$refs.comA;
   console.log(comA.title); // Vue.js
   comA.sayHello(); // 弹窗
  }
 }
</script>

不过,这两种方法的弊端是,无法在跨级或兄弟间通信。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情