乾貨分享:Vue3元件通訊的7種方式!

2022-02-16 13:00:40
元件(Component)是 Vue 最核心的功能,是可複用的vue範例;但元件範例的作用域是相互獨立的,也就是說不同元件間的資料是無法直接互相參照的。那麼,如何將元件間的資料關聯起來?如何進行通訊傳遞資料呢?下面本篇文章就給大家分享七種元件通訊方式,希望對大家有所幫助!

本篇文章是全部採用的<script setup>這種組合式API寫法,相對於選項式來說,組合式API這種寫法更加自由,具體可以參考Vue檔案對兩種方式的描述。

注:也開始教授最新版本的vue課程了,感興趣的朋友可以瞭解學習。

本篇文章將介紹如下七種元件通訊方式:

  • props
  • emit
  • v-model
  • refs
  • provide/inject
  • eventBus
  • vuex/pinia(狀態管理工具)

開始搞事情~

舉一個栗子

俗話說的好,學習不寫demo,那就是耍流氓~

本篇文章將圍繞下面這個demo,如下圖所示:

1.gif

上圖中,列表輸入框分別是父子元件,根據不同傳值方式,可能誰是父元件誰是子元件會有所調整。

1、Props方式

Props方式是Vue中最常見的一種父傳子的一種方式,使用也比較簡單。【相關推薦:】

根據上面的demo,我們將資料以及對資料的操作定義在父元件,子元件僅做列表的一個渲染;

父元件程式碼如下:

<template>
  <!-- 子元件 -->
  <child-components :list="list"></child-components>
  <!-- 父元件 -->
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="請輸入"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        新增
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// add 觸發後的事件處理常式
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
</script>

子元件只需要對父元件傳遞的值進行渲染即可,程式碼如下:

<template>
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in props.list" :key="i">{{ i }}</li>
  </ul>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
  list: {
    type: Array,
    default: () => [],
  },
})
</script>

2、emit方式

emit方式也是Vue中最常見的元件通訊方式,該方式用於子傳父

根據上面的demo,我們將列表定義在父元件,子元件只需要傳遞新增的值即可。

子元件程式碼如下:

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="請輸入"
    />
    <div class="input-group-append">
      <button @click="handleSubmit" class="btn btn-primary" type="button">
        新增
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, defineEmits } from 'vue'
const value = ref('')
const emits = defineEmits(['add'])
const handleSubmit = () => {
  emits('add', value.value)
  value.value = ''
}
</script>

在子元件中點選【新增】按鈕後,emit一個自定義事件,並將新增的值作為引數傳遞。

父元件程式碼如下:

<template>
  <!-- 父元件 -->
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
  <!-- 子元件 -->
  <child-components @add="handleAdd"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
// add 觸發後的事件處理常式
const handleAdd = value => {
  list.value.push(value)
}
</script>

在父元件中只需要監聽子元件自定義的事件,然後執行對應的新增操作。

3、v-model方式

v-model是Vue中一個比較出色的語法糖,就比如下面這段程式碼

<ChildComponent v-model:title="pageTitle" />

就是下面這段程式碼的簡寫形勢

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

v-model確實簡便了不少,現在我們就來看一下上面那個demo,如何用v-model實現。

子元件

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="請輸入"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        新增
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, defineEmits, defineProps } from 'vue'
const value = ref('')
const props = defineProps({
  list: {
    type: Array,
    default: () => [],
  },
})
const emits = defineEmits(['update:list'])
// 新增操作
const handleAdd = () => {
  const arr = props.list
  arr.push(value.value)
  emits('update:list', arr)
  value.value = ''
}
</script>

在子元件中我們首先定義propsemits,然後新增完成之後emit指定事件。

注:update:*是Vue中的固定寫法,*表示props中的某個屬性名。

父元件中使用就比較簡單,程式碼如下:

<template>
  <!-- 父元件 -->
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
  <!-- 子元件 -->
  <child-components v-model:list="list"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
</script>

4、refs方式

在使用選項式API時,我們可以通過this.$refs.name的方式獲取指定元素或者元件,但是組合式API中就無法使用哪種方式獲取。如果我們想要通過ref的方式獲取元件或者元素,需要定義一個同名的Ref物件,在元件掛載後就可以存取了。

範例程式碼如下:

<template>
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in childRefs?.list" :key="i">
      {{ i }}
    </li>
  </ul>
  <!-- 子元件 ref的值與<script>中的保持一致 -->
  <child-components ref="childRefs"></child-components>
  <!-- 父元件 -->
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const childRefs = ref(null)
</script>

子元件程式碼如下:

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="請輸入"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        新增
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, defineExpose } from 'vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// add 觸發後的事件處理常式
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
defineExpose({ list })
</script>

setup元件預設是關閉的,也即通過模板ref獲取到的元件的公開範例,不會暴露任何在**<script setup>中宣告的繫結。如果需要公開需要通過****defineExpose**** API暴露**。

5、provide/inject方式

provideinject是Vue中提供的一對API,該API可以實現父元件向子元件傳遞資料,無論層級有多深,都可以通過這對API實現。範例程式碼如下所示:

父元件

<template>
  <!-- 子元件 -->
  <child-components></child-components>
  <!-- 父元件 -->
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="請輸入"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        新增
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, provide } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// 向子元件提供資料
provide('list', list.value)
// add 觸發後的事件處理常式
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
</script>

子元件

<template>
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
</template>
<script setup>
import { inject } from 'vue'
// 接受父元件提供的資料
const list = inject('list')
</script>

值得注意的是使用provide進行資料傳遞時,儘量readonly進行資料的包裝,避免子元件修改父級傳遞過去的資料

6、事件匯流排

Vue3中移除了事件匯流排,但是可以藉助於第三方工具來完成,Vue官方推薦mitttiny-emitter

在大多數情況下不推薦使用全域性事件匯流排的方式來實現元件通訊,雖然比較簡單粗暴,但是長久來說維護事件匯流排是一個大難題,所以這裡就不展開講解了,具體可以閱讀具體工具的檔案

7、狀態管理工具

VuexPinia是Vue3中的狀態管理工具,使用這兩個工具可以輕鬆實現元件通訊,由於這兩個工具功能比較強大,這裡就不做展示了,具體可以查閱檔案

寫在最後

本篇文章到這就結束了,總的來說比較簡單,沒有什麼複雜內容。

如果這篇文章對你來說有點作用,歡迎點贊、評論、收藏,避免在需要的時候找不到。

如果文中有錯誤,歡迎指正~

原文地址:https://juejin.cn/post/7062740057018335245

作者:一碗周

(學習視訊分享:)