vue如何实现父子组件通信、跨级通信、动态组件和异步组件通信

前端 潘老师 6个月前 (11-18) 171 ℃ (0) 扫码查看

本文主要讲解关于vue如何实现父子组件通信相关内容,包括父组件传值给子组件,子组件传值给父组件,父组件直接访问子组件、子组件直接访问父组件、跨级通信、动态组件和异步组件之间的通信,每个主题都结合详细的代码案例来深入讲解,更容易理解与掌握。让我们来一起学习下吧!

父组件传子组件【props】

  • props 遵循着单向绑定原则
    • 每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,
  • 子组件不能修改父组件传递过来的数据,若是子组件修改了,控制台会抛出警告
// 父组件
<comp-child mytitle="标题11111" :isShowLogo="true" />
<comp-child mytitle="标题222222" />

// 子组件
<template>
  <header>
    <div v-if="isShowLogo">logo</div>
    <<h2>{{ mytitle }}</h2>
  </header>
</template>

<script>
export default {
  // 子组件接收父组件传递的内容用:props
  // props: ["mytitle", "isShowLogo"],

  // 防止父组件传入的属性类型不对,使用props的type属性
  props: {
    mytitle: {
      type: String,
    },
    isShowLogo: {
      type: Boolean,
      default: false, // 设置默认值
      // required: true, // 必须传值
    },
  },
};
</script>

子组件传父组件【$emit】

  • 父组件通过v-on事件(简写@事件)来监听子组件中的传值
  • 子组件通过$emit触发事件,并传值
  • 例:
    • 父组件:@myevent=”事件处理函数”
    • 子组件:this.$emit(“myevent”,”传递的值”)
    • 【注】子组件中监听的事件名称 == 父组件中v-on绑定的事件

父组件

  • 组件监听子组件,方法不加(),默认会传一个事件对象e
  • 事件对象e就是子组件传递过来的值
<template>
  <!-- 父组件监听子组件,方法不加(),默认会传一个事件对象e -->
  <comp-childMenu @myevent="changeEvent" />
  <ul v-if="isShow">
    <li v-for="data in list" :key="data">
      {{ data }}
    </li>
  </ul>
</template>

<script>
import compChildMenu from "./comp-childMenu.vue";
export default {
  data() {
    return {
      list: ["首页", "用户管理", "商品管理", "订单管理"],
      isShow: true,
    };
  },
  components: {
    compChildMenu,
  },
  methods: {
    changeEvent(e) {
      this.isShow = !this.isShow;
      console.log("父组件", e);
    },
  },
};
</script>

