doina

一个小菜鸟运维工程师.

vue 组件化开发 父子组件传递数据

[toc]

介绍

  • 在开发中,往往一些数据确实需要从上层传递到下层:
    • 比如在一个页面中,我们从服务器请求到了很多的数据。
    • 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
    • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
  • 如何进行父子组件间的通信呢?Vue官方提到:
    • 通过props向子组件传递数据
    • 通过事件向父组件发送消息

父组件传递数据给子组件

《vue 组件化开发 父子组件传递数据》

  • 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>

<!--vue操作的实例-->
<div id="app">
<!--使用组件, 在组件中绑定两个属性, 这两个属性必须是在组件注册的时候, 通过props传递进来的-->
<!--props中的变量使用驼峰式写法, 这里在这里绑定时, 需要在大写字母前用-连起来,且大写要转换为小写-->
  <cpn :c-title="title" :cdevops="devops" :my-blog-address="blogAddress"></cpn>
</div>

<!--在模板中使用传递进来的属性(变量)-->
<template id="cpn">
  <div>
    <!--模板里引用的变量要和传递进来v-bind绑定的属性名称一致-->
    <!--cname并未在实例中传递,而是在组件中定义了默认值,所以可以直接使用-->
    <h2>{{cTitle}}</h2>
    <h4>{{cname}}</h4>
    <h4>{{myBlogAddress}}</h4>
    <ul>
      <li v-for="item in cdevops">{{item}}</li>
    </ul>
  </div>
</template>

<script src="../../js/vue.v2.6.12.js"></script>
<script>
  // 父组件传给子组件, 通过props, 值有两种:
    // 字符串数组,数组中的字符串就是传递时的名称。
    // 对象,对象可以设置传递时的类型,也可以设置默认值等

  // 子组件传给父组件, 通过自定义事件
  Vue.component(
      'cpn', {
        template: '#cpn',
        // 第一种: 传递一个数组
        // props: ['ctitle', 'cdevops']

        // 第二种: 传递对象, 名称不能使用驼峰式, 如cTitle, 会出问题. 因为浏览器在html标签里不区分大小写,
        // 如果一定要用驼峰式写法, 可以在v-bind绑定属性时, 使用-来连接
        props: {
          // 可以直接指定传递的类型
          cTitle: String,

          // 也可以定义传递类型,默认值,可是否必传递
          cdevops: {
            // 指定类型
            type: Array,
            // 指定为必须传递的值
            request: true,
            // 定义默认值, 如果默认值是数组或者对象, 那么必须是一个函数, 使用return返回
            default: function () {
              return ['keepalived', 'nginx', 'zabbix']
            }
          },
          cname: {
            type: String,
            default: 'i\'m baiyongjie.'
          },
          myBlogAddress: {
            type: String,
            default: 'https://baiyongjie.com'
          }
        }

      }
  )


  const app = new Vue({
    el: '#app',
    data: {
      title: '我是一个标题',
      devops: ['python', 'linux', 'javaScript'],
      blogAddress: 'https://www.baiyongjie.com'
    }
  })
</script>
</body>
</html>

《vue 组件化开发 父子组件传递数据》
《vue 组件化开发 父子组件传递数据》

子组件传递数据给父组件

案例一: 点击传递对象

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<!--父组件模板-->
<div id="app">
  <!--监听son-click自定义事件, 监听到执行sonClick函数-->
  <!--这里因为在字幕版中定义了点击事件调用了带参数的btnClick(item), 后面又在btnClick方法中将item通过emit传递到父组件事件中,所以这里不需要带参数就能在父组件中输出-->
  <son @son-click="sonClick"></son>
</div>

<!--子组件模板-->
<template id="son">
  <div>
    <button v-for="item in categories" @click="btnClick(item)">
      {{item.name}}
    </button>
  </div>
</template>

