vue组件通信:父与子、子与父
程序员文章站
2022-03-13 08:31:34
父子通信1.vue中提供了很棒的组件化思想,组件提高了代码的复用性。1....
父与子、子与父通信
1. 组件通信
1.1 组件嵌套
- vue中提供了很棒的组件化思想,组件提高了代码的复用性。
- 组件中可以引入其他的组件
1.2 组件嵌套步骤
- 创建父、子组件
- 在父组件引入子组件
1.3 父与子、子与父通信
- vue.js中,父子组件的关系可以总结为props down ,events up
- 父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息(后面结合代码会细说)
- 父子通信数据传递的示意图:
1.4 实现案例图示
以下内容都是通过该案例展开的。我们希望实现:
- 在文本框输入内容后点击 + 时,下面事项列表能够更新;
- 点击每个事项后的 ×,该事项能够删除。
2. 父组件与子组件通信
2.1 父与子通信
- 子组件要使用父组件的数据,我们需要通过子组件的props选项。如何理解props选项呢?
打个比喻:小孩拿着一个大布袋子问大人要糖吃,大人把糖放进了大布袋子中
props选项就是这个“大布袋子”,父组件把数据放在子组件的props选项中,子组件内部也就有了该数据,这就是上面所说的——props down - 当然,子组件要使用父组件的数据,不仅仅需要通过子组件的props选项。
在父组件的模板中,要动态的绑定父组件的数据到子组件的props,与绑定到任何普通的html特性相类似,就是用v-bind,简写就是冒号( : )。这样每当父组件的数据变化时,该变化也会传导给子组件。
2.2代码实现父与子及详细说明
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>父子通信</title>
<script src="../js/vue.js"></script>
<script>
var todoAdd={
props:["add_tasks"],
data(){
return{task:""}
},
template:`
<div>
<input type="text" v-model="task"><button @click="add">+</button>
</div>`,
methods:{
add(){
this.add_tasks.push(this.task);
this.task="";
}
}
}
var todoItem={
props:["t","i","item_tasks"],
template:`
<li>
{{i+1}}-{{t}}<a href="javascript:;" @click="del">×</a>
</li>`,
methods:{
del(){this.item_tasks.splice(this.i,1);}
}
};
var todoList={
props:["list_tasks"],
template:`
<ul>
<todo-item v-for="(t,i) of list_tasks" :t="t" :i="i" :item_tasks="list_tasks"></todo-item>
</ul>`,
components:{todoItem}
};
Vue.component("to-do",{
data(){
return{tasks:["吃饭","睡觉","打豆豆"]}
},
template:`
<div>
<h1>待办事项列表</h1>
<todo-add :add_tasks="tasks"></todo-add>
<todo-list :list_tasks="tasks"><todo-list>
</div>`,
components:{todoAdd,todoList}
})
</script>
</head>
<body>
<div id="app">
<to-do></to-do>
</div>
<script>
new Vue({
el:"#app"
})
</script>
</body>
</html>
为了便于理解,用图的方式解释一下上面代码:
按照上图红色标记序号详细讲一下:
为了方便理解,我们称todo为爷爷组件,todoList为父亲组件,todoItem为儿子组件
- 爷爷组件todo中的模板代码(template)中——:list_tasks=“tasks”,引号中的tasks就是爷爷组件的数据tasks,等号左边的list_tasks就是给到父亲组件todoList中数据的名称。用上边的比喻来解释就是,大人把这些糖叫做奶糖,小孩把这些糖叫做大白兔奶糖,虽然叫法不一样,但指的是一个东西!(也可以叫一样的名字,即 :tasks=“tasks”,但需要知道等号两边的tasks意义不同)
- 父亲组件todoList中——props:[“list_tasks”],这时父亲组件内部有了list_tasks这个变量。我们需要注意的是:在爷爷组件todo中tasks是一个数组,数组是引用类型,所以tasks变量中存的是一个地址,我们假设这个地址是0x1234,那么我们在父亲组件todoList中拿到的list_tasks也存着一个地址,而且这个地址也是0x1234。这也就表示,我们从爷爷组件中拿到的数据不是副本,tasks和list_tasks指向同一个地址
- 父亲组件todoList的模板代码中使用了list_tasks变量
- 儿子组件todoItem也需要使用爷爷的tasks,但是他不能问爷爷要啊,他就问他爹要啦,他爹也有啊,但名字叫做list_tasks,儿子把要过来的数据又重新起名叫做item_tasks,就可以通过v-for指令在页面生成我们想要得到的事务列表
- 儿子组件todoItem通过props拿到数据item_tasks,item_tasks同样指向地址0x1234
- 触发事件后,儿子组件todoItem调用自己的方法del,对item_tasks中的数据进行了删除。因为tasks、list_tasks和item_tasks都指向一个地址,触发事件影响的也就是爷爷组件todo中的数据。
子组件todoAdd的道理也是如此,就不重复说啦~
这里再说一下绿色标记处的代码:
- 儿子组件todoItem要使用父亲组件todoList的 t 和 i 变量,父亲组件中用代码——:t=“t” :i=“i” 动态的将数据绑定到儿子组件的props中,等号两边的 t 和 i 意义不同,同上面所说。
3. 子组件与父组件通信
学习了以上内容,子组件中内部的方法可以通过以上方式使用到父组件中的数据。但是上面栗子中add()方法、del()方法散落在各个子组件内部,如果有更多的子组件和方法,我们后期是难以维护的。所以,如果我们将所有的方法都放在父组件中,子组件需要使用的话则通过一定方式触发父组件内的方法。这就是子组件与父组件通信。
3.1 子与父通信
- 父组件是使用props传递数据传递给子组件,但如果子组件要把数据传递回去,应该怎么做?那就是自定义事件!
- 使用$on(事件名称)监听事件
- 使用$emit(事件名称)触发时间
- 注意,不能用$on侦听子组件抛出的事件,而必须在模板里直接使用v-on绑定(v-on简写@)
- 父组件内:给子组件绑定一个事件
<child @事件名称="方法名称(参数)"></child>
- 子组件内:在子组件内出发父组件指定的事件
this.$emit("事件名称")
3.2 代码实现子与父及详细说明
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>子父通信</title>
<script src="../js/vue.js"></script>
</head>
<script>
var todoAdd={
template:`
<div>
<input type="text" v-model="task"><button @click="add">+</button>
</div>`,
data(){
return{task:""}
},
methods:{
add(){
this.$emit("add",this.task);
this.task="";
}
}
}
var todoItem={
props:["t","i"],
template:`
<li>
{{i+1}}-{{t}}<a href="javascript:;" @click="del">×</a>
</li>`,
methods:{
del(){this.$emit("del");}
}
};
var todoList={
props:["tasks"],
template:`
<ul>
<todo-item @del="del(i)" v-for="(t,i) of tasks" :t="t" :i="i"></todo-item>
</ul>`,
methods:{
del(i){this.$emit("del",i);}
},
components:{
todoItem
}
};
Vue.component("to-do",{
template:`
<div>
<h1>待办事项列表</h1>
<todo-add @add="add"></todo-add>
<todo-list @del="del" :tasks="tasks"><todo-list>
</div>`,
data(){
return{
tasks:["吃饭","睡觉","打豆豆"]
}
},
methods:{
add(task){
this.tasks.push(task);
},
del(i){
this.tasks.splice(i,1);
}
},
components:{
todoAdd,
todoList
}
})
</script>
<body>
<div id="app">
<to-do></to-do>
</div>
<script>
new Vue({
el:"#app"
})
</script>
</body>
</html>
同样先用图解释一下上述代码:
先看红色标记:
- 当点击button按钮时,子组件todoAdd触发自己的方法add()
- 子组件todoAdd可以使用$emit触发父组件todo的自定义事件,自定义事件名称为add,this.task是参数
- 父组件todo的模板代码(template)中定义了自定义事件(@add=“add”),等号左边是事件名,右边引号中是所触发的父组件中的方法名
- 父组件todo中的add方法被执行,页面上添加了新的事物
再看蓝色标记:
为了方便理解,我们称todo为爷爷组件,todoList为父亲组件,todoItem为儿子组件
- 当点击×时,儿子组件todoItem触发了自己的方法del()
- 儿子组件todoItem使用$emit触发父亲组件todoList的自定义事件,自定义事件名称为del
- 父亲组件todoList的模板代码(template)中定义了自定义事件(@del=“del(i)”),等号左边是事件名,右边引号中是所触发的父亲组件todoList中的方法名。
- 父亲组件todoList中的方法del(i)中有一个参数i,i为当前要删除的tasks数组中元素的下标(tasks数组在爷爷组件的data属性中),该方法同样使用$emit触发了爷爷组件todo的自定义事件,自定义事件名称为del,i为参数(注意:不是this.i)
- 爷爷组件todo的模板代码(template)中定义了自定义事件(@del=“del”),等号左边是事件名,右边引号中是所触发的爷爷组件中的方法名
- 爷爷组件todo中的del方法被执行,选中的事务将被删除
本文地址:https://blog.csdn.net/weixin_44410783/article/details/113960268