Vue

Vue2父子组件通信

2023-08-11  本文已影响0人  h2coder

父组件和子组件通信

使用props传参

父向子传参

子组件(Son.vue)

<template>
  <div class="son" style="border: 3px solid #000; margin: 10px">
    <!-- 子组件,使用父组件传过来的数据 -->
    我是Son组件 {{ title }}
  </div>
</template>

<script>
export default {
  // 子组件,声明要接收的属性
  props: ["title"],
};
</script>

<style>
</style>

父组件(App.vue)

<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是APP组件
    <!-- 使用子组件,并通过属性,传递data中的 myTitle 数据给子组件 -->
    <HmSon :title="myTitle"></HmSon>
  </div>
</template>

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

export default {
  name: "App",
  data() {
    return {
      // 父组件的数据
      myTitle: "学前端",
    };
  },
  // 局部注册子组件
  components: {
    HmSon,
  },
};
</script>

<style>
</style>

子向父传参

父组件(App.vue)

<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是APP组件,选中: {{ selectText }}
    <!-- 父组件,监听子组件的自定义事件 -->
    <HmSon :title="myTitle1" @changeHero="onChangeHero"></HmSon>
    <HmSon :title="myTitle2" @changeHero="onChangeHero"></HmSon>
    <HmSon :title="myTitle3" @changeHero="onChangeHero"></HmSon>
  </div>
</template>

<script>
import HmSon from "./components/HmSon.vue";

export default {
  name: "App",
  data() {
    return {
      myTitle1: "1号选手,刘xx",
      myTitle2: "2号选手,罗xx",
      myTitle3: "3号选手,万xx",
      // 选中的选手名称
      selectText: "",
    };
  },
  methods: {
    // 处理子组件的事件
    onChangeHero(msg) {
      this.selectText = msg;
    },
  },
  components: {
    HmSon,
  },
};
</script>

<style>
</style>

子组件(Son.vue)

<template>
  <div class="son" style="border: 3px solid #000; margin: 10px">
    {{ title }}
    <!-- 子组件,点击按钮时,发送自定义事件给父组件 -->
    <button @click="changeHero">选我</button>
  </div>
</template>

<script>
export default {
  props: ["title"],
  methods: {
    changeHero() {
      // 子组件,发送自定义事件给父组件
      // 语法:this.$emit('事件名称', '参数')
      this.$emit("changeHero", this.title);
    },
  },
};
</script>

<style>
</style>

使用props传递多个值

父组件(App.vue)

<template>
  <div class="app">
    <UserInfo
      :username="username"
      :age="age"
      :isSingle="isSingle"
      :car="car"
      :hobby="hobby"
    ></UserInfo>
  </div>
</template>

<script>
import UserInfo from './components/UserInfo.vue'
export default {
  data() {
    return {
      username: '小帅',
      age: 28,
      isSingle: true,
      car: {
        brand: '宝马',
      },
      hobby: ['篮球', '足球', '羽毛球'],
    }
  },
  components: {
    UserInfo,
  },
}
</script>

<style>
</style>

子组件(UserInfo.vue)

<template>
  <div class="userinfo">
    <h3>我是个人信息组件</h3>
    <div>姓名:{{ username }}</div>
    <div>年龄:{{ age }}</div>
    <div>是否单身:{{ isSingle }}</div>
    <div>座驾:{{ car.brand }}</div>
    <div>兴趣爱好:{{ hobby.join("、") }}</div>
  </div>
</template>

<script>
export default {
  props: ["username", "age", "isSingle", "car", "hobby"],
};
</script>

<style>
.userinfo {
  width: 300px;
  border: 3px solid #000;
  padding: 20px;
}
.userinfo > div {
  margin: 20px 10px;
}
</style>

给props参数设置类型

父组件(App.vue)

<template>
  <div class="app">
    <!-- 不传参,使用默认值 -->
    <BaseProgress></BaseProgress>
    <!-- 传参 -->
    <BaseProgress :w="width"></BaseProgress>
  </div>
