第八单元(组件通信 子父,父子组件通信 自定义事件 事件修饰符 v-model props验证 )
#课程目标
-
掌握使用props让父组件给子组件传参(重点)
-
掌握props属性的使用以及prop验证的常用规则
-
掌握使用自定义事件让子组件给父组件传参(重点)
-
了解v-model在组件中的绑定原理,掌握组件的v-model的绑定
-
熟悉常用的表单修饰符、事件修饰符、键盘修饰符
#知识点
#1.组件间的通信方式
#1.1父组件给子组件传递数据--使用props属性
在父组件中动态绑定自定义的props属性来传递的数据,Parent.vue
如下:
<template>
<div>
Parent
<br />
<Children :pmsg='msg'></Children>
</div>
</template>
<script>
import Children from './Children'
export default {
name: 'Parent',
data () {
return {
msg:'我是父组件的数据'
}
},
components:{
Children
}
}
</script>
代码所示中的:pmsg
(:是vue指令v-bind的缩写)则是绑定的自定义props属性名称,msg
则是父组件想要给子组件传递的数据
在子组件中使用 props 选项去接收来自父组件传递过来的数据 ,Children.vue
示例代码如下:
<template>
<div>
Children
<br />
{{pmsg}}
</div>
</template>
<script>
export default {
name: 'Children',
//通过props来去接收父组件传递的值
props:['pmsg'],
data () {
return {
}
}
}
</script>
代码中要使用父亲传过来的参数,首先要在props
中先定义父亲传过来的props属性名称,然后就可以在页面中使用父亲传过来的数据了,在此案例中,pmsg
就是父组件的传到子组件中的数据了。
注意,由于HTML特性是不区分大小写的,所以传递属性值时,如果想传递驼峰参数,pMsg
应该转换成 kebab-case
(短横线隔开式):p-msg='msg'
。如:
//父组件传值时 想要传pMsg 应该这样写
<Children :p-msg='msg'></Children>
//子组件接收的是驼峰
props:['pMsg']
关于props
- 可以使用v-bind动态绑定父组件来的内容
- 在组件中使用props来从父组件接收参数,注意,在props中定义的属性,都可以在子组件中直接使用
- props来自父级,而组件中data return的数据就是组件自己的数据,两种情况作用域就是组件本身,可以在template,computed,methods中直接使用
- props的值有两种,一种是字符串数组,一种是对象
- 对于数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态(尽可能将父子组件解耦,避免子组件无意中修改了父组件的状态)
#1.2 Prop验证
我们可以给组件的props属性添加验证,当传入的数据不符合要求时,Vue会发出警告。
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
type 可以是下面原生构造器:
- String
- Number
- Boolean
- Function
- Object
- Array
- Symbol
type 也可以是一个自定义构造器函数,使用 instanceof 检测。
<template>
<div>
<div>名字: {{ person-prop.name }}, 年龄: {{ person-prop.age }} </div>
</div>
</template>
<script>
// 自定义Person构造器 这个可以定义在外边引进来,这里只是方便教材描写演示
function Person(name, age) {
this.name = name
this.age = age
}
export default {
name: 'Children',
//通过props来去接收父组件传递的值
//如果传过来的不是Person类型会报错,比如:1,'str',等等的其他类型...
props: {
person-prop: {
type: Person // 指定类型
}
},
data () {
return {
}
}
}
</script>
#1.3 子组件给父组件传递数据--使用自定义事件
父组件使用props
传递数据给子组件,子组件怎么跟父组件通信呢?这时,Vue的自定义事件就派上用场了。接下来我们将学习Vue自定义事件。
每个 Vue 实例都实现了事件接口 (Events interface),即:使用 $on(eventName) 监听事件、 使用 $emit(eventName) 触发事件。
父组件可以在使用子组件的地方直接用 v-on
来监听子组件触发的事件。
也就是说:子组件用 $emit() 来触发事件 ,父组件用 v-on来 监听子组件的事件 。
注意:不能用 $on
侦听子组件抛出的事件,而必须在模板里直接用 v-on
绑定。
第一步:在子组件中触发$emit
自定义事件。(第一个参数是事件名,后边的参数是要传递的数据 )
//smsg 是自定义事件的名称
//和原生的click,change,keyup等等类似
//上面几个分别是 点击事件的名称、数据变动事件的名称、键盘抬起事件的名称
//data是想要传递的数据
this.$emit('smsg',data)
第二步:在父组件中监听自定义事件中来接收子组件的参数
//监听原生的click事件 是@click
//那么监听自定义事件 也是如此 @smsg
//getMsg是函数
//这个函数中如果有值的传递,必须传递$event参数 $event就是子组件中传递过来的data
//所以可以有两种方式写
<Children @smsg="getMsg"></Children>
//或者
<Children @smsg="getMsg($event)"></Children>
子组件Children.vue
具体代码:
<template>
<div class="cc">
Children
<br />
<button @click="clickMe">点击我</button>
</div>
</template>
<script>
export default {
name: 'Children',
data () {
return {
msg:'我是子组件的数据'
}
},
methods:{
clickMe(){
//子组件中先触发这个emit事件
this.$emit('smsg',this.msg);
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.cc{
height: 100px;
background-color: red;
}
</style>
父组件Parent.vue
具体代码:
<template>
<div class="pp">
Parent--{{msg}}
<br />
<Children @smsg="getMsg"></Children>
<!--也可以这样写-->
<!--<Children @smsg="getMsg($event)"></Children>-->
</div>
</template>
<script>
import Children from './Children'
export default {
name: 'Parent',
data () {
return {
msg:'我是没有改变之前的数据'
}
},
methods:{
//this.$emit('smsg',this.msg);
//data这个参数 指的就是子组件中 this.msg
getMsg(data){
//console.log(msg)
this.msg = data;
}
},
components:{
Children
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.pp{
height: 500px;
background-color: pink;
}
</style>
#1.4 在组件中使用 v-model
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件。
我们可以使用v-model
来去绑定组件,试试怎么实现组件间的传值。
首先我们得知道v-model
的绑定原理:
//正常表单绑定v-model
//data中的msg会随着input中的值变化而变化 也就是双向绑定
<input type="text" v-model="msg" />
//不使用v-model完成数据的双向绑定
<input type="text" @input="msg=$event.target.value" :value="msg" />
通过上述的代码,我们已经明白了v-model
的绑定原理,那么如果我们想给一个子组件上的input
与父组件的某个数据做一个双向数据绑定,我们该怎么做呢?(Children组件,假定这个组件有一个input元素)
//如果这样绑定这个v-model指令
<Children v-model="msg"></Children>
//根据之前的原理剖析,上面这个代码可以转换成:
<Children @input="msg=$event.target.value" :value="msg"></Children>
思考:这么写对不对呢?
我们再看子组件Children.vue
中的部分代码:
<template>
<div>
//如果想要作为双向绑定,那么子组件中的值就应该往父组件中传,这里就要用到$emit去自定义事件
//因为父组件是使用input来监听,那么我们就应该使用自定义'input'的事件触发 这里是和原生input同名而已
//并把当前value值传给父组件
<input type="text" @input="$emit('input',$event.target.value)" :value="value" />
</div>
</template>
//既然父组件传了prop名为value的的值,那么props中应该定义一个value
props:['value']
写到这里我们打开浏览器会发现,我们并没有实现双向绑定,为什么呢?
我们再回顾一下,在讲父组件监听自定义事件的时候,说过参数$event
就是子组件传回来的参数。
那么,在父组件中,我们应该改为:
<Children @input="msg=$event" :value="msg"></Children>
到此为止,我们就实现了父组件与子组件中表单元素的双向绑定
#2.修饰符
#2.1表单修饰符
(1).lazy
在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步。
如下,示例:
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" />
(2).number
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符。
如下,示例:
<input v-model.number="age" />
(3).trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符。
如下,示例:
<input v-model.trim="msg" />
#2.2事件修饰符
在Vue中,事件修饰符处理了许多DOM事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理。在Vue中事件修饰符主要有:
- .stop:等同于JavaScript中的
event.stopPropagation()
,防止事件冒泡 - .prevent:等同于JavaScript中的
event.preventDefault()
,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播) - .capture:与事件冒泡的方向相反,事件捕获由外到内
- .self:只会触发自己范围内的事件,不包含子元素
- .once:只会触发一次
.stop:等同于JavaScript中的event.stopPropagation()
,防止事件冒泡
<div id="app">
<div class="outeer" @click.stop="outer">
<div class="middle" @click.stop="middle">
<button @click.stop="inner">点击我</button>
</div>
</div>
</div>
.prevent取消默认事件
.prevent
等同于JavaScript的event.preventDefault()
,用于取消默认事件。
<!--不会跳转到另外一个页面-->
<a href="https://www.baidu.com" @click.prevent="clickMe"></a>
.capture 捕获事件
捕获事件:嵌套两三层父子关系,然后所有都有点击事件,点击子节点,就会触发从外至内 父节点 ==>子节点的点击事件
<div class="outeer" @click.capture="outer">
<div class="middle" @click.capture="middle">
<button @click.capture="inner">点击我</button>
</div>
</div>
.self
修饰符.self
只会触发自己范围内的事件,不会包含子元素。
<div class="outeer" @click.self="outer">
<div class="middle" @click.self="middle">
<button @click.stop="inner">点击我</button>
</div>
</div>
.once 只执行一次点击
如果我们在@click
事件上添加.once
修饰符,只要点击按钮只会执行一次。
#2.3键盘修饰符
在JavaScript事件中除了前面所说的事件,还有键盘事件,也经常需要监测常见的键值。在Vue中允许v-on
在监听键盘事件时添加关键修饰符。记住所有的keyCode
比较困难,所以Vue为最常用的键盘事件提供了别名:
- .enter:回车键
- .tab:制表键
- .delete:含
delete
和backspace
键 - .esc:返回键
- .space: 空格键
- .up:向上键
- .down:向下键
- .left:向左键
- .right:向右键
#授课思路
#案例和作业
使用组件方式实现弹窗