前言:

1.父组件向子组件传值

方法一

父传子主要通过在父组件v-model绑定数据,在子组件进行用props进行数据的接收

<!-- 父组件 -->
<template>
  <div id="app">
    <child-a :Persons="Persons"></child-a>
  </div>
</template>
<script>
  import ChildA from 'components/ChildA'
  export default {
    name: "App",
    data() {
      return {
        Persons: [{
            name: "张三A",
            age: 18,
            height: 188
          },
          {
            name: "李四A",
            age: 19,
            height: 189
          },
          {
            name: "王五A",
            age: 20,
            height: 190
          }
        ]
      }
    },
    components: {
      ChildA,
    }
  }
</script>

<!-- 子组件 -->
<template>
    <div id="child-a">
      <div v-for="(item,index) in Persons">
        <span>{{item.name}}</span>
        <span>{{item.age}}</span>
        <span>{{item.height}}</span>
      </div>
    </div>
  </template>
  <script>
    export default {
      name: 'ChildA',
      props: {
        Persons: {
          type: Array,
          default: []
        }
      },
  </script>
方法二

父组件通过this.$children方法访问子组件,this.$children是一个数组类型,它包含所有子组件对象。
我们这里通过一个遍历,取出所有子组件的message状态。

方法三

children的缺陷:

  • 通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
    但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
    有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs
  • $refs和ref指令通常是一起使用的。
    首先,我们通过ref给某一个子组件绑定一个特定的ID。
    其次,通过this.$refs.ID就可以访问到该组件了。
    父组件触发子组件的方法,主要通过$refs来触发,同时传参,这里获取的是子组件内的所有方法,数据,即获取的是一个组件对象。
  • $refs在实际开发时,经常会使用。如下所示
<child-a ref="childA"></child-a>
<child-b ref="childB"></child-b>
<button @click="shoRefsChild">通过refs访问子组件</button>

shoRefsChild(){
  <!-- 访问组件child-a的message数据 -->
  console.log(this.$refs.childA.message)
  <!-- 访问组件child-a的showMessage方法 -->
  this.$refs.childA.showMessage()
  <!-- 访问组件child-b的message数据 -->
  console.log(this.$refs.childB.message)
  <!-- 访问组件child-b的showMessage方法 -->
  this.$refs.childB.showMessage()
}

完整代码

  <!-- 父组件 -->
  <template>
    <div id="app">
    <child-a ref="childA"></child-a>
    <child-b ref="childB"></child-b>
    </div>
  </template>
  <script>
    import ChildA from 'components/ChildA'
    import ChildB from 'components/ChildB'
    export default {
      name: "App",
      data() {
        return {
          message:''
        }
      },
      components: {
        ChildA,
        ChildB
      },
      methods: {
        shoRefsChild(){
          <!-- 访问组件child-a的message数据 -->
          console.log(this.$refs.childA.message)
          <!-- 访问组件child-a的showMessage方法 -->
          this.$refs.childA.showMessage()
          <!-- 访问组件child-b的message数据 -->
          console.log(this.$refs.childB.message)
          <!-- 访问组件child-b的showMessage方法 -->
          this.$refs.childB.showMessage()
        }
      }
    }
  </script>

  <!-- 子组件A -->
  <template>
    <div id="child-a">
      <h3>{{message}}</h3>
    </div>
  </template>
  <script>
    export default {
      name: 'ChildA',
      data () {
        return {
          message:'我是子组件ChildA'
        }
      },
      methods: {
        init(message){
          this.message = message
        }
      }
    };
  </script>

<!-- 子组件B -->
  <template>
    <div id="child-b">
      <h3>{{message}}</h3>
    </div>
  </template>
  <script>
    export default {
      name: 'ChildA',
      data () {
        return {
          message:'我是子组件ChildB'
        }
      },
      methods: {
        showMessage(){
          console.log(this.message)
        }
      }
    };
  </script>

2.子组件访问父组件

方法一 自定义事件($emit)

子组件中需要以某种方式例如点击事件的方法来触发一个自定义事件,将需要传的值作为$emit的第二个参数,该值将作为实参传给响应自定义事件的方法,在父组件中注册子组件并在子组件标签上绑定对自定义事件的监听

<!-- 父组件 -->
<template>
  <div id="app">
    <child-b @person-click="itemClick"></child-b>
  </div>
</template>
<script>
  import ChildB from 'components/ChildB'
  export default {
    name: "App",
    components: {
      ChildB
    },
    methods: {
      itemClick(index){
        console.log(index)
      }
    }

  }
</script>

<!-- 子组件 -->
<template>
  <div id="child-b">
    <div v-for="(item,index) in Persons" @click="changePerson(index)">
      <span>{{item.name}}</span>
      <span>{{item.age}}</span>
      <span>{{item.height}}</span>
    </div>
  </div>
</template>
<script>
  export default {
    name: 'ChildB',
    data() {
      return {
        Persons: [{
            name: "张三B",
            age: 18,
            height: 188
          },
          {
            name: "李四B",
            age: 19,
            height: 189
          },
          {
            name: "王五B",
            age: 20,
            height: 190
          }
        ]
      }
    },
    methods: {
      changePerson(index) {
        this.$emit('person-click', index)
      }
    }
  };
</script>
方法二 $parent
  1. 如果我们想在子组件中直接访问父组件,可以通过$parent
    注意事项:
    尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
    子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
    如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
    另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。

3.非父子关系之间的数据传递

3.1. 前言

非父子组件关系包括多个层级的组件,也包括兄弟组件的关系。
在Vue1.x的时候,可以通过$dispatch和$broadcast完成
 $dispatch用于向上级派发事件
 $broadcast用于向下级广播事件
但是在Vue2.x都被取消了
 在Vue2.x中,有一种方案是通过中央事件总线,也就是一个中介来完成。
 但是这种方案和直接使用Vuex的状态管理方案还是逊色很多。
 并且Vuex提供了更多好用的功能,所以这里我们暂且不讨论这种方案,后续我们专门学习Vuex的状态管理。

3.2. 中央事件总线

在实际业务中,除了父子通信外,还有很多非父子组件通信的场景,非父子组件一般有两种,兄弟组件和跨多级组件。Vue.js 2.x中,使用一个空的Vue实例作为中央事件总线(bus)。
中央事件总线就好比一个中介,租房者与出租者担任两个跨级组件,租房者与出租者之间的联系全靠中介,而两个跨级组件之间的通信全靠中央事件总线(state)。

3.2.1. 图示

3.2.2代码如下
<!-- main.js -->
Vue.prototype.state = new Vue();
<!-- 实例对象原型上添加新的vue对象  为所有vue组件共有  兄弟子组件可以直接使用 不用经过父组件 -->
<!-- 子组件1 -->
<template>
  <div id="child-a">
    请输入手机号:<input type="text" v-model="phoneNumber" @change="addPhone">
  </div>
</template>
<script>
  export default {
    name: 'ChildA',
    data () {
      return {
        phoneNumber:''
      }
    },
    methods: {
      addPhone(){
        //你在这里触发那个事件 在需要的页面就监听谁
        // console.log(this,this.state);
        this.state.$emit('phoneInput',this.phoneNumber);
        this.phoneNumber = "";
      }
    }
  };
</script>

<!-- 子组件2 -->
<template>
  <div id="child-b">
    <div>
      <ul>
          <li v-for="(item,index) in phoneNumbers">
            {{item}}
          </li>
        </ul>
    </div>
  </div>
</template>
<script>
  export default {
    name: 'ChildB',
    data() {
      return {
        phoneNumbers:[]
      }
    },
    created () {
      //利用该方法监听自定义事件
      this.state.$on('phoneInput',phoneNumber => {
          this.phoneNumbers.push(phoneNumber);
      })
    }
  };
</script>
3.3. Vuex公共数据池

4.总结

在父子组件通信中,无论是子组件向父组件传值还是父组件向子组件传值,他们都有一个共同点就是有中间介质,子向父的介质是自定义事件,父向子的介质是props中的属性。抓准这两点对于父子通信就好理解了

评 论