vue如何实现父子组件通信、跨级通信、动态组件和异步组件通信
本文主要讲解关于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如果绑定在组件上,拿到的就是 组件对象,可以实现通信功能
父组件
- 步骤:
- 在子组件标签上添加ref属性,值为自定义的
- 父组件中通过【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,就相当于做了以下两个步骤
- 组件上绑定modelValue属性
- 组件上绑定update:modelValue事件
- 在组件上绑定了v-model,就相当于做了以下两个步骤
- 子组件:
- 获取到父组件传递过来的值 modelValue值
- 在修改输入框中的值的时候触发@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如何实现父子组件通信、跨级通信、动态组件和异步组件通信相关的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客,学习愉快哦!