单向数据流的好处
父组件通过属性传值
<Student name="李四"/>
子组件通过props参数接收数据,数据优先被设置在vc组件上(vc.数据)
//写法1 数组形式
props:['age']
//写法2 对象形式,设置默认值、接收的数据类型等
props:{
age:{
type:String,
default:'这是默认数据'
}
}
说明
$attrs
里会存储传过来的属性,子组件的vc实例上不会存储。$attrs
里不会存储。$attrs 包含父作用域里除 class 和 style 除外的非 props 属性(子组件没有使用props接收)集合。
什么是双向数据绑定
父组件可以通过props
改变子组件的数据,子组件也可以用emit
事件通知父组件的改变数据。
双方数据可以互相更新,这就是双向数据绑定。
思路
@
或v-on:
将回调函数绑定在子组件的vc上 @自定义事件=’事件回调'
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>
@click.native
,将自定义事件变为原生的DOM事件,是将事件绑定在了子组件的根节点上。$emit()
触发自定义事件的函数是Vue原型上的,组件实例vc可以看见,但是原生的DOM看不见。v-model实现原理 单向绑定默认是表单元素的value属性 + @input事件监听
<input type='text' :value="msg" @input = "msg = $event.target.value"/>
<span>{{msg}}</span>
v-model 父组件通过子组件标签传值,子组件通过$emit触发
<Son v-model="msg" />
<Son :value="msg" @input= "val => msg=val "/>
$emit
修改父组件的数据props:['value']
text 和 textarea 元素使用 value property 和 input 事件;
checkbox 和 radio 使用 checked property 和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件。
.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>
多层嵌套组件传递数据时,如果只是传递数据,而不做中间处理的话就可以用这个,比如父组件向孙子组件传递数据时
v-bind="$attrs"
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的固定写法,不是之前学习的给子组件的props传值
2.父组件(给插槽传结构的代码)外侧包裹标签,也可以写成
,
yyyy
接收到的是插槽传过来的{xxxx:数据}
3.父组件接收的数据是一个对象,对象包含传过来的值。也就是说传过来的值外层包裹了一层对象,所以起名的时候yyy可以和xxx不一样。
<Category title="游戏">
<template scope="ranan">
<ul>
{{ranan}}
ul>
template>
Category>
<template>
<div class="category">
<slot :game="games">我是默认的一些内容slot>
div>
template>
<script>
export default {
name:'Category',
props:['title'],
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
}
},
}
script>
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 提供的变量本身就需要是响应式的。
全局事件总线的特点
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去解绑当前组件所用到的事件