vue2 组件传值的方式 v-model的原理和实现
文章目录vue2 组件传值的方式父组件给子组件传值方式1:props方式2:插槽子组件给父组件传值方式3:组件的自定义事件方式4:父组件给子组件传递函数 子组件调用函数传参 props方式5:全局事件总线 全能vue2 组件传值的方式props通过一般属性实现父向子通信通过函数属性实现子向父通信父组件给子组件传值方式1:props通过一般属性实现父向子通信通过函数属性实现子向父通信 – 方式4父组
vue2 组件传值的方式
面试题
- 组件传值方式
- vue是否是单向数据流
- 父子组件传值方式
- v-model语法糖,具体绑定哪些属性
- v-model双向绑定原理,v-model的实现
什么是单向数据流
- 什么是单向数据流
数据流是指组件之间数据的流向,单向数据流指数据只能从父组件向子组件传递,子组件无法改变父组件的props,如果想修改有其他的方式。 - 为什么不能是双向的
父组件的数据发生改变,会通过props来通知子组件自动更新。 防止多个子组件都尝试修改父组件状态时,导致数据混乱
单向数据流的好处
- 单向数据流会使所有状态的改变可记录、可跟踪,源头易追溯;
- 所有数据只有一份,组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性。
父组件给子组件传值
props
- 通过一般属性实现父向子通信
- 通过函数属性实现子向父通信 – 方式4
父组件通过属性传值
<Student name="李四"/>
子组件通过props参数接收数据,数据优先被设置在vc组件上(vc.数据)
//写法1 数组形式
props:['age']
//写法2 对象形式,设置默认值、接收的数据类型等
props:{
age:{
type:String,
default:'这是默认数据'
}
}
说明
- props是只读属性,子组件不可以修改传入的值。
- 如果子组件不使用prop,子组件的
$attrs
里会存储传过来的属性,子组件的vc实例上不会存储。
如果子组件使用prop,子组件的vc实例上会直接存储传过来的属性,子组件的$attrs
里不会存储。
$attrs 包含父作用域里除 class 和 style 除外的非 props 属性(子组件没有使用props接收)集合。
如何实现子组件给父组件传值?如何实现双向数据绑定?
什么是双向数据绑定
父组件可以通过props
改变子组件的数据,子组件也可以用emit
事件通知父组件的改变数据。
双方数据可以互相更新,这就是双向数据绑定。
props父传子 + 绑定事件监听on/触发事件监听emit 子传父
思路
- 将自定义事件绑定在子组件的实例vc上,回调函数是在父组件中,通过回调函数接收参数
- 写法1: 在父组件中使用@
或v-on:
将回调函数绑定在子组件的vc上@自定义事件=’事件回调'
- 写法2: 在父组件中this.$refs.xxx
获取到子组件的实例,采用$on(‘事件名’,回调函数
)绑定自定义事件 - 子组件
$emit()
触发自定义事件并传递参数,子组件this.$off()
解绑自定义事件
父组件代码
<template>
<div>
<h1>我是父组件</h1>
<Son :info="info" @change="fn"></Son>
</div>
</template>
<script>
import Son from "./Son.vue";
export default {
data() {
return {
info: "我是父组件中的数据",
};
},
components: {
Son,
},
methods: {
fn(info) {
this.info = info + "我是父组件中点击修改后的数据";
},
},
};
</script>
子组件代码
<template>
<div>
<h2>我是子组件</h2>
<p>{{ info }}</p>
<button @click="fn">修改数据(子)</button>
</div>
</template>
<script>
export default {
props: ["info"], //父传子
methods: {
fn() {
//这种直接赋值prop是不可取的,vue会直接报错
//this.info=this.info+"子组件直接赋值prop"
// 修改数据
this.$emit('change',this.info + ",我现在被子组件emit了"); //触发自定义事件并传值,父组件自定义事件的回调函数触发
},
},
};
</script>
自定义事件的补充知识点
- 组件在绑定事件时,默认认为绑定的是自定义事件。
可以使用.native标识符比如@click.native
,将自定义事件变为原生的DOM事件,是将事件绑定在了子组件的根节点上。 - 给原生的DOM绑定自定义事件没有意义,因为
$emit()
触发自定义事件的函数是Vue原型上的,组件实例vc可以看见,但是原生的DOM看不见。
v-model语法糖 = props父传子 + 绑定事件监听@input/触发事件监听emit 子传父
v-model实现原理 单向绑定默认是表单元素的value属性 + @input事件监听
<input type='text' :value="msg" @input = "msg = $event.target.value"/>
<span>{{msg}}</span>
v-model 父组件通过子组件标签传值,子组件通过$emit触发
- 父组件给子组件传值,并绑定自定义事件
默认传递的属性是value,自定义事件名为input
<!--v-model简写-->
<Son v-model="msg" />
<!--原始写法-->
<Son :value="msg" @input= "val => msg=val "/>
- 子组件使用props接收,通过
$emit
修改父组件的数据
props:['value']
text 和 textarea 元素使用 value property 和 input 事件;
checkbox 和 radio 使用 checked property 和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件。
sync 修饰符 props传参+ 绑定事件监听@update:属性名/触发事件监听emit 子传父
.sync修饰符可以实现和v-model
同样的功能,而且它比v-model
更加灵活。
v-model
一个组件只能用一个,sync
可以有多个。
xxx.sync
的原理
①父组件给字子组件传递props:属性名
②给当前子组件绑定了一个自定义事件,事件名为update:属性名
,该事件会更新xxx的值
// 正常父传子:
<son :info="str" :title="str2"></son>
// 加上sync之后父传子(.sync没有数量限制):
<son :info.sync="str" .title.sync="str2"></son>
// 它等价于
<son
:info="str" @update:info="val=>str=val"
:title="str2" @update:title="val=>str2=val"></son>
子组件
<template>
<div>
<p>{{ info }}</p>
<button @click="fn">修改数据(子)</button>
</div>
</template>
<script>
export default {
props: ["info","title"],
name:'son',
methods: {
fn() {
// 修改数据:`$emit`所调用的事件名必须是`update:属性名`
this.$emit('update:info',this.info + ",我现在被子组件emit了")
},
},
};
</script>
父组件给子组件传值
$attrs + $listeners 父组件给子组件传值
多层嵌套组件传递数据时,如果只是传递数据,而不做中间处理的话就可以用这个,比如父组件向孙子组件传递数据时
- $attrs:包含父作用域里除 class 和 style 除外的非 props 属性集合。通过 this.$attrs 获取父作用域中所有符合条件的属性集合,然后还要继续传给子组件内部的其他组件,就可以通过
v-bind="$attrs"
- $listeners:包含父作用域里 .native 除外的监听事件集合。如果还要继续传给子组件内部的其他组件,就可以通过
v-on="$linteners"
需求:对el-button进行二次封装,由传递的参数决定是什么类型的button
父组件
<Son type="success" icon="el-icon-delete" size="mini" title="提示按钮" @click=”handler“></Son>
子组件Son,不使用props接收
<template>
<div>
/*把attrs上的属性都绑定在el-button上,注意v-bind不可以简写*/
/*把父组件传递的自定义事件绑定在子组件上,注意v-on不可以简写*/
<el-button v-bind="$attrs" v-on="$listeners"></el-button>
</div>
</template>
插槽 结构父 -> 子
让父组件可以向子组件指定位置插入html结构,主要通信的数据是html结构
提到了这个感觉就可能会问有哪些插槽了
- 默认插槽
- 具名插槽
slot
标签的name
属性命名 - 作用域插槽 数据在插槽位置,但是根据数据生成的结构需要父组件来决定
作用域插槽 数据: 子 -> 父 结构:父 -> 子
使用场景:数据在插槽位置,但是根据数据生成的结构需要父组件来决定
数据在子组件(作用域),结构由父组件传。
数据: 子 -> 父 结构:父 -> 子
步骤
1.<slot :xxxx="数据">
将数据传给父组件,slot的固定写法,不是之前学习的给子组件的props传值
2.父组件(给插槽传结构的代码)外侧包裹<template scope="yyyy">
标签,也可以写成<template slot-scope="yyyy">
,yyyy
接收到的是插槽传过来的{xxxx:数据}
3.父组件接收的数据是一个对象,对象包含传过来的值。也就是说传过来的值外层包裹了一层对象,所以起名的时候yyy可以和xxx不一样。
<Category title="游戏">
<template scope="ranan">
<ul>
{{ranan}} <!--接收到{game:['红色警戒','穿越火线','劲舞团','超级玛丽'],mag:"hello"}-->
</ul>
</template>
</Category>
<!--category组件-->
<template>
<div class="category">
<slot :game="games">我是默认的一些内容</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
}
},
}
</script>
祖先组件向子孙组件传值
provide/inject
- provide提供数据:指定想要提供给后代组件的数据或方法
- inject在组件中注入数据:在任何后代组件中使用inject接收provide提供的数据或方法,不管组件嵌套多深都可以直接拿来用
inject
在data/props
之前初始化,provide
在data/props
之后初始化,注入内容时,是将内容注入到当前vc的__provide
中
inject配置key先在当前组件读取内容(__provide
),读取不到则取它的父组件读取,找到最终内容保存到当前实例(vc)中,这样可以直接通过this读取到inject注入的内容。
// 父组件
export default {
provide: {
name: "父组件数据",
say() {
console.log("say say say");
},
},
// 当需要用到this的时候需要使用函数形式
provide() {
return {
todoLength: this.todos.length
}
},
}
// 子组件
<template>
<div>
<div>provide inject传递过来的数据: {{ name }}</div>
<div>provide inject传递过来的方法<button @click="say">say</button></div>
</div>
</template>
<script>
export default {
inject: ['name', 'say'],
},
}
</script>
Vue 不会对 provide 中的变量进行响应式处理。所以,要想 inject 接受的变量是响应式的,provide 提供的变量本身就需要是响应式的。
全能
全局事件总线 $on
全局事件总线的特点
1.所有组件都可以看见 – 事件总线需要在Vue显式原型上,这样所有的组件都可以看见
2.需要有$on
绑定事件、$off
解绑事件、$emit
触发事件 – Vue显式原型里的$on
、$off
、$emit
函数,所以事件总线需要能看见Vue显式原型
import Vue from 'vue'
const vm = new Vue({
el:'#app',
render:h=>h(App),
beforeCreate(){
Vue.prototype.$bus= this;//安装全局事件总线
}
})
组件中通过this.$bus.$on
、this.$bus.$off
、this.$bus.$emit
使用 --使用方法类似组件的自定义事件
1.全局事件总线所有组件都可以看见,所以要小心事件名重复。
2.最好在beforeDestroy
钩子中,使用$off去解绑当前组件所用到的事件
更多推荐
所有评论(0)