</template>

<script>
import BaseProgress from "./components/BaseProgress.vue";
export default {
  data() {
    return {
      // 正确
      width: 80,
      // 传错为字符串类型
      // width: "60",
    };
  },
  components: {
    BaseProgress,
  },
};
</script>

<style>
</style>

子组件(BaseProgress.vue)

<template>
  <div class="base-progress">
    <div class="inner" :style="{ width: w + '%' }">
      <span>{{ w }}%</span>
    </div>
  </div>
</template>

<script>
export default {
  // 写法一,不校验类型,只声明变量名
  // props: ["w"],
  // 写法二:属性:类型。作用:限定参数的数据类型,参数类型不一致,则报错
  // props: {
  //   w: Number,
  // },
  // 写法三,更新完整,能设置参数类型、是否必传、默认值、参数校验函数
  props: {
    w: {
      // 参数类型
      type: Number,
      // 参数是否必传
      // required: true,
      // 默认值
      default: 60,
      // 校验函数,value参数,就是父组件传递过来的参数,返回true,则校验通过,否则不通过
      validator(value) {
        return value > 50;
      },
    },
  },
};
</script>

<style scoped>
.base-progress {
  height: 26px;
  width: 400px;
  border-radius: 15px;
  background-color: #272425;
  border: 3px solid #272425;
  box-sizing: border-box;
  margin-bottom: 30px;
}
.inner {
  position: relative;
  background: #379bff;
  border-radius: 15px;
  height: 25px;
  box-sizing: border-box;
  left: -3px;
  top: -2px;
}
.inner span {
  position: absolute;
  right: 0;
  top: 26px;
}
</style>

使用EventBus

EventBus.js

// 导入Vue
import Vue from 'vue';

// 创建一个Vue实例,作为事件总线
const Bus = new Vue();

// 默认导出
export default Bus;

父组件(App.vue)

<template>
  <div class="app">
    <BaseA></BaseA>
    <BaseB></BaseB>
    <BaseC></BaseC>
  </div>
</template>

<script>
import BaseA from './components/BaseA.vue'
import BaseB from './components/BaseB.vue'
import BaseC from './components/BaseC.vue'
export default {
  components:{
    BaseA,
    BaseB,
    BaseC
  }
}
</script>

<style>

</style>

组件B(BaseB.vue)

<template>
  <div class="base-b">
    <div>我是B组件(发布方)</div>
    <button @click="fn">发送消息</button>
  </div>
</template>

<script>
// 导入EventBus
import Bus from "../utils/EventBus";

export default {
  methods: {
    // 发送自定义事件
    fn() {
      console.log('组件B,发送事件!');
      Bus.$emit("sendMsg", "我是来自于B组件的数据");
    },
  },
};
</script>

