Vue.js-组件通信

1.1 父组件向子组件传递

真实的开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的。
子组件是不能引用父组件或者Vue实例的数据的。这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)

props基本用法

  • 在组件中,使用选项props来声明需要从父级接收到的数据
  • props的值有两种方式
    • 方式一:字符串数组,数组中的字符串就是传递时的名称。
    • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
  • props传递案例

Vue.js-组件通信

代码示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>父组件向子组件传递参数</title>
</head>
<body>
    <div id="app">
	  <!--3.使用组件-->
	  <!--绑定属性-->
      <cpn1 :cmessage="message" :cstarts="starts"></cpn1>
    </div>

    <!--创建模板-->
    <template id="cpn">
      <div>
        <ul>
          <li v-for="item in cstarts">{{item}}</li>
        </ul>
        <h3>{{cmessage}}</h3>
      </div>
    </template>

    <script src="../js/vue.js"></script>
    <script>
      const cpn1 = {
        // 定义模板
        template: '#cpn',
        // 父组件向子组件传递内容(父传子: props)
        props: {
          // 类型限制,提供默认值,和及其必传值
          cmessage: {
            type: String,
            dafault: 'Curry',
            required: true
          },

          // 类型是数组或者对象类型时候,默认值必须是一个函数
          cstarts: {
            type: Array,
            default(){
              return []
            }
          }
        }
      }
      // 创建对象
      const app = new Vue({
        // 挂载要管理的元素
        el: '#app',

        // 定义数据
        data: {
          message: 'hello world!',
          starts: ['kobe', 'Jmaes', 'Curry', 'Duncan']
        },

        // 注册组件
        components: {
          cpn1
        }
      })
  </script>
</body>
</html>

执行结果

Vue.js-组件通信

1.2 props驼峰标识问题

注意: props传递中的不支持驼峰标识!!!

 <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>props中的驼峰标识</title>
</head>
<body>
    <div id="app">
	  <!--这里不支持驼峰标识-->
      <cpn1 :c-info="info" :child-my-message="message"></cpn1>
    </div>

    <!--定义模板-->
    <template id="cpn">
	  <!--定义子组件模板的时候,要用外层的div将其包裹起来-->
      <div>
        <h2>{{cInfo}}</h2>
        <h2>{{childMyMessage}}</h2>
      </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
      const cpn1 = {
        template: '#cpn',
        props: {
          cInfo: {
            type: Object,
            default() {
              return {}
            }
          },
          childMyMessage: {
            type: String,
            default: ''
          }
        }
      }

      // 创建对象
      const app = new Vue({
        // 挂载要管理的元素
        el: '#app',

        // 定义数据
        data: {
          info: {
            name: 'guardWhy',
            age: 26,
            height: 1.71
          },
          message: 'curry'
        },
        // 注册组件
        components:{
          cpn1
        }
      })
  </script>
</body>
</html>

执行结果

Vue.js-组件通信

1.3 子级向父级传递(自定义事件)

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

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

自定义事件的流程

在子组件中,通过$emit()来触发事件。
在父组件中,通过v-on来监听子组件事件。

Vue.js-组件通信

代码示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>子传父自定义事件</title>
</head>
<body>
<!--父组件模板-->
    <div id="app">
      <!--父组件监听(v-on)事件-->
      <cpn1 @item-click="cpnClick"></cpn1>
    </div>


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

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

      // 子组件
      const cpn1 = {
        // 定义模板
        template: '#cpn',
        data(){
          return{
            categories: [
              {id: 'aaa', name: '热门推荐'},
              {id: 'bbb', name: '手机数码'},
              {id: 'ccc', name: '家用家电'},
              {id: 'ddd', name: '电脑办公'},
            ]
          }
        },

        methods: {
          btnClick(item){
            // console.log(item);
            // 发射事件:自定义事件
            this.$emit('item-click', item)
          }
        }
      }

      // 父组件
      const app = new Vue({
        // 挂载要管理的元素
        el: '#app',
        components : {
          cpn1
        },

        methods: {
          cpnClick(item){
            // 输出item
            console.log('cpnClick', item);
          }
        }
      })
  </script>
</body>
</html>

执行结果
Vue.js-组件通信

1.4 组件通信集合双向绑定

方式一

 <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>组件通信(双向绑定)</title>
