我是靠谱客的博主 执着花瓣,最近开发中收集的这篇文章主要介绍Vue学习总结(十三)——父子组件通信,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1、父子组件的通信

在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的。

但是,在开发中,往往一些数据确实需要从上层传递到下层:

  • 比如在一个页面中,我们从服务器请求到了很多的数据。
  • 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
  • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。

Vue官方关于父子组件间的通信提到了:

  • 通过prop向子组件传递数据。
  • 通过事件向父组件发送消息。

 真实的开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的。

 2、父传子--props基本用法

在组件中,使用选项props来声明需要从父级接收到的数据。

props的值有两种方式:

  • 方式一:字符串数组,数组中的字符串就是传递时的名称。
  • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

(1)当props是数组的时候的用法:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <h2>
      <cpn :cmovies="movies" :cmessage="message"></cpn>
    </h2>
  </div>
  <template id='cpn'>
    <div>
      <ul>
        <li v-for="item in cmovies">{{item}}</li>
      </ul>
      <div>{{cmessage}}</div>
    </div>
  </template>
  <script src="../../js/vue.js"></script>
  <script>
    // 父传子:使用props
    const cpn = {
      template: '#cpn',
      props: ['cmovies', 'cmessage']
    }
    const app = new Vue({
      el: '#app',
      data: {
        message: '你好呀',
        movies: ['海王', '海贼王', '海尔兄弟']
      },
      components: {
        cpn
      }
    })
  </script>
</body>
</html>

(2)当props是对象的时候,props数据验证

当需要对props进行类型等验证时,就需要对象写法了。

验证都支持哪些数据类型呢?

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

当我们有自定义构造函数时,验证也支持自定义的类型。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <h2>
      <!--当没有传message的时候,会使用默认值-->
      <cpn :cmovies="movies"></cpn>
    </h2>
  </div>
  <template id='cpn'>
    <div>
      <ul>
        <li v-for="item in cmovies">{{item}}</li>
      </ul>
      <div>{{cmessage}}</div>
    </div>
  </template>
  <script src="../../js/vue.js"></script>
  <script>
    // 父传子:使用props
    const cpn = {
      template: '#cpn',
      // props: ['cmovies', 'cmessage', 'propB', 'propC', 'propD']
      props: {
        // 1.类型限制
        // cmovies: Array,
        // cmessage: String
        // 2.提供一些默认值
        cmessage: {
          type: String,
          default: 'hello world',
          // 3、当required是true的时候必须传这个值
          // required: true
        },
        cmovies: {
          type: Array,
          // Vue2.5.17以下不会报错,但是以上版本会报错,因为类型是对象或者数组时,默认值必须为一个函数
          // default: [],
          default() {
            return []
          }
        },
        // 4、多个可能的类型
        propB: [String, Number],
        // 5、自定义验证函数
        propC: {
          validater(value) {
            return ['success', 'warning', 'error'].indexOf(value) !== -1
          }
        },
        // 6、自定义类型
        propD: Person
      }
    }
    const app = new Vue({
      el: '#app',
      data: {
        message: '你好呀',
        movies: ['海王', '海贼王', '海尔兄弟']
      },
      components: {
        cpn
      }
    })
    // 自定义一个Person类型
    function Person() {
      this.message='111'
    }
  </script>
</body>
</html>

当props对象的required值为true的时候,必须要传这个值,否则会报错,报错信息见下图:

(3)props 驼峰标识

         注意:v-bind那里不支持驼峰,所以不能使用cInfo,而是要c-info

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <!--v-bind这里不支持驼峰,所以不能使用cInfo,而是要c-info-->
    <cpn :c-info="info" :child-my-message="message"></cpn>
  </div>
  <template id='cpn'>
    <div>
      <div>{{cInfo}}</div>
      <div>{{childMyMessage}}</div>
    </div>
  </template>
  <script src="../../js/vue.js"></script>
  <script>
    // 父传子:使用props
    const cpn = {
      template: '#cpn',
      props: {
        cInfo: {
          type: Object,
          default() {
            return {}
          }
        },
        childMyMessage: {
          type: String,
          default: ''
        }
      }
    }
    const app = new Vue({
      el: '#app',
      data: {
        info: {
          name: 'why',
          age: 18,
          height: 1.88
        },
        message: 'hello world'
      },
      components: {
        cpn
      }
    })
    // 自定义一个Person类型
    function Person() {
      this.message='111'
    }
  </script>