<style scoped>
.base-b {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>

组件A和组件C

<template>
  <div class="base-a">
    我是A组件(接收方)
    <p>{{ msg }}</p>
  </div>
</template>

<script>
import Bus from "../utils/EventBus";
export default {
  data() {
    return {
      msg: "",
    };
  },
  created() {
    // 监听事件,更新组件变量
    Bus.$on("sendMsg", (msg) => {
      console.log(`组件A,接收到事件,数据:${msg}`);
      this.msg = msg;
    });
  },
};
</script>

<style scoped>
.base-a {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>
<template>
  <div class="base-c">
    我是C组件(接收方)
    <p>{{ msg }}</p>
  </div>
</template>

<script>
import Bus from "../utils/EventBus";
export default {
  data() {
    return {
      msg: "",
    };
  },
  created() {
    // 监听事件,更新组件变量
    Bus.$on("sendMsg", (msg) => {
      console.log(`组件C,接收到事件,数据:${msg}`);
      this.msg = msg;
    });
  },
};
</script>

<style scoped>
.base-c {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>

provide和inject实现父子通信

父组件(App.vue)

<template>
  <div class="app">
    我是APP组件 {{ color }} --- {{ userInfo.uName }}
    <button @click="changeData">修改数据</button>
    <SonA></SonA>
    <SonB></SonB>
  </div>
</template>

<script>
import SonA from "./components/SonA.vue";
import SonB from "./components/SonB.vue";
export default {
  data() {
    return {
      // 注意:如果提供的时普通类型,是非响应式数据(也就是数据改变时,子孙组件不能能收到更新)
      // 如果提供的是复杂类型,则是响应式数据(则是改变后,子孙的数据,可以跟着祖先改变)
      color: "red",
      userInfo: {
        uName: "八戒",
      },
    };
  },
  // 祖孙组件提供数据,provide和inject是配套使用的
  // 这种数据共享方式,要求组件的关系,必须是祖先和子孙关系,不能是纯兄弟关系(否则不生效)
  provide() {
    return {
      color: this.color,
      userInfo: this.userInfo,
    };
  },
  methods: {
    // 点击事件,更新数据
    changeData() {
      this.color = "blue";
      this.userInfo.uName = "悟空";
    },
  },
  components: {
    SonA,
    SonB,
  },
};
</script>

<style>
.app {
  border: 3px solid #000;
  border-radius: 6px;
  margin: 10px;
}
</style>

子组件(SonA.vue、SonB.vue)

父组件包裹2个子组件,SonA和SonB,其中SonA又包裹了一个孙组件GrandSon,SonB没有孙组件

<template>
  <div class="SonA">我是SonA组件
    <GrandSon></GrandSon>
  </div>
</template>

<script>
import GrandSon from '../components/GrandSon.vue'
export default {
  components:{
    GrandSon
  }
}
</script>

<style>
.SonA {
  border: 3px solid #000;
  border-radius: 6px;
  margin: 10px;
  height: 200px;
}
</style>
<template>
  <div class="SonB">我是SonB组件 {{ color }} --- {{ userInfo.uName }}</div>
</template>

<script>
export default {
  // 孙子组件,声明获取祖先组件的数据
  inject: ["color", "userInfo"],
};
</script>

<style>
.SonB {
  border: 3px solid #000;
  border-radius: 6px;
  margin: 10px;
  height: 200px;
}
</style>

孙组件(GrandSon.vue)

<template>
  <div class="grandSon">我是GrandSon {{ color }} --- {{ userInfo.uName }}</div>
</template>

<script>
export default {
  // 孙子组件,声明获取祖先组件的数据
  inject: ["color", "userInfo"],
};
</script>

<style>
.grandSon {
  border: 3px solid #000;
  border-radius: 6px;
  margin: 10px;
  height: 100px;
}
</style>

通过ref

父组件(App.vue)

<template>
  <div class="app">
    <button @click="fn">清空 重置</button>
    <!-- 通过ref属性,绑定子组件 -->
    <BaseForm ref="baseForm"></BaseForm>
  </div>
</template>

<script>
import BaseForm from "./components/BaseForm.vue";
export default {
  methods: {
    fn() {
      // console.log(this.$refs.baseForm);

      // 父组件中,调用子组件的方法
      this.$refs.baseForm.reset();
      // 也能操作子组件的数据(这也是一种父组件向子组件传参的方式,但比较少用)
      this.$refs.baseForm.username = "wally";
    },
  },
  components: {
    BaseForm,
  },
};
</script>

<style>
</style>

子组件(BaseForm.vue)

<template>
  <form autocomplete="off">
    <div>
      <label for="">用户名<input type="text" v-model="username" /></label>
      <label for="">密码<input type="text" v-model="password" /></label>
      <button @click="reset">重置</button>
    </div>
  </form>
</template>

<script>
export default {
  data() {
    return {
      username: "",
      password: "",
    };
  },
  methods: {
    reset() {
      this.username = "";
      this.password = "";
    },
  },
};
</script>

<style>
</style>
上一篇 下一篇

猜你喜欢

热点阅读