Vue基础(组件通信)

因为每个组件的变量和值都是独立的,组件通信先暂时关注父传子, 子传父。
父: 使用其他组件的vue文件。
子: 被引入的组件(嵌入)。
例如: App.vue(父) MyProduct.vue(子)

一、父传子props

Vue基础(组件通信)
components/MyProduct.vue - 准备标签

<template>
  <div class="my-product">
    <h3>标题: {{ title }}</h3>
    <p>价格: {{ price }}元</p>
    <p>{{ intro }}</p>
  </div>
</template>

<script>
export default {
  props: ['title', 'price', 'intro']
}
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

App.vue中使用并传入数据

<template>
  <div>
    <!-- 
      目标: 父(App.vue) -> 子(MyProduct.vue) 分别传值进入
      需求: 每次组件显示不同的数据信息
      步骤(口诀):
        1. 子组件 - props - 变量 (准备接收)
        2. 父组件 - 传值进去
     -->
    <Product title="炒龙虾" price="50" intro="开业大酬宾, 全场8折"></Product>
    <Product
      title="葡萄干奶香土司"
      price="20"
      intro="开业大酬宾, 全场8折"
    ></Product>
    <Product title="烤红薯" price="15" :intro="str"></Product>
  </div>
</template>

<script>
// 1. 创建组件 (.vue文件)
// 2. 引入组件
import Product from "./components/MyProduct";
export default {
  data() {
    return {
      str: "开业大酬宾, 全场8折",
    };
  },
  // 3. 注册组件
  components: {
    // Product: Product // key和value变量名同名 - 简写
    Product,
  },
};
</script>
<style>
</style>

总结: 组件封装复用的标签和样式, 而具体数据要靠外面传入

二、父向子-配合循环

1、配合循环

目的: 把数据循环分别传入给组件内显示(数据list)

<template>
  <div>
    <MyProduct v-for="item in list" :key="item.id"
    :title="item .proname"
    :price="item .proprice"
    :intro="item .info"
    ></MyProduct>
  </div>
</template>

<script>
// 目标: 循环使用组件-分别传入数据
// 1. 创建组件
// 2. 引入组件
import MyProduct from './components/MyProduct'
export default {
  data() {
    return {
      list: [
        {
          id: 1,
          proname: "炒龙虾",
          proprice: 50,
          info: "开业大酬宾, 全场8折",
        },
        {
          id: 2,
          proname: "葡萄干奶香土司",
          proprice: 20,
          info: "开业大酬宾, 全场8折",
        },
        {
          id: 3,
          proname: "烤红薯",
          proprice: 15,
          info: "开业大酬宾, 全场8折",
        },
      ],
    };
  },
  // 3. 注册组件
  components: {
    // MyProduct: MyProduct
    MyProduct
  }
};
</script>
<style>
</style>

2、单向数据流

在vue中需要遵循单向数据流原则
1)父组件的数据发生了改变,子组件会自动跟着变
2)子组件不能直接修改父组件传递过来的props props是只读的
父组件传给子组件的是一个对象,子组件修改对象的属性,是不会报错的,对象是引用类型, 互相更新
Vue基础(组件通信)
目标: props变量本身是只读不能重新赋值,从父到子的数据流向,叫单向数据流
原因: 子组件修改, 不通知父级, 造成数据不一致性
如果第一个MyProduct.vue内自己修改商品价格为5.5, 但是App.vue里原来还记着18.8 - 数据 不一致了。所以: Vue规定props里的变量, 本身是只读的。

三、子向父—自定义事件

需求:
商品组件, 实现砍价功
Vue基础(组件通信)
子组件中自定义事件方法
子组件内,当用户点击砍价按钮的时候,自定义一个事件,并将数据通过该事件传递出去。

<template>
	<div class='my-product'>
	    <h3>标题: {{ title }}</h3>
	    <p>价格: {{ price }}元</p>
	    <p>{{ intro }}</p>
	    <button @click="subFn">点击砍1元</button>
	</div>