<style>
* {padding: 0px;margin: 0px;}
ul,li {list-style: none;}
ul {width: 180px;border-right: 1px solid #ccc;}
li {line-height: 50px;border-bottom: 1px solid #ccc;text-align: center;}
</style>

子组件代码实现

<template>
  <!-- 父组件监听子组件,方法不加(),默认会传一个事件对象e -->
  <comp-childMenu @myevent="changeEvent" />
  <ul v-if="isShow">
    <li v-for="data in list" :key="data">
      {{ data }}
    </li>
  </ul>
</template>

<script>
import compChildMenu from "./comp-childMenu.vue";
export default {
  data() {
    return {
      list: ["首页", "用户管理", "商品管理", "订单管理"],
      isShow: true,
    };
  },
  components: {
    compChildMenu,
  },
  methods: {
    changeEvent(e) {
      this.isShow = !this.isShow;
      console.log("父组件", e);
    },
  },
};
</script>

<style>
* {padding: 0px;margin: 0px;}
ul,li {list-style: none;}
ul {width: 180px;border-right: 1px solid #ccc;}
li {line-height: 50px;border-bottom: 1px solid #ccc;text-align: center;}
</style>

效果图

  • 模拟菜单显示隐藏效果

父组件直接访问子组件【ref + $refs】

  • ref如果绑定在dom节点上,拿到的就是 原生dom节点
  • ref如果绑定在组件上,拿到的就是 组件对象,可以实现通信功能

父组件

  • 步骤:
    1. 在子组件标签上添加ref属性,值为自定义的
    2. 父组件中通过【this.$refs.ref属性名.子组件中的属性名】来获取或修改子组件中的值
      • 【注】this.$refs.ref属性名:获取的是子组件的实例对象(proxy)
<template>
  <compChild title="用户名" type="text" ref="username" />
  <!-- 自定义ref的属性名 -->
  <compChild title="密码" type="password" ref="password" />
  <button @click="login()">登录</button>
  <button @click="reset()">重置</button>
</template>

<script>
import compChild from "./compChild.vue";
export default {
  components: {
    compChild,
  },
  methods: {
    login() {
      // 通过$refs获取子组件的值
      // mytext是子组件中双向绑定input框的data状态
      console.log(
        `提交给后端的数据:用户名:${this.$refs.username.mytext};密码:${this.$refs.password.mytext}`
      );
    },
    reset() {
        // 通过$refs直接给子组件赋值
      this.$refs.username.mytext = this.$refs.password.mytext = "";
      console.log("清空数据成功!");
    },
  },
};
</script>

<style>
button {padding: 0px 10px;margin: 0px 20px;}
</style>

子组件代码实现

<template>
  <div>
    <label>{{ title }}:</label>
    <input :type="type" v-model="mytext" />
  </div>
</template>
    
<script>
export default {
  props: ["title", "type"],
  data() {
    return {
      mytext: "",
    };
  },
};
</script>

效果图

子组件直接访问父组件【$parent】

  • $parent 子组件直接访问父组件(亲父亲)
  • $root 直接访问根组件

父组件代码实现

<template>
  <compChild />
  <ul v-if="isShow">
    <h2>父组件内列表</h2>
    <li>用户管理</li>
    <li>商品管理</li>
    <li>订单管理</li>
  </ul>
</template>

<script>
import compChild from "./compChild.vue";
export default {
  components: {
    compChild,
  },
  data() {
    return {
      isShow: true,
    };
  },
};
</script>

子组件代码实现

<template>
  <div>
    <h3>compChild -- 子组件</h3>
    <button @click="clickShow()">显示 / 隐藏</button>
  </div>
</template>

<script>
export default {
  methods: {
    clickShow() {
      this.$parent.isShow = !this.$parent.isShow; //直接获取到父组件的属性,并修改
    },
  },
};
</script>

<style scoped>
div {background: wheat;padding: 10px;}
</style>

跨级通信【provide + inject】

  • 主组件:使用provide暴露自己
  • 要用主组件的其他组件:要先使用inject接收

主组件代码实现

<template>
  <div class="box">
    <compHeader></compHeader>
    <compNav></compNav>
  </div>
</template>

<script>
import compHeader from "./compHeader.vue";
import compNav from "./compNav.vue";
export default {
  components: {
    compHeader,
    compNav,
  },
  data() {
    return {
      title: "首页",
    };
  },
  provide() {
    return {
      app: this, // 提供数据,this表示当前组件实例对象
    };
  },
};
</script>

<style>
* {padding: 0px;margin: 0px;}
ul,li {list-style: none;}
</style>

组件 – 头部代码实现

<template>
  <!-- 显示数据 -->
  <h2>{{ app.title }}</h2>
</template>

<script>
export default {
  inject: ["app"], // 接收父组件传递过来的数据
};
</script>

<style>
h2 {text-align: center;line-height: 80px;  border-bottom: 1px solid #ccc;}
</style>

组件 – 底部导航代码实现

<template>
  <div>
    <ul>
      <li v-for="item in nav" :key="item" @click="clickTitle(item)">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  inject: ["app"], // 接收父组件传过来的数据
  data() {
    return {
      nav: ["首页", "影院", "资讯", "我的"],
    };
  },
  methods: {
    clickTitle(title) {
      this.app.title = title; // 修改父组件的数据
    },
  },
};
</script>

<style>
ul {display: flex;justify-content: space-around;background: wheat;position: fixed;width: 100%;bottom: -1px;}
li {line-height: 50px;padding: 0px 20px;}
</style>

效果图

  • 单页面切换App

动态组件【component + is】

  • component 动态组件
  • :is=”组件名” 渲染对应的组件

父组件代码实现

  • 案例是是跨级通信案例的加强版
<template>
  <div class="box">
    <!-- 头部 -->
    <compHeader></compHeader>
    <!-- 内容区 -->
    <component :is="obj[title]"></component>
    <!-- 底部导航 -->
    <compNav></compNav>
  </div>
</template>

<script>
import compHeader from "./compHeader.vue";
import compNav from "./compNav.vue";
import Home from "./components/Home.vue";
import Cinema from "./components/Cinema.vue";
import Info from "./components/Info.vue";
import Mine from "./components/Mine.vue";
export default {
  components: {
    compHeader,  // 头部
    compNav,  // 底部导航
    Home,  // 首页
    Cinema,  // 影院
    Info,  // 资讯
    Mine,  // 我的
  },
  data() {
    return {
      title: "首页",
      obj: {
        首页: "Home",
        影院: "Cinema",
        资讯: "Info",
        我的: "Mine",
      },
    };
  },
  provide() {
    return {
      app: this, // 提供数据,this表示当前组件实例对象
    };
  },
};
</script>

 

异步组件 【defineAsyncComponent】

  • 在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。
  • Vue 提供了 defineAsyncComponent 方法来实现此功能

简单版代码实现

// 导入异步组件工具
import { defineAsyncComponent } from "vue";
// 异步导入组件
AsyncComp: defineAsyncComponent(() => import('./Foo.vue')), 

加载与错误提示(加强版)

  • 这里还需要创建两个组件,加载中(LoadingComponent)和加载失败(ErrorComponent)页面
const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),
  
  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})

v-model使用在组件上

  • 父组件:
    • 在组件上绑定了v-model,就相当于做了以下两个步骤
      1. 组件上绑定modelValue属性
      2. 组件上绑定update:modelValue事件
  • 子组件:
    1. 获取到父组件传递过来的值 modelValue值
    2. 在修改输入框中的值的时候触发@input事件,然后通过$emit方法将值传递给父组件

父组件代码实现

<template>
  <!-- <Child type="text" v-model="username" /> -->

  <!-- 原理 -->
  <Child :modelValue="username" @update:modelValue="(e) => (username = e)" />
</template>

<script>
import Child from "./Child.vue";
export default {
  components: {
    Child,
  },
  data() {
    return {
      username: "",
    };
  },
};
</script>

子组件代码实现

<!-- 
<template>
  <input :type="type" @input="ChangeInput" :value="modelValue" />
</template>

<script>
export default {
  props: ["type", "modelValue"],
  data() {
    return {};
  },
  methods: {
    ChangeInput(e) {
      console.log(e.target.value);
      this.$emit("update:modelValue", e.target.value);
    },
  },
};
</script> 
-->

<!-- 原理 -->
<!-- 
子组件:
  获取到父组件传递过来的值 modelValue值
  在修改输入框中的值的时候触发@input事件,然后通过emit方法将值传递给父组件
 -->
<template>
  <input :type="type" @input="ChangeInput" :value="modelValue" />
</template>

<script>
export default {
  props: ["type", "modelValue"],
  methods: {
    ChangeInput(e) {
      console.log(e.target.value);
      this.$emit("update:modelValue", e.target.value);
    },
  },
};
</script>

以上就是关于vue如何实现父子组件通信、跨级通信、动态组件和异步组件通信相关的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客,学习愉快哦!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/11375.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】