首頁  >  文章  >  web前端  >  詳解8種vue組件通訊方式,快來收藏!

詳解8種vue組件通訊方式,快來收藏!

青灯夜游
青灯夜游轉載
2021-04-22 13:38:213396瀏覽

本篇文章帶大家詳細了解一下vue中8種元件通訊方式。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

詳解8種vue組件通訊方式,快來收藏!

vue是資料驅動視圖更新的框架,所以對於vue來說元件間的資料通訊非常重要,那麼元件之間如何進行資料通訊的呢?

首先我們需要知道在vue中組件之間存在什麼樣的關係, 才更容易理解他們的通信方式, 就好像過年回家,坐著一屋子的陌生人,相互之間怎麼稱呼,這時就需要先知道自己和他們之間是什麼樣的關係。 (學習影片分享:vue影片教學

vue元件中關係說明:

詳解8種vue組件通訊方式,快來收藏!

##如上圖所示, A與B、A與C、B與D、C與E組件之間是父子關係; B與C之間是兄弟關係;A與D、A與E之間是隔代關係; D與E是堂兄關係(非直系親屬)

針對上述關係我們歸類為:

    父子元件之間通訊
  • 非父子元件之間通訊(兄弟元件、隔代關係組件等)
本文會介紹組件間通訊的8種方式如下圖目錄所示:並介紹在不同的場景下如何選擇有效方式實現的元件間通訊方式,希望可以幫助小夥伴們更能理解組件間的通訊。

詳解8種vue組件通訊方式,快來收藏!

一、

props / $emit


父元件透過

props的方式向子元件傳遞數據,而透過$emit 子元件可以向父元件通訊。

1. 父元件傳送值

# #下面透過一個範例說明父元件如何傳遞資料給子元件:在子元件 article.vue中如何取得父元件section.vue中的資料

articles:['紅樓夢', '西遊記','三國演義']
// section父组件
<template>
  <div class="section">
    <com-article :articles="articleList"></com-article>
  </div>
</template>

<script>
import comArticle from &#39;./test/article.vue&#39;
export default {
  name: &#39;HelloWorld&#39;,
  components: { comArticle },
  data() {
    return {
      articleList: [&#39;红楼梦&#39;, &#39;西游记&#39;, &#39;三国演义&#39;]
    }
  }
}
</script>
// 子组件 article.vue
<template>
  <div>
    <span v-for="(item, index) in articles" :key="index">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: [&#39;articles&#39;]
}
</script>

總結: prop 只可以從上一層元件傳遞到下一層元件(父子元件),也就是所謂的單向資料流。而且 prop 只讀,不可被修改,所有修改都會失效並警告。

2. 子元件傳送值給父元件

$emit 我自己的理解是這樣的:
$emit綁定一個自訂事件, 當這個語句被執行時, 就會將參數arg傳遞給父元件,父元件透過v-on監聽並接收參數。透過一個例子,說明子元件如何向父元件傳遞資料。 在上個範例的基礎上, 點擊頁面渲染出來的ariticle

item

, 父元件中顯示在陣列中的下標<pre class="brush:js;toolbar:false;">// 父组件中 &lt;template&gt; &lt;div class=&quot;section&quot;&gt; &lt;com-article :articles=&quot;articleList&quot; @onEmitIndex=&quot;onEmitIndex&quot;&gt;&lt;/com-article&gt; &lt;p&gt;{{currentIndex}}&lt;/p&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import comArticle from &amp;#39;./test/article.vue&amp;#39; export default { name: &amp;#39;HelloWorld&amp;#39;, components: { comArticle }, data() { return { currentIndex: -1, articleList: [&amp;#39;红楼梦&amp;#39;, &amp;#39;西游记&amp;#39;, &amp;#39;三国演义&amp;#39;] } }, methods: { onEmitIndex(idx) { this.currentIndex = idx } } } &lt;/script&gt;</pre><pre class="brush:js;toolbar:false;">&lt;template&gt; &lt;div&gt; &lt;div v-for=&quot;(item, index) in articles&quot; :key=&quot;index&quot; @click=&quot;emitIndex(index)&quot;&gt;{{item}}&lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { props: [&amp;#39;articles&amp;#39;], methods: { emitIndex(index) { this.$emit(&amp;#39;onEmitIndex&amp;#39;, index) } } } &lt;/script&gt;</pre>二,  $children /

$parent


詳解8種vue組件通訊方式,快來收藏!

上面這張圖片是vue官方的解釋,透過$parent$children就可以存取元件的實例,拿到實例代表什麼?代表可以存取此元件的所有方法和data。接下來就是怎麼實作拿到指定元件的實例。

使用方法

// 父组件中
<template>
  <div class="hello_world">
    <div>{{msg}}</div>
    <com-a></com-a>
    <button @click="changeA">点击改变子组件值</button>
  </div>
</template>

<script>
import ComA from &#39;./test/comA.vue&#39;
export default {
  name: &#39;HelloWorld&#39;,
  components: { ComA },
  data() {
    return {
      msg: &#39;Welcome&#39;
    }
  },

  methods: {
    changeA() {
      // 获取到子组件A
      this.$children[0].messageA = &#39;this is new value&#39;
    }
  }
}
</script>
// 子组件中
<template>
  <div class="com_a">
    <span>{{messageA}}</span>
    <p>获取父组件的值为:  {{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messageA: &#39;this is old&#39;
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>

要注意邊界情況,如在#app上拿$parent得到的是new Vue()的實例,在這實例上再拿

$parent
得到的是

undefined,而在最底層的子元件拿$children
是個空數組。也要注意得到

$parent

$children的值不一樣,$children 的值是數組,而$parent是個對象總結上面兩種方式用於父子組件之間的通信, 而使用props進行父子組件通信更加普遍; 二者皆不能用於非父子組件之間的通信。

######三、###provide###/ ###inject##################概念:#################################################################### ########provide###/ ###inject### 是###vue2.2.0###新增的api, 簡單來說就是父元件中透過###provide###來提供變數, 然後再子元件中透過###inject###來注入變數。 ###
注意: 这里不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中回去数据

举例验证

接下来就用一个例子来验证上面的描述:
假设有三个组件: A.vue、B.vue、C.vue 其中 C是B的子组件,B是A的子组件

// A.vue

<template>
  <div>
    <comB></comB>
  </div>
</template>

<script>
  import comB from &#39;../components/test/comB.vue&#39;
  export default {
    name: "A",
    provide: {
      for: "demo"
    },
    components:{
      comB
    }
  }
</script>
// B.vue

<template>
  <div>
    {{demo}}
    <comC></comC>
  </div>
</template>

<script>
  import comC from &#39;../components/test/comC.vue&#39;
  export default {
    name: "B",
    inject: [&#39;for&#39;],
    data() {
      return {
        demo: this.for
      }
    },
    components: {
      comC
    }
  }
</script>
// C.vue
<template>
  <div>
    {{demo}}
  </div>
</template>

<script>
  export default {
    name: "C",
    inject: [&#39;for&#39;],
    data() {
      return {
        demo: this.for
      }
    }
  }
</script>

四、ref / refs


ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据, 我们看一个ref 来访问组件的例子:

// 子组件 A.vue

export default {
  data () {
    return {
      name: &#39;Vue.js&#39;
    }
  },
  methods: {
    sayHello () {
      console.log(&#39;hello&#39;)
    }
  }
}
// 父组件 app.vue

<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.name);  // Vue.js
      comA.sayHello();  // hello
    }
  }
</script>

五、eventBus


eventBus  又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。

eventBus也有不方便之处, 当项目较大,就容易造成难以维护的灾难

在Vue的项目中怎么使用eventBus来实现组件之间的数据通信呢?具体通过下面几个步骤

1. 初始化

首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它.

// event-bus.js

import Vue from &#39;vue&#39;
export const EventBus = new Vue()

2. 发送事件

假设你有两个组件: additionNumshowNum, 这两个组件可以是兄弟组件也可以是父子组件;这里我们以兄弟组件为例:

<template>
  <div>
    <show-num-com></show-num-com>
    <addition-num-com></addition-num-com>
  </div>
</template>

<script>
import showNumCom from &#39;./showNum.vue&#39;
import additionNumCom from &#39;./additionNum.vue&#39;
export default {
  components: { showNumCom, additionNumCom }
}
</script>
// addtionNum.vue 中发送事件

<template>
  <div>
    <button @click="additionHandle">+加法器</button>    
  </div>
</template>

<script>
import {EventBus} from &#39;./event-bus.js&#39;
console.log(EventBus)
export default {
  data(){
    return{
      num:1
    }
  },

  methods:{
    additionHandle(){
      EventBus.$emit(&#39;addition&#39;, {
        num:this.num++
      })
    }
  }
}
</script>

3. 接收事件

// showNum.vue 中接收事件

<template>
  <div>计算和: {{count}}</div>
</template>

<script>
import { EventBus } from &#39;./event-bus.js&#39;
export default {
  data() {
    return {
      count: 0
    }
  },

  mounted() {
    EventBus.$on(&#39;addition&#39;, param => {
      this.count = this.count + param.num;
    })
  }
}
</script>

这样就实现了在组件addtionNum.vue中点击相加按钮, 在showNum.vue中利用传递来的    num 展示求和的结果.

4. 移除事件监听者

如果想移除事件的监听, 可以像下面这样操作:

import { eventBus } from &#39;event-bus.js&#39;
EventBus.$off(&#39;addition&#39;, {})

六、Vuex


1.  Vuex介绍

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.
Vuex 解决了多个视图依赖于同一状态来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上

2. Vuex各个模块

  • state:用于数据的存储,是store中的唯一数据源

  • getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算

  • mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件

  • actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作

  • modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护

3. Vuex实例应用

// 父组件

<template>
  <div id="app">
    <ChildA/>
    <ChildB/>
  </div>
</template>

<script>
  import ChildA from &#39;./components/ChildA&#39; // 导入A组件
  import ChildB from &#39;./components/ChildB&#39; // 导入B组件

  export default {
    name: &#39;App&#39;,
    components: {ChildA, ChildB} // 注册A、B组件
  }
</script>
// 子组件childA

<template>
  <div id="childA">
    <h1>我是A组件</h1>
    <button @click="transform">点我让B组件接收到数据</button>
    <p>因为你点了B,所以我的信息发生了变化:{{BMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        AMessage: &#39;Hello,B组件,我是A组件&#39;
      }
    },
    computed: {
      BMessage() {
        // 这里存储从store里获取的B组件的数据
        return this.$store.state.BMsg
      }
    },
    methods: {
      transform() {
        // 触发receiveAMsg,将A组件的数据存放到store里去
        this.$store.commit(&#39;receiveAMsg&#39;, {
          AMsg: this.AMessage
        })
      }
    }
  }
</script>
// 子组件 childB

<template>
  <div id="childB">
    <h1>我是B组件</h1>
    <button @click="transform">点我让A组件接收到数据</button>
    <p>因为你点了A,所以我的信息发生了变化:{{AMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        BMessage: &#39;Hello,A组件,我是B组件&#39;
      }
    },
    computed: {
      AMessage() {
        // 这里存储从store里获取的A组件的数据
        return this.$store.state.AMsg
      }
    },
    methods: {
      transform() {
        // 触发receiveBMsg,将B组件的数据存放到store里去
        this.$store.commit(&#39;receiveBMsg&#39;, {
          BMsg: this.BMessage
        })
      }
    }
  }
