Vue
Vue 组件
Vue 组件概念
组件就是可复用的 Vue 文件( Vue 实例, 封装标签, 样式和 JavaScript 代码)。
组件化 :封装的思想,把页面上可重用的部分封装为组件,从而方便项目的 开发 和 维护
一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的 结构 样式 和 行为( html , css 和 js )
Vue 组件使用
创建组件,封装代码
引入组件,注册组件
全局注册:
main.js中vue.compnent(自定义组件名, 组件对象)。js// 目标: 全局注册 (一处定义到处使用) // 1. 创建组件 - 文件名.vue // 2. 引入组件 import Pannel from './components/Pannel' // 3. 全局 - 注册组件 Vue.component('PannelG', Pannel)局部注册:某个 Vue 文件中,
components:{ 自定义组件名: 组件对象 }。jsimport Pannel from './components/Pannel_1' export default { // 3. 局部 - 注册组件 components: { PannelL: Pannel, }, }
使用组件
把注册的组件名当成标签名使用在结构中即可。
Vue scoped 作用
scpoed 添加到 style 标签上的属性;作用:使得样式只作用于当前文件的标签上。
Vue 组件通信
因为每个组件的变量和值都是独立的。
父: 使用其他组件的 Vue 文件。父组件中,通过标签属性的方式将数据传递给子组件。
子: 被引入的组件(嵌入)。子组件中,props 定义变量,用于接收父组件传递过来的参数。
Vue 父传子
关键代码:
props
步骤:
在文件夹
components下创建子组件Product.vue。vue<template> <div class="my-product"> <h3>标题: {{ title }}</h3> <p>价格: {{ price }}元</p> <p>{{ intro }}</p> </div> </template>组件内在
props定义变量, 用于接收外部传入的值。vue<script> export default { props: ['title', 'price', 'intro'], } </script>App.vue中引入注册组件, 使用时, 传入具体数据给组件显示。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>
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><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 父向子单向数据流
从父到子的数据流向,叫单向数据流 ,在 Vue 中需要遵循单向数据流原则。
父组件的数据发生了改变,子组件会自动跟着变。
子组件不能直接修改父组件传递过来的
props数据。注意:
关于子组件不能直接修改父组件传递过来的
props数据中,指的是不能修改其引用的地址(因为是数组类型的数据,属于复杂数据类型),比如用map方法或者filter方法修改了原数组返回了新数组并覆盖了原数据,此时就是改变了引用地址。jsthis.arr = this.arr.filter((item) => item.id != id)但是如果仅仅改变了数组内的值,其地址没有发生变化,则可以让子组件改变
props内的数据。如splice、push等方法。jslet index = this.arr.findIndex((item) => item.id == id) this.arr.splice(index, 1)
原因:
子组件修改了不通知父组件,会造成数据不一致。因此 Vue 规定,props 是只读的。
父组件传给子组件的是一个对象,子组件修改对象的属性,是不会报错的,对象是引用类型, 互相更新
总结: 所以
props变量本身是不能重新赋值的。
Vue 子传父
子组件中:在恰当时机(一般是触发某个数据时候)通过 $emit 自定义事件,把值传递给父组件。
父组件中:使用子组件标签上绑定自定义事件来接收数据。
步骤:
子:
js@自定义事件名="methods函数" methods函数(){ this.$emit(自定义名称,索引或id,数值) }- 参数 1:自定义的名称,可随便更换。
- 参数 2:由父传子获得的索引或
id。 - 参数 3:变量或数值。
父:
js自定义名称="自定义函数名" 自定义函数名(参数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>总结
父自定义事件和方法, 等待子组件触发事件给方法传值。
Vue 跨组件通信
作用:让非父子的组件能够使用通用的组件进行通讯:事件总线(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 先执行
Vue 拓展
Vue 原生事件绑定到组件
<base_input @input.navite="inputFn"></base_input>如上有一个 base_input 的子组件,想要使用原生 input 框自带的输入事件,可使用 .navite 修饰符。但是当该子组件根节点不是 input 输入框而是 label (如下),则加了修饰符也不会生效。
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
>
</label>因此官方特地提供了一个 $listeners ,它是一个对象,里面包含了作用在这个组件上的所有监听器。例如:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}有了这个 $listeners,可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。对于类似 <input> 的你希望它也可以配合 v-model 工作的组件来说,为这些监听器创建一个类似下述 inputListeners 的计算属性通常是非常有用的:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign(
{},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
},
},
)
},
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`,
})现在 <base-input> 组件是一个完全透明的包裹器了,也就是说它可以完全像一个普通的 <input> 元素一样使用了:所有跟它相同的 attribute 和监听器都可以工作,不必再使用 .native 监听器。