组件通信

父传子【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>
相关推荐
前端熊猫3 分钟前
Element Plus 日期时间选择器大于当天时间置灰
前端·javascript·vue.js
Python私教1 小时前
Vue3中的`ref`与`reactive`:定义、区别、适用场景及总结
前端·javascript·vue.js
经常见1 小时前
Vue哲学:让开发者“偷懒”又高效
vue.js
racerun2 小时前
Vue vuex.store mapState
前端·javascript·vue.js
编程百晓君2 小时前
OpenHarmony开发板环境搭建
vue.js
LCG元2 小时前
Vue.js组件开发-实现列表无缝动态滚动
vue.js
GISer_Jing3 小时前
前端经典面试合集(二)——Vue/React/Node/工程化工具/计算机网络
前端·vue.js·react.js·node.js
高兴蛋炒饭11 小时前
RouYi-Vue框架,环境搭建以及使用
前端·javascript·vue.js
ᥬ 小月亮12 小时前
Vue中接入萤石等直播视频(更新中ing)
前端·javascript·vue.js
呜呼~2251412 小时前
前后端数据交互
java·vue.js·spring boot·前端框架·intellij-idea·交互·css3