</body>
</html>

 3、子传父--自定义事件

什么时候需要自定义事件呢?

  •  当子组件需要向父组件传递数据时,就要用到自定义事件了
  •  我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。

 自定义事件流程:

  •  在子组件中,通过$emit()来触发事件。
  •  在父组件中,通过v-on来监听子组件事件。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <!--父组件模板-->
  <div id="app">
    <!--接收子组件发送的事件-->
    <cpn @emit-categories="getCategories"></cpn>
  </div>
  <!--子组件模板-->
  <template id='cpn'>
    <div>
      <button v-for="item in categories" @click="emitCategories(item)">{{item.name}}</button>
    </div>
  </template>
  <script src="../../js/vue.js"></script>
  <script>
    const cpn = {
      template: '#cpn',
      data() {
        return {
          categories: [
          {id: "111", name: "家用电器"},
          {id: "222", name: "美容美发"},
          {id: "333", name: "小吃零食"},
          {id: "444", name: "柴米粮油"}
          ]
        }
      },
      methods: {
        // 子组件传递给父组件,自定义事件
        emitCategories(item) {
          this.$emit('emit-categories', item)
        }
      }
    }
    const app = new Vue({
      el: '#app',
      data: {
        message: 'hello world'
      },
      components: {
        cpn
      },
      methods: {
        getCategories(item) {
          console.log('begin get category', item)
        }
      }
    })
  </script>
</body>
</html>

4、父子组件通信--结合双向绑定

不正确的用法:

通过v-model确实是可以绑定到props里面的两个属性,但是可以看到程序有报错。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <!--父组件模板-->
  <div id="app">
    <!--接收子组件发送的事件-->
    <cpn :number1="num1" :number2="num2"></cpn>
  </div>
  <!--子组件模板-->
  <template id='cpn'>
    <div>
      <h2>{{number1}}</h2>
      <!--不建议这么使用,会报错。避免直接改变props的值,尽量由父组件去修改这个值。-->
      <input type="text" v-model="number1"></input>
      <h2>{{number2}}</h2>
      <input type="text" v-model="number2"></input>
    </div>
  </template>
  <script src="../../js/vue.js"></script>
  <script>
    const cpn = {
      template: '#cpn',
      props: {
        number1: Number,
        number2: Number
      },
      data() {
        return {
        }
      }
    }
    const app = new Vue({
      el: '#app',
      data: {
        num1: 1,
        num2: 0
      },
      components: {
        cpn
      }
    })
  </script>
</body>
</html>

报错信息:避免直接改变props的值,尽量由父组件去修改这个值。

正确的用法:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <h2>父组件的num1:{{num1}}</h2>
    <!--组件也可以使用单标签-->
    <cpn :number1="num1" :number2="num2" @num1change="num1change"/>
  </div>
  <template id='cpn'>
    <div>
      <h2>props:{{number1}}</h2>
      <h2>data:{{dataNumer1}}</h2>
      <!--不建议这么使用,会报错。避免直接改变props的值,尽量由父组件去修改这个值。-->
      <!-- <input type="text" v-model="number1"> -->
      <!--建议绑定到data或者computed计算属性上-->
      <!-- <input type="text" v-model="dataNumer1"> -->
      <input type="text" :value="dataNumer1" @input="number1Input">
      <h2>props:{{number2}}</h2>
      <h2>data:{{dataNumer2}}</h2>
      <input type="text" :value="dataNumer2" @input="dataNumer2=$event.target.value">
    </div>
  </template>
