在原生的JavaScript中,当我们需要使用重复使用标签的时候,大多数情况下我们会使用复制粘贴的方法。
在vue中我们使用组件的方式,可以有效的解决代码片段的重复使用。
1、组件的引入
【实现多个折叠面板】:不使用组件:
<!--
* @Author: your name
* @Date: 2022-03-12 08:50:07
* @LastEditTime: 2022-03-12 15:44:24
* @LastEditors: Please set LastEditors
* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
* @FilePath: \componentstest\src\App.vue
-->
<template>
<div id="app">
<h3>案例:折叠面板</h3>
<div>
<div class="title">
<h4>芙蓉楼送辛渐</h4>
<!-- 1.绑定点击事件 -->
<span class="btn" @click="btn">
<!-- 4. 根据isShow的值显示不同文字 -->
{{ isShow ? '收起' : '展开'}}
</span>
</div>
<!-- 2. v-show配合变量来控制标签隐藏出现 -->
<div class="container" v-show="isShow">
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</div>
</div>
<div>
<div class="title">
<h4>芙蓉楼送辛渐</h4>
<!-- 1.绑定点击事件 -->
<span class="btn" @click="btn">
<!-- 4. 根据isShow的值显示不同文字 -->
{{ isShow ? '收起' : '展开'}}
</span>
</div>
<!-- 2. v-show配合变量来控制标签隐藏出现 -->
<div class="container" v-show="isShow1">
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</div>
</div>
<div>
<div class="title">
<h4>芙蓉楼送辛渐</h4>
<!-- 1.绑定点击事件 -->
<span class="btn" @click="btn">
<!-- 4. 根据isShow的值显示不同文字 -->
{{ isShow ? '收起' : '展开'}}
</span>
</div>
<!-- 2. v-show配合变量来控制标签隐藏出现 -->
<div class="container" v-show="isShow2">
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isShow: true,
isShow1:true,
isShow2:true
}
},
methods: {
btn(){
// 3. 点击时, 把值改成false
this.isShow = !this.isShow
}
}
}
</script>
<style lang="less">
body {
background-color: #ccc;
#app {
width: 400px;
margin: 20px auto;
background-color: #fff;
border: 4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
padding: 1em 2em 2em;
h3 {
text-align: center;
}
.title {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #ccc;
padding: 0 1em;
}
.title h4 {
line-height: 2;
margin: 0;
}
.container {
border: 1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
}
}
</style>
【使用组件】:
- Panel.vue
<template>
<div>
<div>
<div class="title">
<h4>芙蓉楼送辛渐</h4>
<!-- 1.绑定点击事件 -->
<span class="btn" @click="btn">
<!-- 4. 根据isShow的值显示不同文字 -->
{{ isShow ? "收起" : "展开" }}
</span>
</div>
<!-- 2. v-show配合变量来控制标签隐藏出现 -->
<div class="container" v-show="isShow">
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</div>
</div>
</div>
</template>
<script>
export default {
// 数据对象
data() {
return {
isShow: true,
};
},
// 函数
methods: {
btn() {
// 3. 点击时, 把值改成false
this.isShow = !this.isShow;
},
},
};
</script>
<style scoped lang="less">
.title {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #ccc;
padding: 0 1em;
}
.title h4 {
line-height: 2;
margin: 0;
}
.container {
border: 1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
</style>
- App.vue
<template>
<div id="app">
<h3>案例:折叠面板</h3>
<Panel name=""></Panel>
<Panel name=""></Panel>
<Panel name=""></Panel>
</div>
</template>
<script>
// 导入组件
import Panel from "@/components/Panel";
export default {
components: {
Panel: Panel,
},
};
</script>
<style lang="less">
body {
background-color: #ccc;
#app {
width: 400px;
margin: 20px auto;
background-color: #fff;
border: 4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
padding: 1em 2em 2em;
h3 {
text-align: center;
}
}
}
</style>
2、组件的基本概念
组件是可复用的Vue实例,封装了标签、样式和JavaScript代码
组件化思想:封装的思想就是将页面中重复使用的部分进行封装为组件
一个页面可以拆分为多个组件进行拼凑
1、组件化图解
2、使用场景
- 需要重复使用某些标签的时候
- 需要组件化开发的时候
3、组件的好处
- 提高了代码的复用性
- 各自独立,互不影响
3、组件的基本使用
1、创建组件
2、注册组件
1、全局注册
【语法】:
在main.js中进行如下操作
import 组件对象 from 组件路径
Vue.component("组件名",组件对象)
【演示】:
- 创建组件
<template>
<div>
<div>
<div class="title">
<h4>芙蓉楼送辛渐</h4>
<!-- 1.绑定点击事件 -->
<span class="btn" @click="btn">
<!-- 4. 根据isShow的值显示不同文字 -->
{{ isShow ? "收起" : "展开" }}
</span>
</div>
<!-- 2. v-show配合变量来控制标签隐藏出现 -->
<div class="container" v-show="isShow">
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</div>
</div>
</div>
</template>
<script>
export default {
// 数据对象
data() {
return {
isShow: true,
};
},
// 函数
methods: {
btn() {
// 3. 点击时, 把值改成false
this.isShow = !this.isShow;
},
},
};
</script>
<style scoped lang="less">
.title {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #ccc;
padding: 0 1em;
}
.title h4 {
line-height: 2;
margin: 0;
}
.container {
border: 1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
</style>
- 全局引入组件
import Vue from 'vue'
import App from './App.vue'
// 引入组件
import Pannel from '@/components/Pannel'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
- 全局注册组件
import Vue from 'vue'
import App from './App.vue'
// 引入组件
import Pannel from '@/components/Pannel'
Vue.config.productionTip = false
// 注册组件
Vue.component("Pannel",Pannel)
new Vue({
render: h => h(App),
}).$mount('#app')
- 使用组件
<template>
<div id="app">
<!-- 使用全局注册的组件 -->
<Pannel></Pannel>
</div>
</template>
<script>
import Pannel from './components/Pannel.vue'
export default {
components: { Pannel },
}
</script>
<style lang="less">
body {
background-color: #ccc;
#app {
width: 400px;
margin: 20px auto;
background-color: #fff;
border: 4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
padding: 1em 2em 2em;
h3 {
text-align: center;
}
}
}
</style>
2、局部注册
具体可以参考第一部分,组件的引入
总结:组件的基本使用步骤
1、创建组件
2、全局或局部引入组件
3、全局或局部注册组件
4、使用组件
4、组件中的- scoped作用
保证样式只对当前的页面生效
准备: 当前组件内标签都被添加 data-v-hash值 的属性
获取: css选择器都被添加 [data-v-hash值] 的属性选择器
5、组件通信
组件之间是相互独立的,互不影响的,但是当我们需要在组件之间传递数据的时候该如何实现呢?
在vue中提供了组件通信的方法来进行组件之间的相互传值
1、父组件向子组件传值
【步骤】:
- 在子组件中定义接收父组件传递的数据的变量
export default {
props: ["title", "price", "intro"],
};
- 父组件开始传值
<template>
<div>
<!-- 父组件向子组件传值
步骤:在子组件内定义接收的变量
父组件向子组件传值
动态属性用来绑定一个变量
-->
<MyProduct title="好吃的口水鸡" price="50" intro="5"></MyProduct>
<MyProduct title="好吃的北京烤鸭" price="500" intro="5"></MyProduct>
<MyProduct title="好吃的馒头" price="10" :intro="number"></MyProduct>
</div>
</template>
<script>
import MyProduct from "@/components/MyProduct";
export default {
data() {
return {
number: 0,
};
},
// 组件
components: {
MyProduct,
},
};
</script>
<style>
</style>
2、父组件向子组件传值结合循环使用
<template>
<div>
<MyProduct
v-for="item in list"
:key="item.id"
:title="item.proname"
:price="item.proprice"
:intro="item.info"
></MyProduct>
</div>
</template>
<script>
// 导入组件
import MyProduct from "@/components/MyProduct";
export default {
// 数据
data() {
return {
list: [
{
id: 1,
proname: "超级好吃的棒棒糖",
proprice: 18.8,
info: "开业大酬宾, 全场8折",
},
{
id: 2,
proname: "超级好吃的大鸡腿",
proprice: 34.2,
info: "好吃不腻, 快来买啊",
},
{
id: 3,
proname: "超级无敌的冰激凌",
proprice: 14.2,
info: "炎热的夏天, 来个冰激凌了",
},
],
};
},
// 组件
components: {
MyProduct,
},
};
</script>
<style>
</style>
3、组件的单向数据流
从父组件到子组件的数据流向就叫做单项数据流
- 组件
<template>
<div>
<div class="my-product">
<h1>标题:{{ title }}</h1>
<p>价格:{{ price }}¥</p>
<p>折扣:{{ intro }}</p>
<button @click="reduce">砍一刀</button>
</div>
</div>
</template>
<script>
export default {
props: ["title", "price", "intro"],
methods: {
reduce() {
//子组件修改数据值,不会通知父组件,会导致数据不一致
//vue本身不允许props重新赋值
this.price=this.price-1;
},
},
};
</script>
<style lang="less" scoped>
.my-product {
width: 500px;
height: 200px;
margin: 10px auto;
border: 2px solid #000;
border-radius: 10px;
h1 {
text-align: center;
}
p {
margin-left: 120px;
}
}
</style>
- 使用
<template>
<div>
<MyProductReduce
v-for="item in list"
:key="item.id"
:title="item.proname"
:price="item.proprice"
:intro="item.info"
></MyProductReduce>
</div>
</template>
<script>
// 导入组件
import MyProductReduce from "@/components/MyProductReduce";
export default {
// 数据
data() {
return {
list: [
{
id: 1,
proname: "超级好吃的棒棒糖",
proprice: 18.8,
info: "开业大酬宾, 全场8折",
},
{
id: 2,
proname: "超级好吃的大鸡腿",
proprice: 34.2,
info: "好吃不腻, 快来买啊",
},
{
id: 3,
proname: "超级无敌的冰激凌",
proprice: 14.2,
info: "炎热的夏天, 来个冰激凌了",
},
],
};
},
// 组件
components: {
MyProductReduce,
},
};
</script>
<style>
</style>
4、子组件向父组件传值_自定义事件
通过子组件触发夫组件自定义事件方法 实现商品组件的砍价
【语法】:
@自定义事件名 = "父methods中的函数名"
- 子组件
<template>
<div>
<div class="my-product">
<h1>标题:{{ title }}</h1>
<p>价格:{{ price }}¥</p>
<p>折扣:{{ intro }}</p>
<button @click="reducePri">砍一刀</button>
</div>
</div>
</template>
<script>
export default {
props: ["index", "title", "price", "intro"],
//函数
methods: {
reducePri() {
this.$emit("reduce", this.index, 1);
},
},
};
</script>
<style lang="less" scoped>
.my-product {
width: 500px;
height: 200px;
margin: 10px auto;
border: 2px solid #000;
border-radius: 10px;
h1 {
text-align: center;
}
p {
margin-left: 120px;
}
}
</style>
- 父组件
<template>
<div>
<MyProductReduces
v-for="(item, index) in list"
:key="item.id"
:title="item.proname"
:price="item.proprice"
:intro="item.info"
:index="index"
@reduce="reduceprice"
></MyProductReduces>
</div>
</template>
<script>
// 导入组件
import MyProductReduces from "@/components/MyProductReduces";
export default {
// 数据
data() {
return {
list: [
{
id: 1,
proname: "超级好吃的棒棒糖",
proprice: 18.8,
info: "开业大酬宾, 全场8折",
},
{
id: 2,
proname: "超级好吃的大鸡腿",
proprice: 34.2,
info: "好吃不腻, 快来买啊",
},
{
id: 3,
proname: "超级无敌的冰激凌",
proprice: 14.2,
info: "炎热的夏天, 来个冰激凌了",
},
],
};
},
// 函数
methods: {
reduceprice(index, price) {
this.list[index].proprice > 1 &&
(this.list[index].proprice = (this.list[index].proprice - price).toFixed(2));
},
},
// 组件
components: {
MyProductReduces,
},
};
</script>
<style>
</style>
当子组件需要修改父组件中的数据的时候使用子组件向父组件传值技术
使用步骤:
1、父组件内部给子组件绑定自定义事件
2、在子组件中使用emit(‘自定义事件名’,值)
5、跨组件传值
【图解】:
【核心】:
- EventBus/index.js- 定义事件总线bus对象
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()
- components/MyProduct_sub.vue
<template>
<div class="my-product">
<h3>标题: {{ title }}</h3>
<p>价格: {{ price }}元</p>
<p>{{ intro }}</p>
<button @click="subFn">宝刀-砍1元</button>
</div>
</template>
<script>
import eventBus from '../EventBus'
export default {
props: ['index', 'title', 'price', 'intro'],
methods: {
subFn(){
this.$emit('subprice', this.index, 1) // 子向父
eventBus.$emit("send", this.index, 1) // 跨组件
}
}
}
</script>
<style>
.my-product {
width: 400px;
padding: 20px;
border: 2px solid #000;
border-radius: 5px;
margin: 10px;
}
</style>
- List.vue
<template>
<ul class="my-product">
<li v-for="(item, index) in arr" :key="index">
<span>{{ item.proname }}</span>
<span>{{ item.proprice }}</span>
</li>
</ul>
</template>
<script>
// 目标: 跨组件传值
// 1. 引入空白vue对象(EventBus)
// 2. 接收方 - $on监听事件
import eventBus from "../EventBus";
export default {
props: ["arr"],
// 3. 组件创建完毕, 监听send事件
created() {
eventBus.$on("send", (index, price) => {
this.arr[index].proprice > 1 &&
(this.arr[index].proprice = (this.arr[index].proprice - price).toFixed(2));
});
},
};
</script>
<style>
.my-product {
width: 400px;
padding: 20px;
border: 2px solid #000;
border-radius: 5px;
margin: 10px;
}
</style>
- APP.vue
<template>
<div style="overflow: hidden;">
<div style="float: left;">
<MyProduct
v-for="(obj, ind) in list"
:key="obj.id"
:title="obj.proname"
:price="obj.proprice"
:intro="obj.info"
:index="ind"
></MyProduct>
</div>
<div style="float: left;">
<List :arr="list"></List>
</div>
</div>
</template>
<script>
import MyProduct from "./components/MyProductRedu.vue";
import List from "./components/List";
export default {
data() {
return {
list: [
{
id: 1,
proname: "超级好吃的棒棒糖",
proprice: 18.8,
info: "开业大酬宾, 全场8折",
},
{
id: 2,
proname: "超级好吃的大鸡腿",
proprice: 34.2,
info: "好吃不腻, 快来买啊",
},
{
id: 3,
proname: "超级无敌的冰激凌",
proprice: 14.2,
info: "炎热的夏天, 来个冰激凌了",
},
],
};
},
components: {
MyProduct,
List,
},
};
</script>
<style>
</style>