Bootstrap

Vue2组件通信

前言

最近复习了vue相关知识点,今天分享下组件通信。
组件在嵌套的过程中,经常会遇到互相传递数据的情况。
我们可以把组件之间的数据传递分为三种情况:

  • 父级向子级传递数据
  • 子级向父级传递数据
  • 非父子级传递数据

1.父传子(props属性)

   props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
   props基于对象的写法有4个属性(具体可以参考下Vue官方文档,附上一张属性解释图):
   type类型    required必填项    default默认值   validator自定义验证函数

在这里插入图片描述

子组件设置props属性,接收从父组件传递过来的参数。
需求:父组件是App.vue,子组件是Child.vue。想将父组件里面的一些数据传递到子组件中。
- 在父组件中引入、注册、使用子组件,使用时绑定要传递的属性;
- 在子组件中使用props接收父组件传递过来的数据;
- 子组件的模板中使用props定义的属性。

父组件App.vue的代码如下:

<template>
  <div id="app">
    <h2>Hi,我是父组件</h2>
    <div class="content">
      <!-- 使用子组件 -->
      <!-- 1.不传值 -->
      <Child></Child>
      <!-- 2.父组件传递字符串到子组件不需要使用v-bind来绑定(直接传递写死的值不需要动态绑定) -->
      <Child cmsg="这是子组件喔~"></Child>
      <!-- 3.传递动态数据需要利用v-bind动态绑定 -->
      <Child :ctitle="title" cmsg="这是子组件喔~" :cnum="num" :clist="list">
      </Child>
    </div> 
    </div>
  </div>
</template>

<script>
// 引入子组件
import Child from "./components/Child.vue";

export default {
  name: "App",
  // 注册组件
  components: {
    Child,
  },
  data() {
    return {
      title: "父组件传递的标题",
      list: [],
      num: 88,
    };
  },
  methods: {},
};
</script>

子组件Child.vue用props进行接收从父组件传递过来的值,代码如下:

<template>
  <div>
    <h5>hello,我是子组件</h5>
    <div>
      标题:<b>{{ ctitle }}</b>
    </div>
    <div>
      介绍:<b>{{ cmsg }}</b>
    </div>
    <div>
      数字:<b>{{ cnum }}</b>
    </div>
    <div>
      列表内容:<b>{{ clist }}</b>
    </div>
  </div>
</template>

<script>
export default {
  name: "Child",
  data() {
    return {};
  },
  // 子组件设置props属性,接收从父组件传递过来的参数
  // props选项可以写成数组形式,也可以写成对象形式。
  // props选项写成对象,有很多好处,比如:类型限制、默认值、必传值等。
  props: {
    ctitle: {
      type: String,
      //默认值只有在不传值的时候起作用
      default: "默认标题",
    },
    cmsg: {
      type: String,
      default: "默认介绍",
    },
    cnum: {
      type: Number,
      default: 0,
    },
    clist: {
      type: Array,
      //注意:数组和对象的default默认值必须是一个函数,并且都需要有return
      default() {
        return [1, 2, 3, 4];
      },
    },
  },
  methods: {},
};
</script>

<style>
b {
  color: #6db6fa;
}
</style>

注意:数组和对象的default默认值必须是一个函数,并且都需要有return。

场景1:父组件不传递值,子组件就会显示props中定义的默认的内容。
在这里插入图片描述

效果如下:
在这里插入图片描述

场景2:父组件传递字符串到子组件不需要使用v-bind来绑定(直接传递写死的值不需要动态绑定):
在这里插入图片描述

效果如下:
在这里插入图片描述

场景3:父组件需要利用v-bind动态绑定子组件的参数:
在这里插入图片描述

效果如下:
在这里插入图片描述

2.子传父($emit)

在子组件中,通过$emit发射自定义事件。
在父组件中,通过v-on来监听子组件的自定义事件。当监听到这个事件,在父组件的methods中进行处理就可以了。
例如:现在想写一个功能,点击子级的button按钮时,将子级的数据传递给父级,然后让父级的数据变成子级的数据。
子组件Child.vue代码如下:

<template>
  <div>
    <h2>hello,我是子组件</h2>
    <button @click="sendData">传递数据</button>
  </div>
</template>

<script>
export default {
  name: "Child",
  data() {
    return {
      // 将childData传递给父组件
      childData: " I am Child-data",
    };
  },
  methods: {
    sendData() {
      // 通过$emit()来触发/发射一个自定义事件item-click
      this.$emit("item-click", this.childData);
    },
  },
};
</script>

父组件App.vue的代码如下:

