vue组件通信:父与子、子与父

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为儿子组件

  1. 爷爷组件todo中的模板代码(template)中——:list_tasks=“tasks”,引号中的tasks就是爷爷组件的数据tasks,等号左边的list_tasks就是给到父亲组件todoList中数据的名称。用上边的比喻来解释就是,大人把这些糖叫做奶糖,小孩把这些糖叫做大白兔奶糖,虽然叫法不一样,但指的是一个东西!(也可以叫一样的名字,即 :tasks=“tasks”,但需要知道等号两边的tasks意义不同)
  2. 父亲组件todoList中——props:[“list_tasks”],这时父亲组件内部有了list_tasks这个变量。我们需要注意的是:在爷爷组件todo中tasks是一个数组,数组是引用类型,所以tasks变量中存的是一个地址,我们假设这个地址是0x1234,那么我们在父亲组件todoList中拿到的list_tasks也存着一个地址,而且这个地址也是0x1234。这也就表示,我们从爷爷组件中拿到的数据不是副本,tasks和list_tasks指向同一个地址
  3. 父亲组件todoList的模板代码中使用了list_tasks变量
  4. 儿子组件todoItem也需要使用爷爷的tasks,但是他不能问爷爷要啊,他就问他爹要啦,他爹也有啊,但名字叫做list_tasks,儿子把要过来的数据又重新起名叫做item_tasks,就可以通过v-for指令在页面生成我们想要得到的事务列表
  5. 儿子组件todoItem通过props拿到数据item_tasks,item_tasks同样指向地址0x1234
  6. 触发事件后,儿子组件todoItem调用自己的方法del,对item_tasks中的数据进行了删除。因为tasks、list_tasks和item_tasks都指向一个地址,触发事件影响的也就是爷爷组件todo中的数据。

子组件todoAdd的道理也是如此,就不重复说啦~

这里再说一下绿色标记处的代码:

  • 儿子组件todoItem要使用父亲组件todoList的 ti 变量,父亲组件中用代码——: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>

同样先用图解释一下上述代码:
在这里插入图片描述
先看红色标记:

  1. 当点击button按钮时,子组件todoAdd触发自己的方法add()
  2. 子组件todoAdd可以使用$emit触发父组件todo的自定义事件,自定义事件名称为add,this.task是参数
  3. 父组件todo的模板代码(template)中定义了自定义事件(@add=“add”),等号左边是事件名,右边引号中是所触发的父组件中的方法名
  4. 父组件todo中的add方法被执行,页面上添加了新的事物

再看蓝色标记:
为了方便理解,我们称todo为爷爷组件,todoList为父亲组件,todoItem为儿子组件

  1. 当点击×时,儿子组件todoItem触发了自己的方法del()
  2. 儿子组件todoItem使用$emit触发父亲组件todoList的自定义事件,自定义事件名称为del
  3. 父亲组件todoList的模板代码(template)中定义了自定义事件(@del=“del(i)”),等号左边是事件名,右边引号中是所触发的父亲组件todoList中的方法名。
  4. 父亲组件todoList中的方法del(i)中有一个参数i,i为当前要删除的tasks数组中元素的下标(tasks数组在爷爷组件的data属性中),该方法同样使用$emit触发了爷爷组件todo的自定义事件,自定义事件名称为del,i为参数(注意:不是this.i)
  5. 爷爷组件todo的模板代码(template)中定义了自定义事件(@del=“del”),等号左边是事件名,右边引号中是所触发的爷爷组件中的方法名
  6. 爷爷组件todo中的del方法被执行,选中的事务将被删除

猜你喜欢

转载自blog.csdn.net/weixin_44410783/article/details/113960268