因为每个组件的变量和值都是独立的,组件通信先暂时关注父传子, 子传父。
父: 使用其他组件的vue文件。
子: 被引入的组件(嵌入)。
例如: App.vue(父) MyProduct.vue(子)
一、父传子props
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是只读的
父组件传给子组件的是一个对象,子组件修改对象的属性,是不会报错的,对象是引用类型, 互相更新
目标: props变量本身是只读不能重新赋值,从父到子的数据流向,叫单向数据流。
原因: 子组件修改, 不通知父级, 造成数据不一致性
如果第一个MyProduct.vue内自己修改商品价格为5.5, 但是App.vue里原来还记着18.8 - 数据 不一致了。所以: Vue规定props里的变量, 本身是只读的。
三、子向父—自定义事件
需求:
商品组件, 实现砍价功
子组件中自定义事件方法
子组件内,当用户点击砍价按钮的时候,自定义一个事件,并将数据通过该事件传递出去。
<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(‘事件名’, 函数体)
使用场景:兄弟组件传值
- 创建一个*时间总线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 元素相冲突