一、event深入
1、事件注意事项
事件:1、系统事件:点击、双击、鼠标系列等等
2、自定义事件
事件源(事件给谁绑定的)、事件类型(单击还是双击还是自定义等)、事件回调
1)原生DOM ----- (eg: button)可以绑定系统事件(单击、双击等等)
2)组件标签 ------- event1可以绑定系统事件(但是不起作用,因为属于自定义事件)
------- 在原生事件后加“.native”,可以把自定义事件变为原生DOM事件
注意:给原生DOM绑定自定义事件是没有任何意义的,因为没有办法触发$emit函数
<!-- 原生的DOM绑定系统事件 注意:给原生DOM绑定自定义事件是没有任何意义的,因为没有办法去发$emit函数 -->
<button @click="handler">原生DOM绑定原生事件</button>
<!-- Event组件:Event1 非原生DOM节点,而绑定的click事件并非原生DOM事件,而是自定义事件,加上.native,可以把自定义事件变为原生DOM事件,当前原生DOMclick事件,其实是给子组件的根节点绑定了
点击事件---相当于事件委派 -->
<Event1 @click.native="handler1"></Event1>
二、v-model
v-model它是vue框架中的指令,它主要结合表单元素一起使用(文本框、复选框、单选按钮等等)
它主要的作用是收集表单数据
原生DOM当中是有oninput事件,它经常结合表单元素一起使用,当表单元素文本内容发生变化的时候就会触发一次回调
vue2中:可以通过value与input事件实现v-model的功能( :value是单向绑定)
//v-model方式实现数据双向绑定
<input type="text" v-model="msg">
<span>{{msg}}</span>
<br>
//原生的方式实现数据双向绑定
// :value是只能单向绑定
<!-- 原生DOM当中是有oninput事件,它经常结合表单元素一起使用,当表单元素文本内容发生
变化的时候就会触发一次回调
vue2中:可以通过value与input事件实现v-model的功能 -->
<input type="text" :value="msg" @input="msg = $event.target.value">
<span>{{msg}}</span>
data() {
return {
msg:''
}
}
v-model实现原理:是通过value与input事件实现的,而且还需要注意可以通过v-model实现父子组件数据同步。
v-mode实现父子之间的通信如下:
// 父组件
// 注意: :value是props @input是自定义事件
// 以下两行代码等同
<Inner :value="msg" @input="msg = $event"></Inner>
<Inner v-model="msg"></Inner>
//子组件
<input type="text" :value="value" @input="$emit('input',$event.target.value)">
三、属性修饰符sync
可以实现父子组件数据同步
:money.sync的含义:
1、父组件给字符串传递props (money)
2、给当前子组件绑定了一个自定义事件,而且事件名称即为update:money(update:事件名)
以下两种方式效果一样
// 父组件
<div style="height: 150px; backgroundColor: #9370db;">
小明的爸爸现在有{{money}}元
<h2>不使用sync修饰符</h2>
<Child1 :money='money' @update:money="money=$event"></Child1>
<h2>使用sync修饰符</h2>
<!--
1、父组件给字符串传递props (money)
2、给当前子组件绑定了一个自定义事件,而且事件名称即为update:money
-->
<Child2 :money.sync="money"></Child2>
</div>
//子组件 1
<div>
<span>小明每次花100元</span>
<button @click="$emit('update:money',money-100)">花钱</button>
爸爸还剩 {{money}} 元
</div>
//子组件2
<div>
<span>小明每次花100元</span>
<button @click="$emit('update:money',money-100)">花钱</button>
爸爸还剩 {{money}} 元
</div>
四、$attrs与$listeners
他们两者是组件实例的属性,可以获取到父组件给子组件传递的props与自定义事件
具体用法如下:下面案例是对elementUI中的el-button按钮的二次封装
//父组件
<template>
<div style="height: 150px; backgroundcolor: #aeeeee">
<h2>自定义带Hover提示的按钮</h2>
<AttrsChild
type="success"
icon="el-icon-delete"
size="mini"
title="提示按钮"
@click="handler"
></AttrsChild>
</div>
</template>
<script>
import AttrsChild from "./AttrsChild";
export default {
name: "Attr",
components: {
AttrsChild,
},
methods: {
handler() {
alert(111)
}
},
};
</script>
//子组件
<template>
<div>
<!-- 可以巧妙利用a标签实现按钮带有提示功能 -->
<a :title="title">
<!-- 下面这种写法,不能用 : 代替v-bind、 不能用 @ 代替v-on -->
<el-button v-bind="$attrs" v-on="$listeners"></el-button>
</a>
</div>
</template>
<script>
export default {
props:['title'],
mounted() {
// $attrs属性组件的一个属性,可以获取到父组件传递过来的props数据
// 对于子组件而言,父组件给的数据可以利用props接收,但是,如果组件通过props接收的属性,在$attrs属性当中是获取不到的
console.log(this.$attrs)
console.log(this.$listeners)
}
}
</script>
五、$children与$parent
ref 可以获取到某一个组件的子组件
$children组件实例的属性 也可以获取到当前组件的全部子组件(返回的是一个数组)
$parent组件实例的属性 可以获取到当前子组件的父组件,进而可以操作父组件的数据与方法
案例如下:
父组件代码
<template>
<div style="height: 150px; backgroundColor: #f0fff0">
<h2>爸爸有存款:{{ money }}</h2>
<button @click="fromM(100)">找小明借钱100</button>
<button @click="fromH(150)">找小红借钱150</button>
<button @click="fromAll(200)">找所有孩子借钱200</button>
<br />
<!-- ref:相当于原生的id,获取节点 -->
<Son ref="xm" />
<br />
<Daughter ref="xh"></Daughter>
</div>
</template>
<script>
import Son from "./Son";
import Daughter from "./Daughter";
export default {
components: { Daughter, Son },
data() {
return {
money:1000
}
},
methods: {
// 向小明借
fromM(money) {
this.money += money
// console.log(this.$refs.xm.money)
this.$refs.xm.money -= money
},
// 向小红
fromH(money) {
this.money += money
this.$refs.xh.money -= money
},
// 找全部孩子借200
fromAll(money) {
this.money += 2*money
console.log(this.$children)
// 组件实例自身拥有一个属性$children,可以获取当前组件当中全部子组件
this.$children.forEach(item => {
item.money -= 200
})
}
}
};
</script>
<style>
</style>
子组件Son
<template>
<div>
<h3>儿子小明有存款:{{money}}</h3>
<button @click="giveMoney(50)">给爸爸:50</button>
</div>
</template>
<script>
export default {
name:'Son',
data() {
return {
money:30000
}
},
methods: {
giveMoney(money) {
this.money -= money
// 通过$parent属性获取到某一个组件的父组件,可以操作父组件的数据与方法
this.$parent.money += money
}
}
}
</script>
<style>
</style>
子组件Daughter
<template>
<div>
<h3>女儿小红有存款:{{ money }}</h3>
<button @click="giveMoney(100)">给爸爸:100</button>
</div>
</template>
<script>
//引入需要混入的文件
//import myMixin from "@/pages/Communication/ChildrenParent/myMixin/myMixin";
export default {
name: "Daughter",
//使用
// mixins: [myMixin],
data() {
return {
money: 20000,
};
},
methods: {
giveMoney(money) {
this.money -= money;
this.$parent.money += money;
},
},
};
</script>
<style>
</style>
六、混入mixin
如果项目当中出现很多结构类似功能,要想到组件复用
如果项目当中很多的组件JS业务逻辑相似,要想到mixin。
案例:就拿上面的案例来说道说道,把相同的代码封装到一起,然后引用,引用这里我就直接在上面的案例引入了,只需要两部:
1、import myMixin from "@/pages/Communication/ChildrenParent/myMixin/myMixin";
2、mixins: [myMixin],
下面是封装好的js文件。
export default {
methods: {
giveMoney(money) {
this.money -= money;
this.$parent.money += money;
},
},
}
七、插槽
可以实现父子组件通信(通信的是结构),插槽有三种:
1、默认插槽
2、具名插槽
3、作用域插槽:子组件的数据来源于父组件,但是子组件决定不了自身的结构与外观
案例如下(此案例为作用域插槽,其他的后续再补充):
父组件:
<template>
<div style="height:200px;backgroundColor:#87CEFF;">
<h2>效果一:显示TODO列表时,已完成TODO为绿色</h2>
<!-- 子组件:数据来源于父组件 -->
<List :todos="todos">
<!-- 子组件决定不了结构与外观 -->
<template slot-scope="todo">
<span :style="{color:todo.todo.isComplete? 'green' : 'red'}">{{todo.todo.text}}</span>
</template>
</List>
<hr>
<h2>效果二:显示TODO列表时,带序号,TODO的颜色为蓝绿搭配</h2>
<List1 :todos="todos">
<template slot-scope="{todo,$index}">
<span :style="{color:todo.isComplete? 'green' : 'blue'}">{{$index}} --- {{todo.text}}</span>
</template>
</List1>
</div>
</template>
<script>
import List from './List'
import List1 from './List1'
export default {
components: { List,List1 },
data() {
return {
todos: [
{id:1,text:'AAA',isComplete:false},
{id:2,text:'BBB',isComplete:true},
{id:3,text:'CCC',isComplete:false},
{id:4,text:'DDD',isComplete:false},
]
}
}
}
</script>
<style>
</style>
子组件List:
<template>
<div>
<ul>
<li v-for="(item,index) in todos" :key="index">
<!-- 作用域插槽 -->
<slot :todo="item"></slot>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
todos:Array
}
}
</script>
<style>
</style>
子组件List1:
<template>
<div>
<ul>
<li v-for="(item,index) in todos" :key="index">
<!-- 作用域插槽 -->
<slot :todo="item" :$index='index'></slot>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
todos:Array
}
}
</script>
<style>
</style>