43

Vue 组件:父子组件通信

 4 years ago
source link: https://www.tuicool.com/articles/jEna636
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Vue%20%E7%BB%84%E4%BB%B6%EF%BC%9A%E7%88%B6%E5%AD%90%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1-0.png

子组件是不能直接访问父组件中的数据的,但有时候父子组件之间需要进行数据交互,这就涉及到了父子组件通信的问题。简单来说,父组件向子组件通信是通过 props 进行的,而子组件向父组件通信则是通过 自定义事件 进行的。

我们用一个简易的 todolist 案例来理解这两个过程。

todolist 案例

1.父传子

假定我们现在有一个需求:在输入框中输入待办事项,点击添加按钮可以将事项展现在页面上。如下图所示:

Vue%20%E7%BB%84%E4%BB%B6%EF%BC%9A%E7%88%B6%E5%AD%90%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1-1.gif

分析:页面分为两个部分,一部分是操作区,一部分是展示区。展示区可以用 li ,那么这些 li 就可以看作是可复用的子组件,而其它部分则看作是父组件,我们在父组件中操作,结果却是在子组件中显示的,所以这里是父组件向子组件通信的问题。

首先将根实例作为父组件,然后注册一个子组件,写好大概的结构:

<!--父组件模板-->
<div id="app">
  <input type="text" v-model="newvalue">
  <button @click="addItem">点击添加</button>
  <cpn></cpn>
</div>

<!--子组件模板-->
<template id="cpn">
  <div>
    <ul></ul>
  </div>
</template>
const cpn = {
  template:"#cpn"
}
const app  = new Vue({
  el:'#app',
  data:{
    newvalue:'',
    list:[]
  },
  methods:{
    addItem(){
      this.list.push(this.newvalue);
      this.newvalue = ''
    }
  },
  components:{
    cpn
  }
})

表单元素需要双向数据绑定,所以我们这里使用 v-model="newvalue"newvalue 初始化的时候是空字符串,后面就代表我们输入的待办事项,监听按钮的点击事件并把它 push 到空数组中,之后为了用户操作方便(不需要手动删除输入框内容),我们再把 newvalue 置空。这时候,父组件的操作已经完成了,接下来要把数据传递给子组件并显示出来。

list 是要传递的数据,首先把它交付给自定义属性 list2 ,对于子组件,它需要通过 props (可以是数组或者对象)去接收。之后,我们在子组件模板中进行列表的遍历,遍历的对象就是 list2 数组。

代码如下:

<!--父组件模板-->
<div id="app">
  <input type="text" v-model="newvalue">
  <button @click="addItem">点击添加</button>
  <cpn v-bind:list2="list"></cpn>
</div>

<!--子组件模板-->
<template id="cpn">
  <div>
    <ul>
      <li v-for="(item,index) in list2">{{item}}</li>
    </ul>
  </div>
</template>
const cpn = {
  template:"#cpn",
  props:["list2"]
}
const app  = new Vue({
  el:'#app',
  data:{
    newvalue:'',
    list:[]
  },
  methods:{
    addItem(){
      this.list.push(this.newvalue);
      this.newvalue = ''
    }
  },
  components:{
    cpn
  }
})

2.子传父

作为一个 todolist,除了添加之外应该还可以删除,所以接下来的需求是点击待办事项可以进行删除。如下图所示:

Vue%20%E7%BB%84%E4%BB%B6%EF%BC%9A%E7%88%B6%E5%AD%90%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1-2.1.gif

分析:因为这里子组件只负责点击操作,实际的删除需要父组件自己去操作数据(类似于子组件打个电话告诉父组件该删除哪个东西了),所以这里涉及到了子组件向父组件通信的问题。

这里首先还是监听待办事项的点击事件,点击后调用函数,之后执行函数中的 this.$emit('eventName',args) ,作用是由实例向外触发一个自定义事件(参数可选),之后父组件再监听这个自定义事件,一旦监听到事件就调用父组件(即根实例)下挂载的方法,来删除待办事项。

代码如下:

<div id="app">
  <input type="text" v-model="newvalue">
  <button @click="addItem">点击添加</button>
  <!-- 3.父组件监听到自定义事件 receive 后,调用 deleteItem -->
  <cpn v-bind:list2="list" @receive="deleteItem"></cpn>
</div>
<template id="cpn">
  <div>
    <ul>
      <!--1.监听点击事件-->
      <li v-for="(item,index) in list2" @click="remove(index)">
        {{item}}
      </li>
    </ul>
  </div>
</template>
const cpn = {
  template:"#cpn",
  props:["list2"],
  methods:{
    remove(index){
      this.$emit("receive",index)  // 2.向外触发自定义事件 receive
    }
  }
}
const app  = new Vue({
  el:'#app',
  data:{
    newvalue:'',
    list:[]
  },
  methods:{
    addItem(){
      this.list.push(this.newvalue);
      this.newvalue = ''
    },
    // 4.执行 deleteItem 删除数组元素
    deleteItem(index){
      this.list.splice(index,1)
    }
  },
  components:{
    cpn
  }
})

props

前面使用的 props 是数组,实际开发中用的更多的其实是对象。作为对象的 props 可以配置高级选项,如类型检测、自定义校验和设置默认值等。

假定上面的子组件还接受了其它数据:

const cpn = {
  template:"#cpn",
  methods:{
    .......
  },
  props:{
    propA:Array, // 接受的 propA 类型必须是数组。也可以指定自定义类型
    propB:{String,Number}, // propB 必须是字符串或者数字
    propC:{
      type:String,
      required:true    // 必须接受 propC,否则报错
    },
    propD:{
      type:String,
      default:"demo"   // 没有接受到 propD 时使用这个默认值
    },
    propD:{
      type:Object,
      default:function(){   // 数组或对象指定默认值时必须是一个函数
        return {message:"Hello"}
      }
    },
    propE:{
      validator(value){
        // 这个值必须匹配下列字符串中的一个
        return {'aaa','bbb'}.indexOf(value) !== -1
      }
    }
  }
}

到这里的话,父子组件之间的通信就已经结束了。使用 Vue 的时候应该避免直接去操作 dom,而是通过数据的改变让页面自动变化。

  • 父组件向子组件传值:在父组件中通过 v-on 绑定自定义属性以存储父组件数据,然后子组件通过 props 接收,这样就可以拿到父组件中的数据;
  • 子组件向父组件通信:子组件监听到事件后,通过 $emit 向外触发自定义事件,父组件监听到该事件后操作数据。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK