Skip to content

Vue 组件中的数据传递方式

大致分为两类通信方式:

  • 父子组件之间通信
  • 非父子组件之间通信(兄弟组件、隔代关系组件等)

img

Vue 的父子组件通信

props

这是一个学习过 Vue 的人都会使用的方法,将父组件的数据传递个子组件,在父组件中修改 props 中的数据,那么子组件也会动态的修改。

这个组件的信息传递方向是:父组件 -> 子组件;

Father.vue

HTML
<template>
  <div id="father">
    <p>这里是father组件</p>
    <children
        title="Vue中父子组件之间的通信方式"
        date="2020/04/19">
    </children>
  </div>
</template>

将要传递的数据写在子组件的标签上,并且和对于的名字对应上。

Children

HTML
<script>
export default {
  name: 'Children',
  props: {
    title: {
      type: String,
      default: '',
    },
    date: {
      type: String,
      default: '',
    }
  }
}
</script>

在子组件的 props 中设置相应的名称,用来接收父组件传递的数据。

这里只介绍了简单的使用,更多且更加详细的用法还是进入官网进行查看传送门。

总结: prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。

$emit

这个方法也是在 Vue 中用的很多的一个,提交事件,由子组件提交一个事件,然后父组件进行调用,然后父组件通过这种方法察觉到子组件的变化,进行数据的更新。

这个方法是一个 Vue 上面的实例方法。

JavaScript
vm.$emit(eventName, [...args]);

$parent / $children

都是 Vue 上的一个实例,可以用来访问当前组件的子组件或者父组件,使用 this 可以直接调用。

使用方法

JavaScript
// 访问子组件信息
this.$children[0].messageA = 'this is new value';

// 访问父组件信息
this.$parent.msg;

其他通信方式

provide / inject

父组件中通过 provide 来提供变量,然后在子组件中通过 inject 来注入变量。

不论子组件嵌套有多深,只要调用了 inject 那么就可以注入 provide 中的数据,而不局限于只能从当前父组件的 props 属性中取数据。

使用案例:

假设有三个组件 A、B、C,B 为 A 的子组件,C 为 B 的子组件,形成嵌套关系。

在组件 A 中:

JavaScript
import comB from 'comB.vue';
export default {
    provide: {
        for: "deom"
    },
    components: {
        comB
    }
}

在组件 B 中:

JavaScript
import comC from 'comC.vue';
export default {
    inject: ['for'],
    data() {
        return {
            demo: this.for
        }
    },
    components: {
        comC
    }
}

在组件 C 中

JavaScript
export default {
    inject: ['for'],
    data() {
        return {
            demo: this.for
        }
    }
}

[Vue官方文档 - provide / inject]

ref / refs

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据。

JavaScript
const comA = this.$refs.comA;

eventBus

eventBus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。

eventBus也有不方便之处, 当项目较大,就容易造成难以维护的灾难

初始化

首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它。

JavaScript
// event-bus.jsimport Vue from 'vue'
export const EventBus = new Vue()

发送事件

假设你有两个组件: additionNumshowNum, 这两个组件可以是兄弟组件也可以是父子组件;这里我们以兄弟组件为例:

在 additionNum 中发送一个事件

HTML
// addtionNum.vue 中发送事件

<template>
  <div>
    <button @click="additionHandle">+加法器</button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js'
console.log(EventBus)
export default {
  data(){
    return{
      num:1
    }
  },

  methods:{
    additionHandle(){
      EventBus.$emit('addition', {
        num:this.num++
      })
    }
  }
}
</script>

接收事件

在 showNum 中接收在 additionNum 中发送的事件

HTML
// showNum.vue 中接收事件

<template>
  <div>计算和: {{count}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      count: 0
    }
  },

  mounted() {
    EventBus.$on('addition', param => {
      this.count = this.count + param.num;
    })
  }
}
</script>

这样就实现了在组件addtionNum.vue中点击相加按钮, 在showNum.vue中利用传递来的 num 展示求和的结果。

移除监听者

如果想移除事件的监听, 可以像下面这样操作:

JavaScript
import { eventBus } from 'event-bus.js'
EventBus.$off('addition', {})

Vuex

Vuex 介绍

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 Vuex 解决了多个视图依赖于同一状态来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上。

Vuex 各个模块

  1. state:用于数据的存储,是store中的唯一数据源
  2. getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
  3. mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
  4. actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
  5. modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护

[官方文档-Vuex]

localStorage / sessionStorage

本地存储的方式,将数据存储在本地,在其他地方可以直接获取。

  • window.localStorage.getItem(key)获取数据
  • window.localStorage.setItem(key,value)存储数据

注意用JSON.parse() / JSON.stringify() 做数据格式转换 localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题。

$attrs / $listeners

当两个组件是隔代传递数据时,可以使用 $attrs$listeners

使用案例:

JavaScript
// app.vue
// index.vue

<template>
  <div>
    <child-com1
      :name="name"
      :age="age"
      :gender="gender"
      :height="height"
      title="程序员成长指北"
    ></child-com1>
  </div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
  components: { childCom1 },
  data() {
    return {
      name: "zhang",
      age: "18",
      gender: "女",
      height: "158"
    };
  }
};
</script>
// childCom1.vue

<template class="border">
  <div>
    <p>name: {{ name}}</p>
    <p>childCom1的$attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
  props: {
    name: String // name作为props属性绑定
  },
  created() {
    console.log(this.$attrs);
     // { "age": "18", "gender": "女", "height": "158", "title": "程序员成长指北" }
  }
};
</script>
// childCom2.vue

<template>
  <div class="border">
    <p>age: {{ age}}</p>
    <p>childCom2: {{ $attrs }}</p>
  </div>
</template>
<script>

export default {
  inheritAttrs: false,
  props: {
    age: String
  },
  created() {
    console.log(this.$attrs); 
    // { "gender": "女", "height": "158", "title": "程序员成长指北" }
  }
};
</script>

总结

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

  • 父子组件通信:

    • props
    • $parent / $children
    • provide / inject
    • ref
    • $attrs / $listeners
  • 兄弟组件通信:

    • eventBus
    • Vuex
  • 跨级通信:

    • eventBus
    • Vuex
    • provide /inject
    • $attrs / $listeners

参考文档

稀土掘金-Vue 父子组件通信

稀土掘金-vue 中8种组件通信方式,值得收藏

前端知识体系 · wcrane