<script src="../vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1: 1,
      num2: 2
    },
    methods: {
      num1change(value) {
        this.num1 = parseInt(value)
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        props: {
          number1: Number,
          number2: Number
        },
        data() {
          return {
            dataNumer1: this.number1,
            dataNumer2: this.number2
          }
        },
        methods: {
          number1Input(event) {
            // 1、将input中的value赋到dnumber中
            this.dataNumer1 = event.target.value;
            // 2、同时发送一个事件,让父组件修改值
            this.$emit('num1change', this.dataNumer1)
            // 3、同时修改dnumber2的值, 变为number1的100倍
            this.dataNumer2 = this.dataNumer1 * 100
            // 4、发送一个事件,让父组件修改值
            this.$emit('num2change', this.dataNumer2)
          }
        }
      }
    }
  })
</script>
</body>
</html>

5、父访问子

(1)父组件访问子组件的方式: $children

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。

父组件访问子组件:使用$children或$refs(reference引用)

我们先来看下$children的访问

this.$children是一个数组类型,它包含所有子组件对象。

我们这里通过一个遍历,取出所有子组件的message状态。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
    <button @click="btnClick">按钮</button>
  </div>
  <template id='cpn'>
    <div>我是子组件</div>
  </template>
<script src="../vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    methods: {
      btnClick() {
        // 1.$children
        console.log (this.$children)
        for(let c of this.$children) {
          console.log(c.name)
          c.showMessage()
        }
        // 在实际开发中一般不会这么使用,因为在实际开发中组件顺序不固定,不能通过坐标来获取,如果有新需求在多个组件中间加了一个组件,就会导致问题
        this.$children[1].showMessage()
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是子组件的name'
          }
        },
        methods: {
          showMessage() {
            console.log('子组件show message.')
          }
        }
      }
    }
  })
</script>
</body>
</html>

(2) 父组件访问子组件的方式: $refs

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn ref="cpn1"></cpn>
    <button @click="btnClick">按钮</button>
  </div>
  <template id='cpn'>
    <div>我是子组件</div>
  </template>
<script src="../vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    methods: {
      btnClick() {
        //2.refs属性  对象属性,默认refs中是空对象
        console.log(this.$refs)
        // 需要在组件上加个ref属性,ref="cpn1",则可以通过ref属性给的名称cpn1拿到组件对象
        this.$refs.cpn1.showMessage()
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是子组件的name'
          }
        },
        methods: {
          showMessage() {
            console.log('子组件show message.')
          }
        }
      }
    }
  })
</script>
</body>
</html>

6、子访问父

(1)子组件访问父组件:使用$parent

(在开发里不建议这样使用,因为一旦这样使用,子组件就不够独立了。在开发中,我们会把很多东西抽成一个个组件,一方面是为了组件思想,但是主要是为了复用性。这样用则复用性不是很强,因为在不同的父组件中使用,可能不一定会有要获取的内容,会导致和父组件耦合性过高。)

(2)直接访问根组件:$root

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">
    <cpn></cpn>
  </div>
  <template id='cpn'>
    <div>
      我是子组件
      <button @click="btnClick">按钮</button>
      <ccpn></ccpn>
    </div>
  </template>
  <template id='ccpn'>
    <div>
      我是孙子组件
      <button @click="btnClick">按钮</button>
    </div>
  </template>
<script src="../vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '根组件,您好呀'
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是cpn组件的name'
          }
        },
        methods: {
          btnClick() {
            // 1.访问父组件 $parent
            console.log(this.$parent)
          }
        },
        components: {
          ccpn: {
            template: '#ccpn',
            methods: {
              btnClick() {
                // 1.访问父组件 $parent, 在开发里不建议这样使用,因为一旦这样使用,子组件就不够独立了。
                // 在开发中,我们会把很多东西抽成一个个组件,一方面是为了组件思想,但是主要是为了复用性。
                // 这样用则复用性不是很强,因为在不同的父组件中使用,可能不一定会有要获取的内容,会导致和父组件耦合性过高。
                console.log(this.$parent)
                console.log(this.$parent.name)
                // 2.访问根组件$root
                console.log(this.$root)
                console.log(this.$root.message)
              }
            }
          }
        }
      }
    }
  })
</script>
</body>
</html>

执行结果如下:

 

最后

以上就是执着花瓣为你收集整理的Vue学习总结(十三)——父子组件通信的全部内容,希望文章能够帮你解决Vue学习总结(十三)——父子组件通信所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部