</script>

vuex的store,js

import Vue from &#39;vue&#39;
import Vuex from &#39;vuex&#39;
Vue.use(Vuex)
const state = {
  // 初始化A和B组件的数据,等待获取
  AMsg: &#39;&#39;,
  BMsg: &#39;&#39;
}

const mutations = {
  receiveAMsg(state, payload) {
    // 将A组件的数据存放于state
    state.AMsg = payload.AMsg
  },
  receiveBMsg(state, payload) {
    // 将B组件的数据存放于state
    state.BMsg = payload.BMsg
  }
}

export default new Vuex.Store({
  state,
  mutations
})

七、 localStorage / sessionStorage


这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。

通过window.localStorage.getItem(key) 获取数据

通过window.localStorage.setItem(key,value) 存储数据

注意用JSON.parse() / JSON.stringify() 做数据格式转换
localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题.

$attrs$listeners


现在我们来讨论一种情况, 我们一开始给出的组件关系图中A组件与D组件是隔代关系, 那它们之前进行通信有哪些方式呢?

  • 使用props绑定来进行一级一级的信息传递, 如果D组件中状态改变需要传递数据给A, 使用事件系统一级级往上传递

  • 使用eventBus,这种情况下还是比较适合使用, 但是碰到多人合作开发时, 代码维护性较低, 可读性也低

  • 使用Vuex来进行数据管理, 但是如果仅仅是传递数据, 而不做中间处理,使用Vuex处理感觉有点大材小用了.

