Vue
Vue 组件
概念
组件就是可复用的 vue 文件( Vue 实例, 封装标签, 样式和 JS 代码)。
组件化 :封装的思想,把页面上 可重用的部分 封装为 组件,从而方便项目的 开发 和 维护
一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的 结构 样式 和 行为( html , css 和 js )
使用
-
创建组件,封装代码
-
引入组件,注册组件
- 全局注册:
main.js中vue.compnent(自定义组件名, 组件对象)。// 目标: 全局注册 (一处定义到处使用) // 1. 创建组件 - 文件名.vue // 2. 引入组件 import Pannel from './components/Pannel' // 3. 全局 - 注册组件 Vue.component("PannelG", Pannel) - 局部注册:某个
vue文件中,components:{ 自定义组件名: 组件对象 }。import Pannel from './components/Pannel_1' export default { // 3. 局部 - 注册组件 components: { PannelL: Pannel } }
- 全局注册:
-
使用组件
把注册的组件名当成标签名使用在结构中即可。
scoped 作用
scpoed 添加到 style 标签上的属性;作用:使得样式只作用于当前文件的标签上。
组件通信
因为每个组件的变量和值都是独立的。
父: 使用其他组件的vue文件。父组件中,通过标签属性的方式将数据传递给子组件。
子: 被引入的组件(嵌入)。子组件中,props 定义变量,用于接收父组件传递过来的参数。
父传子
关键代码:
props
步骤:
- 在文件夹
components下创建子组件Product.vue。<template> <div class="my-product"> <h3>标题: {{ title }}</h3> <p>价格: {{ price }}元</p> <p>{{ intro }}</p> </div> </template> - 组件内在
props定义变量, 用于接收外部传入的值。<script> export default { props: ['title', 'price', 'intro'] } </script> App.vue中引入注册组件, 使用时, 传入具体数据给组件显示。<template> <div> <Product title="好吃的口水鸡" price="50" intro="开业大酬宾, 全场8折"></Product> <Product title="好可爱的可爱多" price="20" intro="老板不在家, 全场1折"></Product> <Product title="好贵的北京烤鸭" price="290" :intro="str"></Product> </div> </template> <script> // 引入组件 import Product from './components/MyProduct' export default { data(){ return { str: "好贵啊, 快来啊, 好吃" } }, // 3. 注册组件 components: { Product // Product: Product // key和value变量名同名 - 简写 } } </script>
整体步骤概况如下图所示。
父向子-配合循环
把数据循环分别传入给组件内显示。每次循环,变量和组件互不影响。
子组件代码:
<template>
<div class="my-product">
<h3>标题: {{ title }}</h3>
<p>价格: {{ price }}</p>
<p>{{ intro }}</p>
</div>
</template>
<script>
export default {
props: ["title", "price", "intro"],
};
</script>
父组件代码
<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.vue";
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>
父向子-单向数据流
从父到子的数据流向,叫单向数据流 ,在 vue 中需要遵循单向数据流原则。
- 父组件的数据发生了改变,子组件会自动跟着变。
- 子组件不能直接修改父组件传递过来的
props数据。注意:
关于子组件不能直接修改父组件传递过来的
props数据中,指的是不能修改其引用的地址(因为是数组类型的数据,属于复杂数据类型),比如用map方法或者filter方法修改了原数组返回了新数组并覆盖了原数据,此时就是改变了引用地址。this.arr = this.arr.filter((item) => item.id != id);但是如果仅仅改变了数组内的值,其地址没有发生变化,则可以让子组件改变
props内的数据。如splice、push等方法。let index = this.arr.findIndex((item) => item.id == id); this.arr.splice(index, 1);
原因:
子组件修改了不通知父组件,会造成数据不一致。因此
vue规定,props是只读的。
父组件传给子组件的是一个对象,子组件修改对象的属性,是不会报错的,对象是引用类型, 互相更新
总结: 所以
props变量本身是不能重新赋值的。
子传父
子组件中:在恰当时机(一般是触发某个数据时候)通过 $emit 自定义事件,把值传递给父组件。
父组件中:使用子组件标签上绑定自定义事件来接收数据。
步骤:
- 子:
@自定义事件名="methods函数" methods函数(){ this.$emit(自定义名称,索引或id,数值) }- 参数1:自定义的名称,可随便更换。
- 参数2:由父传子获得的索引或
id。 - 参数3:变量或数值。
- 父:
自定义名称="自定义函数名" 自定义函数名(参数1,参数2){}- 参数1:接收子组件传递过来的索引或
id。 - 参数2:接收子组件传递过来的数值。
- 参数1:接收子组件传递过来的索引或
整体流程如下所示:
- 子组件:
<div class="my-product"> <h3>标题: {{ title }}</h3> <p>价格: {{ price }}</p> <p>{{ intro }}</p> <button @click="kanFn">砍价</button> </div> </template> <script> export default { props: ["title", "price", "intro", "index"], methods: { kanFn() { this.$emit("subprice", this.index, 2); }, }, }; </script> - 父组件
<template> <div> <h1>hello world</h1> <aa></aa> <bb></bb> <myproduct v-for="(item, index) in list" :key="item.id" :title="item.proname" :price="item.proprice" :intro="item.info" :index="index" @subprice="fn" ></myproduct> </div> </template> <script> import myproduct from "./components/myProduct.vue"; 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: { // 这里的参数是由子组件中的this.$emit中第二第三个参数传来的 fn(index, price) { this.list[index].proprice = this.list[index].proprice - price; }, }, components: { myproduct }, }; </script>
总结: 父自定义事件和方法, 等待子组件触发事件给方法传值。
跨组件通信
作用:让非父子的组件能够使用通用的组件进行通讯:事件总线(event-bus)。
核心语法: 在src文件夹下创建一个 EventBus 的文件夹,里面放一个 index.js文件,用于定义事件总线 bus 对象。
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()
创建一个子组件 a.vue 。
引入事件总线 /EventBus/index.js ,为其绑定事件,触发事件后用 $emit() 方法传递参数。
- 参数1:自定义方法名称。
- 参数2:变量。
<template>
<div>
<h1>我是a</h1>
<button @click="give">点我</button>
</div>
</template>
<script>
import eventBus from "../EventBus/index.js";
export default {
data() {
return {
msg: "我是a",
};
},
methods: {
give() {
eventBus.$emit("sendata", this.msg);
},
},
};
</script>
创建一个子组件 b.vue 。
引入事件总线 /EventBus/index.js ,创建好 b.vue 组件后立即监听,使用的方法是创建一个 created 函数,在其之内用 $on 方法来监听自定义事件方法,返回回调函数。
<template>
<div>
<h1>我是b</h1>
</div>
</template>
<script>
import eventBus from "../EventBus/index.js";
export default {
created() {
eventBus.$on("sendata", (val) => {
console.log(val);
});
},
};
</script>
在 App.vue 组件中引入两个子组件。
<template>
<div>
<aa></aa>
<bb></bb>
</div>
</template>
<script>
import aa from "./components/a.vue";
import bb from "./components/b.vue";
export default {
components: { aa, bb },
};
</script>
整体流程如下所示。
总结: 空的Vue对象, 只负责on注册事件, emit触发事件, 一定要确保$on先执行