<template>
  <div id="app">
    <h2>Hi,我是父组件</h2>
    <h3>{{ childData }}</h3>
    <!-- 子父组件中,通过v-on来监听子组件发射的自定义事件;
        当监听到这个事件,在父组件的methods中进行处理就可以了 。
    -->
    <hr />
    <child @item-click="changeData"></child>
  </div>
</template>

<script>
// 引入子组件
import Child from "./components/Child.vue";

export default {
  name: "App",
  // 注册组件
  components: {
    Child,
  },
  data() {
    return {
      childData: "I am Father-data",
    };
  },
  methods: {
    // 定义changeData方法
    changeData(childData) {
      this.childData = childData;
    },
  },
};
</script>

过程分析:

  1. 子组件如果想给父组件传递数据,需要发射emit一个事件,并且这个事件是需要自定义的。
  2. 之后,需要在父组件中使用子组件标签时,在这里监听这个自定义事件。
  3. 当监听到这个事件,去父组件的methods中处理这个事件就可以了。
    在这里插入图片描述
    运行项目,进入页面查看,可以看到子组件里面的数据成功传递给了父组件。
    在这里插入图片描述

3.非父子级传递数据(vuex的store)


在项目中的Compnents文件夹中新建两个文件User.vue和About.vue,它们不是父子组件的关系;将它们在App.vue中引入、注册并使用。

需求:在About组件中点击“修改数据”按钮,User组件的数据也会改变。

  1. 在项目中安装vuex:npm i vuex@3(这里是Vue2项目,需要安装指定的vuex的3版本);
  2. 然后,在项目新建一个文件夹store,在里面创建一个index.js文件,并在index.js文件中引入插件vuex,安装插件,创建vuex里面的store对象,导出store共享;在state中定义两个状态:counter与message。
    在这里插入图片描述
  3. 接着,回到main.js中挂载;一旦在main.js中挂载store之后,那么,所有的vue组件都有一个$store对象了。
import Vue from "vue";
import App from "./App.vue";
// 引入store
import store from "./store";

Vue.config.productionTip = false;

new Vue({
  store,
  render: (h) => h(App),
}).$mount("#app");

这么做,项目里面的组件就都能拿到counter和message了,直接通过 $store.state.counter和 $store.state.message来取出来直接展示index.js中的state值,如下图所示:
在这里插入图片描述
至此,About与User实现了数据的共享了。

  1. 接下来,就是实现数据传递了。那么,如何实现呢?在index.js中的mutations中定义方法EditStateMessage;
import Vue from "vue"; 
import Vuex from "vuex"; 

Vue.use(Vuex);

// 创建store对象。
const store = new Vuex.Store({
  // state就是保存状态
  state: {
    counter: 912,
    message: "hello,vuex",
  },
  // 只要是改store里面的state,一定是通过mutation的。
  // mutations中默认参数为:state。
  mutations: {
    EditStateMessage(state, str) {
      state.message = str;
    },
  },
});

// 导出store对象
export default store;

在About.vue中写一个button来修改数据,并绑定点击事件changeData ,在methods中定义changeData,调用store中的EditStateMessage,后面写想要设置的值即可,这里点击即可改变数据为”hi,我是修改好的数据”。

<template>
  <div>
    <h2>我是About组件</h2>
    <h3>{{ $store.state.counter }}</h3>
    <button @click="changeData" style="background: red">
      修改数据
    </button>
    <h3>{{ $store.state.message }}</h3>
  </div>
</template>

<script>
export default {
  name: "About",
  data() {
    return {};
  },
  methods: {
    changeData() {
      this.$store.commit("EditStateMessage", "hi,我是修改好的数据");
    },
  },
};
</script>

在这里插入图片描述
这样,就实现了非父子级关系的数据传递。
运行项目,点击“修改数据”前的页面效果如下图所示:

在这里插入图片描述
点击About组件的“修改数据”按钮,可以发现不仅About组建的数据会被修改,User组件也会改变数据。
在这里插入图片描述

4.父组件主动获取子组件的数据/方法

可以利用ref
调用子组件的时候定义了ref=”child”(ref的命名是随意的),那么父组件想主动获取子组件的数据/方法,就利用this.$refs.child.xxx即可。
在这里插入图片描述
效果如下:
在这里插入图片描述

总结:如果给子组件上绑定一个ref=”child”,那么在父组件中就可以通过this.$refs.child.xxx获取子组件的数据和方法了。

获取子组件的数据:如果子组件的data上有个cmessage属性,那么在父组件中,可以通过this.$refs.child.cmessage来拿到子组件中的cmessage数据;

获取子组件的方法:如果子组件的methods上有个getChild()方法,那么在父组件中,可以通过this.$refs.child.getChild()来拿到子组件中的方法。

总结

以上就是今天要分享的Vue2组件通信的相关内容,欢迎大家给出意见哦~

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;