vue2.4中,为了解决该需求,引入了$attrs$listeners , 新增了inheritAttrs 选项。 在版本2.4以前,默认情况下,父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外),将会“回退”且作为普通的HTML特性应用在子组件的根元素上。接下来看一个跨级通信的例子:

// app.vue
// index.vue

<template>
  <div>
    <child-com1
      :name="name"
      :age="age"
      :gender="gender"
      :height="height"
      title="程序员成长指北"
    ></child-com1>
  </div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
  components: { childCom1 },
  data() {
    return {
      name: "zhang",
      age: "18",
      gender: "女",
      height: "158"
    };
  }
};
</script>
// childCom1.vue

<template class="border">
  <div>
    <p>name: {{ name}}</p>
    <p>childCom1的$attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
  props: {
    name: String // name作为props属性绑定
  },
  created() {
    console.log(this.$attrs);
     // { "age": "18", "gender": "女", "height": "158", "title": "程序员成长指北" }
  }
};
</script>
// childCom2.vue

<template>
  <div class="border">
    <p>age: {{ age}}</p>
    <p>childCom2: {{ $attrs }}</p>
  </div>
</template>
<script>

export default {
  inheritAttrs: false,
  props: {
    age: String
  },
  created() {
    console.log(this.$attrs); 
    // { "gender": "女", "height": "158", "title": "程序员成长指北" }
  }
};
</script>

更多编程相关知识,请访问:编程视频!!

以上是詳解8種vue組件通訊方式,快來收藏!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

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