</template>
<script>
export default {
  props: ["index", "title", "price", "intro"],
  methods: {
    subFn() {
      this.$emit("subprice", this.index, 1); // 子向父
    },
  },
};
</script>

2 父组件内, 绑定子组件中的自定义事件和事件处理函数,语法: @自定义事件名=“父methods里函数名”。

<template>
  <div>
    <!-- 目标: 子传父 -->
    <!-- 1. 父组件, @自定义事件名="父methods函数" -->
    <MyProduct
      v-for="(obj, ind) in list"
      :key="obj.id"
      :title="obj.proname"
      :price="obj.proprice"
      :intro="obj.info"
      :index="index"
      @subprice="fn" // 自定义事件名
    ></MyProduct>
  </div>
</template>
import MyProduct from "./components/MyProduct";
export default {
  data() {
    return {
      list: [        {
          id: 1,
          proname: "烤红薯",
          proprice: 15,
          info: "开业大酬宾, 全场8折",
        },
      ],
    };
  },
  components: {
    MyProduct,
  },
  methods: {
  // 自定义事件函数
    fn(index, price) {
      const currentPrice = this.list[index].price;
      currentPrice > 1 && (this.list[index].price = (currentPrice - price).toFixed(2));  //toFixed(2)保留两位小数,
    },
  },
};
</script>

在考虑子组件是为了改变父组件的数据,我们可以使用子传父技术,在子组件内恰当时机this.$emit(“自定义事件名”,值),而且在父组件内,给组件@自定义事件=“父methods函数”。

四、EventBus

目标:常用于跨组件通信时使用
语法

  • src/EventBus/index.js – 创建空白Vue对象并导出
  • 在要传递值的组件(a.vue) eventBus.$emit(‘事件名’, 值)
  • 在要接收值的组件(b.vue) eventBus.$on(‘事件名’, 函数体)
    Vue基础(组件通信)

使用场景:兄弟组件传值

  • 创建一个*时间总线EventBus
  • 兄弟组件通过 e m i t 触 发 自 定 义 事 件 , emit触发自定义事件, emit触发自定义事件,emit第二个参数为传递的数值
  • 另一个兄弟组件通过$on监听自定义事件
// bus.js
// 创建一个*时间总线类
class Bus {
  constructor() {
    this.callbacks = {};   // 存放事件的名字
  }
  $on(name, fn) {
    this.callbacks[name] = this.callbacks[name] || [];
    this.callbacks[name].push(fn);
  }
  $emit(name, args) {
    if (this.callbacks[name]) {
      this.callbacks[name].forEach((cb) => cb(args));
    }
  }
}

// main.js
Vue.prototype.$bus = new Bus() // 将$bus挂载到vue实例的原型上
// 另一种方式
Vue.prototype.$bus = new Vue() // Vue已经实现了Bus的功能

Children1.vue

this.$bus.$emit('foo')

Children2.vue

this.$bus.$on('foo', this.handle)

面试题
1)请说下封装 vue 组件的过程
首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。
分析需求:确定业务需求,把页面中可以复用的结构,样式以及功能,单独抽离成一个组件,实现复用
具体步骤:Vue.component 或者在new Vue配置项components中, 定义组件名, 可以在props中接受给组件传的参数和值,子组件修改好数据后,想把数据传递给父组件。可以采用$emit方法。

2)Vue组件如何进行传值的
父向子 -> props定义变量 -> 父在使用组件用属性给props变量传值
子向父 -> $emit触发父的事件 -> 父在使用组件用@自定义事件名=父的方法 (子把值带出来)

3) Vue 组件 data 为什么必须是函数
每个组件都是 Vue 的实例, 为了独立作用域, 不让变量污染别人的变量

4)讲一下组件的命名规范
给组件命名有两种方式(在Vue.Component/components时),一种是使用链式命名"my-component",一种是使用大驼峰命名"MyComponent",因为要遵循W3C规范中的自定义组件名 (字母全小写且必须包含一个连字符),避免和当前以及未来的 HTML 元素相冲突

上一篇:Vue组件Props中接收数组/对象


下一篇:2022年前端React的100道面试题的第5题:React渲染元素