组件通信

父传子【props】

  • props 遵循着单向绑定 原则
    • 每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,
  • 子组件不能修改父组件传递过来的数据,若是子组件修改了,控制台会抛出警告
js 复制代码
// 父组件
<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就是子组件传递过来的值
js 复制代码
<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>

子组件

js 复制代码
<template>
  <div>
    <h2>菜单 <button @click="showSide">显示 / 隐藏</button></h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShow: true,
    };
  },
  methods: {
    showSide() {
      //子组件通过$emit触发事件,并传值,
      // 父组件通过 @myevent="changeEvent" 监听事件,并接收值
      this.$emit("myevent", this.isShow); // "myevent"是事件名(跟父组件中定义的一致),this.isShow是传的值
    },
  },
};
</script>

<style scoped>
div {display: flex;align-items: center;padding-left: 50px;background: wheat;height: 70px;}
button {margin-left: 20px;}
</style>

效果图

  • 模拟菜单显示隐藏效果

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

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

父组件

  • 步骤:
    1. 在子组件标签上添加ref属性,值为自定义的
    2. 父组件中通过【this.$refs.ref属性名.子组件中的属性名】来获取或修改子组件中的值
      • 【注】this.$refs.ref属性名:获取的是子组件的实例对象(proxy)
js 复制代码
<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>

子组件

js 复制代码
<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 直接访问根组件

父组件

js 复制代码
<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>

子组件

js 复制代码
<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接收

主组件

js 复制代码
<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>

组件 - 头部

js 复制代码
<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>

组件 - 底部导航

js 复制代码
<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="组件名" 渲染对应的组件

父组件

  • 案例是是跨级通信案例的加强版
js 复制代码
<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 方法来实现此功能

简单版

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

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

  • 这里还需要创建两个组件,加载中(LoadingComponent)和加载失败(ErrorComponent)页面
js 复制代码
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方法将值传递给父组件

父组件

js 复制代码
<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>

子组件

js 复制代码
<!-- 
<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>
相关推荐
leoZ2319 分钟前
Claude 驱动的全栈开发:Spring Boot + Vue 的 BS 架构实践
vue.js·spring boot·架构
荒诞英雄4 小时前
从 17MB Vendor 大包到按需加载:Vite 多入口 Vue 组件库首屏优化实践
vue.js·vite
jsonbro4 小时前
手把手带你实现发布订阅模式
前端·vue.js·设计模式
Cobyte6 小时前
23.Vue Vapor 组件 attrs 的实现
前端·javascript·vue.js
斯特凡今天也很帅7 小时前
xml页面可以打开,不报错,但是显示数据的地方也不显示,我是怎么解决的
xml·vue.js
柯克七七8 小时前
给老项目加了 TypeScript,本来只想自己爽,结果全公司代码审查标准被我抬高了
前端·vue.js·typescript
自然 醒10 小时前
前端如何实现在线预览office常见文件功能?
前端·vue.js
肉肉不吃 肉11 小时前
前端调试跨域如何解决
前端·vue.js
卓怡学长11 小时前
w268基于springboot + vue 物流系统
java·数据库·vue.js·spring boot·spring·intellij-idea
迅速的自行车1 天前
从一段模板说起
前端·javascript·vue.js