<script src="../../js/vue.v2.6.12.js"></script>
<script>
  // 子组件传给父组件, 通过自定义事件
  // 四步:1.子组件加事件 2. 子组件this.$emit('事件名字', 数据) 3. 父组件加事件 4. 父组件执行事件函数(数据)

  // 定义子组件
  Vue.component('son', {
    template: '#son',
    data: function () {
      return {
        // 分类列表
        categories: [
          {'id': 1, name: '热门推荐'},
          {'id': 2, name: '手机数码'},
          {'id': 3, name: '日用物品'},
          {'id': 4, name: '电脑办公'},
        ]
      }
    },
    methods: {
      // 点击后执行的函数
      btnClick(item) {
        // 发送一个自定义事件, 事件名称是son-click, 参数是item
        this.$emit('son-click', item)
        // 点击button打印
        console.log('子组件: id', item.id, '名称:', item.name, '完整对象: ', item);
      }
    }
  });


  // 父组件
  const app = new Vue({
    el: '#app',
    data: {
      message: 'baiyongjie'
    },
    component: {
      son
    },
    methods: {
      // 监听到事件后执行的函数
      sonClick(item) {
        // 打印传递过来的数据
        console.log('父组件id', item.id, '名称:', item.name, '完整对象: ', item)
      }
    }
  })
</script>
</body>
</html>

《vue 组件化开发 父子组件传递数据》

案例二: 计数器

《vue 组件化开发 父子组件传递数据》

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  当前计数: {{total}}
  <!--监听子组件的自定义事件, 监听到了执行changeTotal函数-->
  <cpn @decremenet="changeTotal" @incremenet="changeTotal"></cpn>
</div>

<!--子组件cpn的模板-->
<template id="cpn">
  <div>
    <!--点击加号时, 调用increment函数-->
    <button @click="incremenet">+</button>
    <!--点击减号时,调用decrement函数-->
    <button @click="decremenet">-</button>
  </div>
</template>

<script src="../../js/vue.v2.6.12.js"></script>
<script>

  // 定义cpn子组件
  const cpn = {
    template: '#cpn',
    data() {
      return {
        counter: 0
      }
    },
    methods: {
      // 进行结果相加, 然后传给父组件
      incremenet() {
        this.counter++
        this.$emit('incremenet', this.counter)
      },
      // 进行结果相减, 然后传给父组件
      decremenet() {
        this.counter--
        this.$emit('decremenet', this.counter)
      }
    }
  }

  const app = new Vue({
    el: '#app',
    data: {
      total: 0,
    },
    // 注册子组件
    components: {
      cpn
    },
    methods: {
      // 修改total为传递过来的值
      changeTotal(counter) {
        this.total = counter
        console.log('当前计数:', counter);
      }
    }
  })
</script>
</body>
</html>

《vue 组件化开发 父子组件传递数据》

父子组件双向通信

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <cpn :number1="num1"
       :number2="num2"
       @num1change="num1change"
       @num2change="num2change"/>
</div>

<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dnumber1}}</h2>
    <!--<input type="text" v-model="dnumber1">-->
    <input type="text" :value="dnumber1" @input="num1Input">
    <h2>props:{{number2}}</h2>
    <h2>data:{{dnumber2}}</h2>
    <!--<input type="text" v-model="dnumber2">-->
    <input type="text" :value="dnumber2" @input="num2Input">
  </div>
</template>

<script src="../../js/vue.v2.6.12.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1: 1,
      num2: 0
    },
    methods: {
      num1change(value) {
        this.num1 = parseFloat(value)
      },
      num2change(value) {
        this.num2 = parseFloat(value)
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        props: {
          number1: Number,
          number2: Number
        },
        data() {
          return {
            dnumber1: this.number1,
            dnumber2: this.number2
          }
        },
        methods: {
          num1Input(event) {
            // 1.将input中的value赋值到dnumber中
            this.dnumber1 = event.target.value;

            // 2.为了让父组件可以修改值, 发出一个事件
            this.$emit('num1change', this.dnumber1)

            // 3.同时修饰dnumber2的值
            this.dnumber2 = this.dnumber1 * 100;
            this.$emit('num2change', this.dnumber2);
          },
          num2Input(event) {
            this.dnumber2 = event.target.value;
            this.$emit('num2change', this.dnumber2)

            // 同时修饰dnumber2的值
            this.dnumber1 = this.dnumber2 / 100;
            this.$emit('num1change', this.dnumber1);
          }
        }
      }
    }
  })
</script>

</body>
</html>
点赞

发表评论

邮箱地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据