</head>
<body>
<!--父组件模板-->
    <div id="app">
      <cpn1 :number1="num1" :number2="num2"
      @num1change="num1change"
      @num2change="num2change"></cpn1>

    </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.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: {
          // 子组件
          cpn1:{
            // 定义模板
            template: '#cpn',
            // 父子之间的通信
            props:{
              number1: Number,
              number2: Number
            },
            // 添加一个data属性,从而实现双向绑定
            data(){
              return{
                dnumber1: this.number1,
                dnumber2: this.number1
              }
            },
            methods: {
              num1Input(event){
                // 将input中的value赋值到dnumber中
                this.dnumber1 = event.target.value;
                // 让父组件可以修改值,发出一个事件
                this.$emit('num1change', this.dnumber1);

                // 修改dnumber2的值
                this.dnumber2 = this.dnumber1 * 100;
                this.$emit('num2change', this.dnumber2)
              },
              num2Input(event){
                // 将input中的value赋值到dnumber中
                this.dnumber2 = event.target.value;
                this.$emit('num2change', this.dnumber2);

                // 修改dnumber1的值
                this.dnumber1 = this.dnumber2 / 100;
                this.$emit('num1change', this.dnumber1)
              }
            }
          }
        }
      })
  </script>
</body>
</html>

执行结果
Vue.js-组件通信

方式二

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <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">
    <h2>props:{{number2}}</h2>
    <h2>data:{{dnumber2}}</h2>
    <input type="text" v-model="dnumber2">
  </div>
</template>

<script src="../js/vue.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,
          name: ''
        },
        data() {
          return {
            dnumber1: this.number1,
            dnumber2: this.number2
          }
        },
        watch: {
          dnumber1(newValue) {
            this.dnumber2 = newValue * 100;
            this.$emit('num1change', newValue);
          },
          dnumber2(newValue) {
            this.number1 = newValue / 100;
            this.$emit('num2change', newValue);
          }
        }
      }
    }
  })
</script>

</body>
</html>

执行结果
Vue.js-组件通信

1.5 父组件直接访问子组件

1.5.1 $children的访问

  • this.$children是一个数组类型,它包含所有子组件对象。
  • 能通过一个遍历,取出所有子组件的message状态。
    Vue.js-组件通信

1.5.2 $children的缺陷

  • 通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
  • 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
  • 有时候,想明确获取其中一个特定的组件,这个时候就可以使用$refs。

1.5.3 $refs的使用

  • $refs和ref指令通常是一起使用的。
  • 首先,通过ref给某一个子组件绑定一个特定的ID。
  • 其次,通过this.$refs.ID就可以访问到该组件了。
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>组件访问(父访问子)</title>
</head>
<body>
<!--2.定义一个div元素-->
<div id="app">
  <cpn1></cpn1>
  <cpn1></cpn1>
  <cpn1 ref="aaa"></cpn1>
  <button @click="btnClick">按钮</button>
</div>

<!--子组件模板-->
<template id="cpn">
  <div>
	<h2>我是子组件</h2>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  // 创建对象
  const app = new Vue({
    // 挂载要管理的元素
    el: '#app',
    // 定义数据
    data: {
      message: 'hello world!'
    },
    methods: {
      btnClick(){
        // 1.$children
		/*
		console.log(this.$children);
		for(let c of this.$children){
		  console.log(c.name);
		  c.showMessage();
		}
		console.log(this.$children[1].name);
		*/

		// 2.$refs ==> 对象类型,默认这是一个空对象
		console.log(this.$refs.aaa.name);
      }
    },
    // 创建子组件
    components: {
      cpn1: {
        template: '#cpn',
        data(){
          return{
            name:'我是子组件的name'
          }
        },
        methods:{
          showMessage(){
            console.log("showMessage!!!");
		  }
        }
      }
    }
  })
</script>
</body>
</html>

执行结果

Vue.js-组件通信

1.6 子组件直接访问父组件

如果想在子组件中直接访问父组件,可以通过$parent

Vue.js-组件通信

注意事项

  • 尽管在Vue开发中,允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
  • 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
  • 如果将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
  • 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于调试和维护。

代码示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>组件访问(子访问父)</title>
</head>
<body>

<div id="app">
  <cpn></cpn>
</div>

<template id="cpn">
  <div>
	<h2>我是cpn组件</h2>
	<ccpn></ccpn>
  </div>
</template>

<template id="ccpn">
  <div>
	<h2>我是子组件</h2>
	<button @click="btnClick">按钮</button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: 'hello Vue.js!!!'
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是cpn组件的name'
          }
        },
        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.js-组件通信

上一篇:Vue面试题真题笔记


下一篇:Vue组件Props中接收数组/对象