(18)深入理解 Vue 组件——⑤ 非父子组件间的传值 | Vue 基础理论实操

3,364 阅读2分钟
本文版权归 “公众号 | 前端一万小时” 所有,欢迎转载!

转载请注明出处,未经同意,不可修改文章内容。

🔥🔥🔥本系列文章已在“公众号 | 前端一万小时”更新完毕,有需要的小伙伴可按需前往查看。

🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。


涉及面试题:
组件间的通信?

[编号:vue_18]

🔗本阶段对应的“官方文档”阅读


Vue 官方对 Vue 框架的定义是一个轻量级的视图层框架,当项目中出现很复杂的数据传递时(主要在于同级组件之间需要进行数据传递的话,如果单纯地靠一层层地传递,可能会让项目变得复杂)。

所以光靠 Vue 框架是解决不了复杂数据的传递,此时我们需要引入一些工具或者设计模式来解决 Vue 之中组件间复杂的数据传递18-01.png

1 非父子组件间的数据传递两种方案

  • 借助 Vue 官方提供的一个数据层的框架——Vuex ;
  • 发布订阅模式(又称为:总线机制/Bus/观察者模式)——本篇文章我们主要学习这种方案!

2 子组件内容是如何根据外部传递的内容进行显示

<body>
  <div id="root">
    <child content="hello"></child>
    <child content="world"></child>
  </div>  
  <script>
    Vue.component("child", {
      props: {
        content: String
      },
      template: "<div>{{content}}</div>"
    })
    var vm = new Vue({
      el: "#root"
    })
  </script>
</body>

3 非父子组件间的传值

❓需求: 有两个非父子组件 1 和 2,当点击子组件 1,组件 1 和 组件 2 的内容随即都变成组件 1 里的内容。组件 2 也一样!

💡答:

  1. 首先,看一下点击组件本身,在组件本身出现的数据变化——分别弹出各自的内容
<div id="root">
  <child content="hello"></child>
  <child content="world"></child>
</div>
<script>
  Vue.component("child", {
    props: {
      content: String
    },
    template: '<div @click="handleClick">{{content}}</div>',
    methods: {
      handleClick: function() {
        alert(this.content)
      }
    }  
  })   
  var vm = new Vue({
    el: "#root"
  })
</script>

vue_18-02.gif

  1. 再看看如何把数据传递给另一个组件:
<div id="root">
  <child content="hello"></child>
  <child content="world"></child>
</div>
<script>
  Vue.prototype.bus = new Vue () // 注释1️⃣:挂载了一个 bus 的属性;

  Vue.component("child", {
    data: function() {  
      // ❗️基于“单向数据流”,我们将接收到的“数据”复制一份放在子组件 data 的 selfContent 里。
      return {
        selfContent: this.content
      }
    },
    props: {
      content: String
    },
    template: '<div @click="handleClick">{{selfContent}}</div>',
    methods: {
      handleClick: function() {
        this.bus.$emit("change", this.selfContent) /*
        																					 注释2️⃣:向外触发事件并携带
    																							 要传递的内容数据;
                                                    */
      }
    }  
  })

  var vm = new Vue({
    el: "#root"
  })
</script>
  • 注释1️⃣Vue.prototype.bus = new Vue()

🔗前置知识《JavaScript 基础——面向对象编程:② 使用原型》

在 Vue 的 prototype 上挂载了一个 bus 的属性,该 bus 属性指向一个 Vue 的实例。

在之后,只要调用 new.Vue 或者创建组件时,每个组件上都会有 bus 这个属性(🚀为什么?因为每个 Vue 实例和组件都是通过 Vue 这个类进行创建的,而在 Vue 的 prototype 上挂载一个 bus 的属性,都指向同一个 Vue 的实例)。

  • 注释2️⃣this.bus.$emit('change',  this.content)  

this.bus  指的就是 Vue.prototype.bus = new Vue () 上挂载的 bus ,同时 bus 作为 Vue 实例,有其方法,这里可以用 $ 调用这个 emit ,以此向外触发事件并同时携带要传递的内容数据。

  1. 向外触发事件之后,其他组件将通过“监听”的方式接收数据(❗️借助生命周期钩子):
<body>
  <div id="root">
    <child content="hello"></child>
    <child content="world"></child>
  </div>

  <script>
    Vue.prototype.bus = new Vue () /*
    															 ❗️这行代码两个作用:
																	 ①:使每个实例都挂载 bus 这个属性;
                                   ②:使 bus 就是一个 Vue 的“对象实例”。
                                   	*/

    Vue.component('child', {
      data: function() {
        return {
          selfContent: this.content
        }
      }, 
      props: {
        content: String
      },
      template: '<div @click="handleClick">{{selfContent}}</div>',
      methods: {
        handleClick: function() {
          this.bus.$emit('change', this.selfContent)
        }
      },  
      mounted: function(){
        var this_ = this // ❗️我们需要在这里对 this 作一个保存;
        this.bus.$on('change', function(msg) {  
        /*
        ❗️让这个组件去监听 bus 的改变。而 bus 本身就是一个实例,
        那么它就可以通过 $on 监听 bus 触发出来的事件。
         */
          
          this_.selfContent = msg // ❗️这里就可以用保存了的 this。
        })
      }
    })

    var vm = new Vue({
      el: "#root"
    })
  </script>
</body>

vue_18-03.gif

祝好,qdywxs ♥ you!