首頁  >  文章  >  web前端  >  Vue組件間怎麼通訊?組件通訊的幾種方式

Vue組件間怎麼通訊?組件通訊的幾種方式

青灯夜游
青灯夜游轉載
2022-08-12 20:30:246240瀏覽

Vue元件間怎麼通訊?以下這篇文章跟大家介紹Vue組件的通訊方式,希望對大家有幫助!

Vue組件間怎麼通訊?組件通訊的幾種方式

vue的兩大功能是響應式程式設計和元件化。元件(Component)是 Vue 最核心的功能,但是各個元件實例的作用域是相互獨立的,這表示不同元件之間的資料是無法直接互相引用的。如果想要跨元件引用數據,就需要用到元件通訊了,在通訊之前先要理解元件之間的關係:

Vue組件間怎麼通訊?組件通訊的幾種方式

如上圖所示:
父子關係:A與B,A與C,B與D,C與E
兄弟關係:B與C
隔代關係(可能隔更多代):A與D,A與E
跨級關係:B與E,D與E等

通訊方式

一、props/$emit

父元件透過v-bind綁定一個自訂的屬性,子元件透過props接收父元件傳來的資料;子元件透過$emit觸發事件,父元件用on()或在子元件的自訂標籤上使用v-on來監聽子元件觸發的自訂事件,從而接收子元件傳來的資料。 (學習影片分享:vue影片教學

1、父元件向子元件傳值

下面透過一個範例來說明父元件向子元件傳值,父元件parent.vue把資料books:['JavaScript高階程式設計', 'CSS新世界', '圖解HTTP 彩色版']傳給子元件child.vue,並在child.vue中展示出來

// 父组件parent.vue
<template>
  <div>
    <child></child>
  </div>
</template>

<script>
import Child from &#39;./components/Child.vue&#39;

export default {
  name: &#39;parent&#39;,
  components: {
    Child
  },
  data() {
    return {
      books: [&#39;JavaScript高级程序设计&#39;, &#39;CSS新世界&#39;, &#39;图解 HTTP 彩色版&#39;]
    }
  }
}
</script>
// 子组件child.vue
<template>
  <div>
    <ul>
      <li>{{item}}</li>
    </ul>
  </div>
</template>

<script>
export default {
  props: {
    books: {
      type: Array,
      default: () => {
        return []
      }
    }
  }
}
</script>

Vue組件間怎麼通訊?組件通訊的幾種方式
注意:透過props傳遞資料是單向的,父元件資料變化時會傳遞給子元件,但子元件不能透過修改props傳過來的資料來修改父元件的對應狀態,也就是所謂的單向資料流。

2、子元件向父元件傳值

下面透過子元件點擊書籍列表,用$emit()觸發,然後再父元件中取得

// 子组件child.vue
<template>
  <div>
    <ul>
      <li>{{item}}</li>
    </ul>
  </div>
</template>

<script>
export default {
  props: {
    books: {
      type: Array,
      default: () => {
        return []
      }
    }
  },
  methods: {
    like(item) {
      this.$emit(&#39;likeBook&#39;, item)
    }
  }
}
</script>
// 父组件parent.vue
<template>
  <div>
    <child></child>
  </div>
</template>

<script>
import Child from &#39;./components/Child.vue&#39;

export default {
  name: &#39;parent&#39;,
  components: {
    Child
  },
  data() {
    return {
      books: [&#39;JavaScript高级程序设计&#39;, &#39;CSS新世界&#39;, &#39;图解 HTTP 彩色版&#39;]
    }
  },
  methods: {
    likeBook(val) {
      alert(&#39;我最喜欢的书籍是《&#39; + val + &#39;》&#39;)
    }
  }
}
</script>

Vue組件間怎麼通訊?組件通訊的幾種方式

二、&dollar;parent/$children

    ##$parent:存取父元件實例
  • $children:存取子元件實例
// 父组件parent.vue
<template>
  <div>
    <child></child>
    <button>获取子组件数据</button>
  </div>
</template>

<script>
import Child from &#39;./components/Child.vue&#39;

export default {
  name: &#39;parent&#39;,
  components: {
    Child
  },
  data() {
    return {
      books: [&#39;JavaScript高级程序设计&#39;, &#39;CSS新世界&#39;, &#39;图解 HTTP 彩色版&#39;]
    }
  },
  methods: {
    getChildData() {
      alert(this.$children[0].msg)
    }
  }
}
</script>
// 子组件child.vue
<template>
  <div>
    <ul>
      <li>{{item}}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: &#39;child&#39;,
  data() {
    return {
      bookLists: [],
      msg: &#39;我是子组件的值!&#39;
    }
  },
  mounted() {
    this.bookLists = this.$parent.books
  }
}
</script>
注意:

&dollar;parent拿到的是對象,如果是最頂層沒有父元件的情況下拿到的是undefined;$children拿到的是數組,如果是做底層沒有子元件的情況下,拿到的是空數組;這兩種通訊方式只能用於父子元件通訊

三、

ref

ref如果在普通Dom元素上使用,引用指向的就是DOM 元素;如果在子元件上使用,引用就指向元件實例,可以透過實例直接呼叫元件的方法和資料

// 父组件parent.vue
<template>
  <div>
    <child></child>
    <button>获取子组件数据</button>
  </div>
</template>

<script>
import Child from &#39;./components/Child.vue&#39;

export default {
  name: &#39;parent&#39;,
  components: {
    Child
  },
  methods: {
    getChildData() {
      const msg = this.$refs[&#39;child&#39;].msg
      console.log(msg)
      this.$refs[&#39;child&#39;].say()
    }
  }
}
</script>
// 子组件child.vue
<script>
export default {
  name: &#39;child&#39;,
  data() {
    return {
      msg: &#39;我是子组件的值!&#39;
    }
  },
  methods: {
    say() {
      alert(&#39;你好,我是子组件!&#39;)
    }
  },
}
</script>
四、

provide/inject##祖先元件經過

provide

來提供變量,子孫組件通過inject注入變量來獲取祖先組件的數據,不管子孫組件嵌套有多深, 只要調用了inject 那麼就可以注入provide中的數據。下面是具體程式碼:<pre class="brush:php;toolbar:false">// 父组件 &lt;template&gt;   &lt;div&gt;     &lt;h1&gt;康熙&lt;/h1&gt;     &lt;son&gt;&lt;/son&gt;   &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import Son from &amp;#39;./components/Son.vue&amp;#39; export default { components: { Son }, provide() { return { FatherToSon: this.FatherToSon, FatherToGrandson: this.FatherToGrandson, } }, data() { return { FatherToSon: &amp;#39;我是康熙,雍正,你是我儿子!&amp;#39;, FatherToGrandson: &amp;#39;我是康熙,乾隆,你是我孙子!&amp;#39;, } } } &lt;/script&gt;</pre> <pre class="brush:php;toolbar:false">// 子组件 &lt;template&gt;   &lt;div&gt;     &lt;h1&gt;雍正&lt;/h1&gt;     &lt;button&gt;接收&lt;/button&gt;     &lt;grandson&gt;&lt;/grandson&gt;   &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import Grandson from &amp;#39;./Grandson.vue&amp;#39; export default { components: { Grandson }, inject: [&amp;#39;FatherToSon&amp;#39;], methods: { receive() { alert(this.FatherToSon) } } } &lt;/script&gt;</pre> <pre class="brush:php;toolbar:false">// 孙组件 &lt;template&gt;   &lt;div&gt;     &lt;h1&gt;乾隆&lt;/h1&gt;     &lt;button&gt;接收&lt;/button&gt;   &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { inject: [&amp;#39;FatherToGrandson&amp;#39;], methods: { receive() { alert(this.FatherToGrandson) } } } &lt;/script&gt;</pre>

注意:provide/inject只能從上往下傳值,且不是響應式,若要變成響應式的資料provide需要提供函數Vue組件間怎麼通訊?組件通訊的幾種方式
五、

eventBus

&dollar;emit/$oneventBus是訊息傳遞的一種方式,基於一個訊息中心,訂閱和發布訊息的模式,稱為發布訂閱者模式。

eventBus 又稱為事件匯流排。在 Vue 中可使用 eventBus 來作為溝通橋樑的概念,就像是​​所有元件共用相同的事件中心,可向該中心註冊發送事件或接收事件,所以元件都可以上下平行地通知其他元件。

  • $emit('name',args): name:发布的消息名称 , args:发布的消息
  • $on('name',fn): name:订阅的消息名称, fn: 订阅的消息
  • &dollar;once('name',fn): name:订阅的消息名称, fn: 订阅的消息。与$on相似但是只触发一次,一旦触发之后,监听器就会被移除
  • &dollar;off('name',callback):name:事件名称,callback:回调监听器
    eventbus可以实现任何组件之前的通信,下面以兄弟组件为例

    1、初始化,全局引入

// main.js
// 全局添加事件总线
Vue.prototype.$bus = new Vue()

2、发送事件

在parent.vue引入ChildA和ChildB组件,使它们成为兄弟组件

// 父组件parent.vue
<template>
  <div>
    <childa></childa>
    <childb></childb>
  </div>
</template>

<script>
import ChildA from &#39;./components/childA&#39;
import ChildB from &#39;./components/childB&#39;
export default {
  components: {
    ChildA,
    ChildB
  }
}
</script>

在ChildA组件中用$emit发送事件

// ChildA组件
<template>
  <div>
    <h1>组件A</h1>
    <button>发送</button>
  </div>
</template>

<script>
export default {
  methods: {
    // 发送事件
    send() {
      this.$bus.$emit(&#39;message&#39;, &#39;欢迎使用eventBus!&#39;)
    }
  }
}
</script>

3、接收事件发送的事件

在ChildB组件中用$on接收ChildA发送的事件

// ChildB组件
<template>
  <div>
    <h1>组件B</h1>
  </div>
</template>

<script>
export default {
  mounted() {
    // 接收事件
    this.$bus.$on(&#39;message&#39;, data => {
      alert(&#39;我是组件B,我收到的消息为:&#39; + data)
    })
  },
  beforeDestroy() {
    this.$bus.$off(&#39;message&#39;)
  }
}
</script>

注意:&dollar;on监听的事件不会自动移除监听,因此在不用时最好使用$off移除监听以免产生问题

六、&dollar;attrs/$listeners

1、简介

当组件为两级嵌套时,一般采用props&dollar;emit,但遇到多级组件嵌套时这种方法就不太适用了,如果不做中间处理,只传递数据用Vue組件間怎麼通訊?組件通訊的幾種方式有点大材小用了。因此在vue2.4中为了解决这一需求,便引入了&dollar;attrs$listeners, 新增了inheritAttrs属性

  • &dollar;attrs:当父组件传递了很多数据给子组件时,子组件没有声明props来进行接收,么子组件中的attrs属性就包含了所有父组件传来的数据(除开已经props声明了的);子组件还可以使用v−bind="$attrs"的形式将所有父组件传来的数据(除开已经props声明了的)传向下一级子组件,通常和interitAttrs属性一起使用。
  • &dollar;listeners:包含了父组件中(不含.native修饰器的)v-on 事件监听器,通过v-on="$listeners",可以将这些事件绑定给它自己的子组件

    2、实例

    下面看一个例子:

// 父组件
<template>
  <div>
    <childa></childa>
  </div>
</template>

<script>
import ChildA from &#39;./components/childA&#39;
export default {
  name: &#39;parent&#39;,
  components: {
    ChildA,
  },
  data() {
    return {
      name: &#39;小明&#39;,
      age: 18,
      sex: &#39;男&#39;
    }
  },
  methods: {
    // 获取名字
    getName() {
      console.log(&#39;我的名字是&#39; + this.name)
    },
    // 获取年龄
    getAge() {
      console.log(&#39;我今年&#39; + this.age + &#39;岁&#39;);
    }
  }
}
</script>
// 子组件A
<template>
  <div>
    <h1>组件A</h1>
    {{ msgA }}
    <hr>
    <childb></childb>
  </div>
</template>

<script>
import ChildB from &#39;./childB.vue&#39;
export default {
  name: &#39;ChildA&#39;,
  components: {
    ChildB
  },
  data() {
    return {
      msgA: null,
      height: &#39;175cm&#39;
    }
  },
  props: {
    sex: {
      type: String,
      default: &#39;&#39;
    }
  },
  mounted() {
    this.msgA = this.$attrs
    console.log(&#39;组件A获取的$listeners:&#39;, this.$listeners)
  },
  methods: {
    // 获取身高
    getHeight() {
      console.log(&#39;我的身高是&#39; + this.height);
    }
  }
}
</script>
// 孙组件B
<template>
  <div>
    <h1>组件B</h1>
    {{ msgB }}
  </div>
</template>

<script>
export default {
  name: &#39;ChildB&#39;,
  data() {
    return {
      msgB: null
    }
  },
  mounted() {
    this.msgB = this.$attrs
    console.log(&#39;组件B获取的$listeners:&#39;, this.$listeners)
  }
}
</script>

$attrs获取的结果:
Vue組件間怎麼通訊?組件通訊的幾種方式
$listeners获取的结果:

Vue組件間怎麼通訊?組件通訊的幾種方式

如代码和图所示组件A中props声明接收了sex属性,因此组件中&dollar;attrs获取的是父组件中绑定的除去sex属性的值;组件A中使用了v-bind="&dollar;attrs"v-on="$listeners",则组件B获取不仅是组件A中本身绑定的属性和方法还包含组件A获取父组件绑定的属性和方法

3、inheritAttrs

如果父组件传递了很多参数给子组件,而子组件没有用props完全接收,那么没有接收的这些属性作为普通的 HTML attribute 应用在子组件的根元素上
如果你不希望子组件的根元素继承特性,你可以在组件的选项中设置inheritAttrs: false

以上面的组件B为例,当Vue組件間怎麼通訊?組件通訊的幾種方式(inheritAttrs默认为true)
Vue組件間怎麼通訊?組件通訊的幾種方式

当Vue組件間怎麼通訊?組件通訊的幾種方式

// 孙组件B
export default {
  name: 'ChildB',
  inheritAttrs: false,
  data() {
    return {
      msgB: null
    }
  },
  mounted() {
    this.msgB = this.$attrs
    console.log('组件B获取的$listeners:', this.$listeners)
  }
}

Vue組件間怎麼通訊?組件通訊的幾種方式

七、Vuex

1、Vuex概述

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
状态管理包含以下几个部分:
Vue組件間怎麼通訊?組件通訊的幾種方式

  • 状态(State),驱动应用的数据源
  • 视图(View),以声明方式将状态映射到视图;
  • 操作(Actions),响应在视图上的用户输入导致的状态变化

视图发生变化会导致数据源的改变,数据源发生变化则会改变视图,则上面表示是一个“单向数据流”。但是当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

因此,为了解决这种问题我们把组件的共享状态抽取出来,以一个全局单例模式管理。在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
Vue組件間怎麼通訊?組件通訊的幾種方式

2、 Vuex各个模块

1、state:存储应用中需要共享的状态,是Vuex中的唯一数据源。
2、getters:类似Vue中的计算属性computedgetter 的返回值会根据它的依赖被缓存起  来,且只有当它的依赖值发生了改变才会被重新计算。
3、mutations:更改 Vuex 的 store 中的状态(state)的唯一方法,且mutation 必须是同步函数
4、actions:类似于 mutation,提交的是 mutation,而不是直接变更状态;可以包含任意异步操作
5、modules:将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割

3、Vuex举例

// 父组件
<template>
  <div>
    <h1>父组件</h1>
    <hr>
    <childa></childa>
    <hr>
    <childb></childb>
  </div>
</template>

<script>
import ChildA from &#39;./components/ChildA&#39;
import ChildB from &#39;./components/ChildB&#39;

export default {
  name: &#39;parent&#39;,
  components: {
    ChildA,
    ChildB
  }
}
</script>
// 子组件A
<template>
  <div>
    <h1>组件A</h1>
    <p>A获取的值: {{ count }}</p>
    <button>ChildA-add</button>
  </div>
</template>

<script>
export default {
  computed: {
    count() {
      return this.$store.state.count
    }
  },
  methods: {
    // 改变store里count的值
    add(num) {
      this.$store.dispatch(&#39;countAdd&#39;, num)
    }
  }
}
</script>

<style>

</style>
// 子组件B
<template>
  <div>
    <h1>组件B</h1>
    <p>B获取的值: {{ countB }}</p>
    <button>ChildB-add</button>
  </div>
</template>

<script>
import { mapMutations, mapGetters } from &#39;Vue組件間怎麼通訊?組件通訊的幾種方式&#39;
export default {
  computed: {
    ...mapGetters({
      countB: &#39;getCount&#39;
    })
  },
  methods: {
    ...mapMutations([&#39;countAdd&#39;]),
    // 改变store里count的值
    add(num) {
      this.countAdd(num)
    }
  }
}
</script>

<style>

</style>

store.js

import Vue from 'vue'
import Vuex from 'Vue組件間怎麼通訊?組件通訊的幾種方式'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0,
  },
  getters: {
    getCount: (state) => {
      return state.count
    }
  },
  mutations: {
    countAdd(state, num) {
      state.count += num
    }
  },
  actions: {
    countAdd(context, num) {
      context.commit('countAdd', num)
    }
  },
  modules: {
  }
})

Vue組件間怎麼通訊?組件通訊的幾種方式

八、localStorage/sessionStorage

1、介绍

localStorage:本地存储对象,存储的数据是永久性数据,页面刷新,即使浏览器重启,除非主动删除不然存储的数据会一直存在
sessionStorage:与localStorage相似,但是只有在当前页面下有效,关闭页面或浏览器存储的数据将会清空

localStorage和sessionStorage常用的API:

setItem (key, value) ——  保存数据,以键值对的方式储存信息。
getItem (key) ——  获取数据,将键值传入,即可获取到对应的value值。
removeItem (key) ——  删除单个数据,根据键值移除对应的信息。
clear () ——  删除所有的数据
key (index) —— 获取某个索引的key

2、举例

// 存储
setItem() {
  window.localStorage.setItem('name1', '小明')
  window.sessionStorage.setItem('name2', '小红')
}
// 接收
receive() {
  const name1 = window.localStorage.getItem('name1')
  const name2 = window.sessionStorage.getItem('name2')
  console.log(name1) // 打印结果为:小明
  console.log(name2) // 打印结果为:小红
}

3、setItem()和getItem()使用时的类型转换

localStorage和sessionStorage通过setItem()存储数据会自动转换为String类型,但是通过getItem()其类型并不会转换回来(localStorage和sessionStorage使用方法一样,下面均以localStorage为例)

const num = 1
window.localStorage.setItem('num', num)

const numRec = window.localStorage.getItem('num')
console.log(numRec, typeof(numRec)) // 1 string

因此正确的存储方式应该为:存储之前用JSON.stringify()方法将数据转换成json字符串形式;需要使用数据的时候用JSON.parse()方法将之前存储的字符串转换成json对象

const num = 1
window.localStorage.setItem('num', JSON.stringify(num))
const obj = {
   name: '小红',
   age: 18
 }
window.localStorage.setItem('obj', JSON.stringify(obj))

const numRec = JSON.parse(window.localStorage.getItem('num'))
console.log(numRec, typeof(numRec)) // 1 'number'
const objRec = JSON.parse(window.localStorage.getItem('obj'))
console.log(objRec, typeof(objRec)) // {name: '小红', age: 18} 'object'

注意:localStorage.setItem()和sessionStorage.setItem()不能直接存储对象,必须使用JSON.stringify()JSON.parse()转换实现

总结

以上8种通信方式主要应用在以下三类场景:

  • 父子元件通訊:最常使用通訊方式的是props/&dollar;emit,單一的父子元件通訊使用 &dollar;parent>/&dollar;children也比較方便;父元件也常使用ref來取得子元件實例;也可使用provide/inject&dollar;attrs/&dollar;listeners以及localStorage/sessionStorage
  • 兄弟元件通訊:簡單的資料傳遞可使用eventBus&dollar;emit/&dollar;on;複雜的資料使用Vuex 比較方便;也可以使用localStorage/sessionStorage;
  • 跨級元件通訊:父子孫等嵌套元件通訊方式多使用provide/inject&dollar;attrs/&dollar;listeners;跨級組件通訊的資料如果不複雜可使用eventBuslocalStorage/sessionStorage;如果資料複雜可使用Vuex,但要注意刷新介面Vuex儲存的資料會消失

結尾

本篇文章只是簡單記錄了一下平時使用的元件通訊方式,並沒有深入的介紹其細節,如果有錯誤的地方歡迎指正

(學習影片分享:web前端開發程式設計基礎影片

以上是Vue組件間怎麼通訊?組件通訊的幾種方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除