一、单向数据流
vue中组件间的数据传递一般是单向的,即由父传子,也就是说组件间的数据传递存在单向数据流。
组件中的数据传递方式一般有以下3种:
- 父传子
- 子传父(通过发布订阅模式实现)
- 兄弟组件间的数据传递(通过事件车event bus实现)
单向数据流:如果子组件使用了父组件中的数据,当父组件通过某些方法发生更改时,更改了自己的数据时,那么子组件也会随着发生改变,父组件中的数据又流向了子组件,子组件发生了更改,这就是组件之间数据传递的单向数据流。
<div id="app">
- 父组件中的内容<br/>
{{num}}<button @click="add">改变父组件中内容</button><br/><br/>
<child :m="num"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
// 单向数据流:如果子组件使用了父组件中的数据,当父组件通过某些方法发生更改时,更改了自己的数据时,那么子组件也会随着发生改变,父组件中的数据又流向了子组件,子组件发生了更改,这就是组件之间数据传递的单向数据流。
let child = {
data() {
return {
num: 100,
}
},
props: {
m: {
type: [Number, String],// type:对传递过来的数据进行验证
}
},// props中放的是动态属性的属性名
methods:{
fn(){
// console.log(this.m);//当父元素的数据传递给子组件,此时子组件可以用this调用
this.num++;
}
},
beforeUpdate(){
console.log("子组件beforeUpdate");
},
updated(){
console.log("子组件updated");
},
template: `<div>
- 子组件中的内容<br/>
父组件传递过来的数据:{{m}}<br/>
子组件自己的数据:{{num}}<button @click='fn'>改变子组件中内容</button>
</div>`// 和props对应;
}
let vm = new Vue({
el: "#app",
data: {
num:20
},
components: {
child
},
methods:{
add(){
this.num++;
}
},
beforeUpdate(){
console.log("父组件beforeUpdate");
},
updated(){
console.log("父组件updated");
}
});
// 由于父组件中的数据传递给子组件,所以当父组件中的数据更新时会引起子组件中的数据更新,执行顺序顺序是:父组件beforeUpdate-->子组件beforeUpdate-->子组件updated-->父组件updated
// 当父元素组件中的数据流向子组件时,在子组件中不允许直接修改父组件传递过来的数据
// 当只修改子组件中的数据时只会触发子组件的子组件beforeUpdate-->子组件updated,不会触发父组件的更新
// 当父组件中没有传递给子组件的数据发生变化时只会触发父组件的父组件beforeUpdat-->父组件updated
</script>
二、父传子
过程:
- 1、把父组件的数据以动态属性的方式放在子组件的行间属性上
- 2、在子组件中使用props属性接收,props的属性值可以是对象或数组
- 3、在子组件中使用父组件传过来的数据时需要使用props中定义的属性
<div id="app">
<!--使用子组件,输出hello world-->
<!--m,s都是自定义的行间属性名,通过v-bind将其变为动态属性-->
<child :m="more" :s="sweet"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let child = {//创建子组件
data(){
return {num:1};
},
props:["m","s"],//props中接收的变量都会挂载到子组件的vue实例上
template:"<div>{{m}} {{s}}</div>"
};
let vm = new Vue({
el:"#app",
data:{
more:"hello",
sweet:"world"
},
components:{//注册子组件
child
}
});
</script>
props验证
子组件中的props属性对应的属性值可以是数组,也可以是对象。
属性值是对象时常见的几个属性:
- type:属性值是对象或数组,表示子组件获取的该变量的数据类型
- required:属性值是boolean值,true表示该属性值必须传递,false表示该属性值可以不传递
- default:设置子组件获取的该变量的默认值,如果传递过来的值是undefined它就取默认值
- validator:它是一个函数,返回值如果true表示验证成功,如果false表示验证失败,该函数的参数是传递过来的数据,如果没有传递数据或传递的数据值是undefined时它的取值就是default的默认值。
<div id="app">
<!--输出zhufeng100-->
<child :m="msg" :s="undefined"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let child = {
data() {
return {
num: 100
};
},
props:{
m:{
// type://对传递过来的数据进行验证
type:[String,Number],//对传递过来的数据进行验证,传递过来的值可以是String或Number类型
required:true,//值为true表示必须要传递的属性,值为false表示可以不传递该属性
default:10,//如果没有传值使用这个默认值
validator(val){//validator验证,如果该函数返回true没有问题,如果返回false会报异常
// val表示传过来的值
return true;
}
},
s:{
type:[Number,String,Object],//它对应的值可以是数组或是对象
// required:true,//表示该项必须传递数据
default:100,//如果传递过来的值是undefined,则取默认值;如果传递的值是null,则直接获取null,此处不能写`:s=""`即在数据传递时不能传递空
validator(val){//如果不传值,函数中val的值是default中对应的值
console.log(val);
return true;
}
}
},
template: "<div>{{m}}{{s}}</div>"//此处和props中的名称一致。如果动态绑定的属性名和props中的名称不一致,但是template中的变量名和props中的名称一致,此时不会报错,template中的值是undefined
};
let vm = new Vue({
el: "#app",
data: {
msg: "zhufeng",
msg1: "peixun"
},
components: {
child
}
});
</script>
三、子传父(发布订阅)
子组件中数据变化引起父组件中的数据变化,实质是通过发布订阅模式实现的。
<div id="app">
{{money}}
<!-- 订阅 -->
<!-- 注意自定义事件名称不能使用驼峰命名法 -->
<son :m="money" @myselfchange="add1"></son>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
// 在vue中子组件不能直接修改父组件中的数据,即不能直接修改props中的数据
let son = {
data(){
return {};
},
props:["m"],//props接收的数据也会放到当前祖籍爱你的实例上
template:"<div>{{m}}<button @click='add'>多要点</button></div>",
methods:{
add(){
// 此处是子组件发布父组件的传递过来的方法从而引起父组件数据的变化,$emit中从第二个参数开始传递的参数都是直接传递给父组件的方法
this.$emit("myselfchange",1);
}
}
};
let vm = new Vue({
el:"#app",
data:{
money:200
},
method:{
},
components:{
son
},
methods:{
add1(val){
this.money += val;
}
}
});
</script>
sync修饰符
<div id="app">
{{money}}
{{cor}}
<son :m="money" :b="cor" @update:b="cor=$event"></son>
<!-- <son :m.sync="money"></son> -->
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
// 在vue中子组件不能直接修改父组件中的数据,即不能直接修改props中的数据
let son = {
data(){
return {};
},
props:["m","b"],//props接收的数据也会放到当前子组件的实例上
template:"<div>{{m}}{{b}}<button @click='add'>多要点</button></div>",
methods:{
add(){
// 此处是子组件发布父组件的传递过来的方法从而引起父组件数据的变化,$emit中从第二个参数开始传递的参数都是直接传递给父组件的方法
this.$emit("update:b",100);
}
}
};
let vm = new Vue({
el:"#app",
data:{
money:200,
cor:300
},
components:{
son
}
});
</script>
四、兄弟之间数据传递(event bus)
兄弟之间的数据传递通过事件车(event bus)实现。
思想:创建一个新的第三方vue实例eventBus,在第一个兄弟组件执行created钩子函数时给eventBus实例订阅该兄弟组件上的方法,再由第二个兄弟组件触发eventBus中订阅的事件执行。
<div id="app">
<!-- 使用组件 -->
<bro1></bro1>
<bro2></bro2>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let eventBus = new Vue;
let bro1 = {//创建组件
data() {
return {color: "红色"};
},
methods: {
changeGreen() {
this.color = "绿色";
},
fn(){//目的是让它的兄弟组件变红,即调用eventBus中的changeRed事件即可
eventBus.$emit("changeRed");
}
},
created() {//给eventBus订阅changeGreen事件
eventBus.$on("changeGreen", this.changeGreen);
},
template: "<div>{{color}}<button @click='fn'>变红</button></div>"
};
let bro2 = {//创建组件
data() {
return {color: "绿色"};
},
methods: {
changeRed() {
this.color = "红色";
},
fn(){//目的是让它的兄弟组件变绿,即调用eventBus中的changeGreen事件即可
eventBus.$emit("changeGreen");
}
},
created() {//给eventBus订阅changeRed事件
eventBus.$on("changeRed", this.changeRed);
},
template: "<div>{{color}}<button @click='fn'>变绿</button></div>"
};
let vm = new Vue({
el: "#app",
components: {//注册组件
bro1, bro2
}
});
</script>