iT邦幫忙

2021 iThome 鐵人賽

DAY 28
2
Modern Web

[ 重構倒數30天,你的網站不Vue白不Vue ] 系列 第 28

[重構倒數第03天] - One-Way Data Flow 單向資料流

前言

該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。

Component 之間在做資料溝通的時候,很常會使用 props 以及 emit 來傳遞你的資料,但是當你 propsemit 變多的時候,其實對於資料流的傳遞上面就會變複雜,我們先來看一下使用 propsemit 的例子。

Vue mike)

首先大家會看到我有一個 header 裡面有一顆 menu 的按鈕,當我點擊了會把我的側邊選單給開啟滑進來,然後點擊了關閉按鈕又被關起來了,這其實在我們前面很多的範例都有許多類似的內容,但是關於 Component 之間在做資料溝通的細節我們並沒有特別的去探討,今天就讓我們來仔細探討這件事情。

這是我們 App.vue

<script>
import { ref } from "vue";
import Header from "./components/Header.vue";
import Nav from "./components/Nav.vue";
export default {
  components: {
    Header,
    Nav,
  },
  setup() {
    const isOpen = ref(false);
    const handToggle = () => isOpen.value = !isOpen.value;
    return { isOpen, handToggle };
  },
};
</script>

<template>
  <Header @toggle="handToggle" />
  <Nav :isOpen="isOpen" @toggle="handToggle" />
</template>

我的 Header.vue

<script>
export default {
  setup(props, { emit }) {
    const handleMenu = () => {
      emit("toggle");
    };
    return { handleMenu };
  },
};
</script>

<template>
  <header>
    <nav>
      <button @click="handleMenu">menu</button>
    </nav>
  </header>
</template>

側邊選單 Nav.vue

<script>
export default {
  props: {
    isOpen: {
      type: Boolean,
      default: false,
    },
  },
  setup(props, { emit }) {
    const handleMenu = () => {
      emit("toggle");
    };
    return { props, handleMenu };
  },
};
</script>

<template>
  <div :class="['nav', { open: props.isOpen }]">
    <a @click="handleMenu">close</a>
  </div>
</template>

整體介紹一下這個資料的 flow

https://ithelp.ithome.com.tw/upload/images/20210928/20125854obPJlLNSFI.jpg

我們會在點擊了 Header.vue裡面的 Menu 後發送一個 emit 給上層的 component,透過上層接收到的 toggle事件去修改我們 isOpenvalue,然後 isOpen再透過 props 傳入 Nav.vue 裡面去控制側邊選單的開起,然後透過側邊選單的 close 按鈕,在發送一個 toggle事件往上去更改 isOpen,然後 props 的 isOpen 也會同步知道被更改,這樣就完成了整個的流程!

codesandbox 範例 :https://codesandbox.io/s/one-way-data-flow-1-i4hcx

But...

這個資料流的流程很亂,上上下下的,雖然製作具有一定規模的專案我們會選用 vuex 或是 composition API 來管理我們的資料,但是難免在一些小案子或是小組件上面我們選擇用比較直觀的方式來處理,但是越直觀的方式就越要特別注意,老實說我不是很推薦濫用 emit,我認爲 emit 的使用應該是要搭配第三方套件或是自己在開發套件上面的使用會比較恰當,所以在這種開發的情境下面,我會推薦另外一種做法 One-Way Data Flow

什麼是One-Way Data Flow (單向資料流) ?

簡單來說就是我們的資料是透過 props 往下傳遞,但是不要往上發送,然後不可以透過子組件去直接修改 props傳遞下來的 value 。

官方文件 :https://v3.vuejs.org/guide/component-props.html#one-way-data-flow

你可能會問,那要怎麼修改上層的資料?

我們來看一下實際的作法

App.vue

<script>
// 其他省略...
export default {
  setup() {
    const isOpen = ref(false);
    const handleOpenMenu = () => isOpen.value = !isOpen.value;
    return { isOpen, handleOpenMenu };
  },
};
</script>

<template>
  <Header :handleOpenMenu="handleOpenMenu" />
  <Nav :isOpen="isOpen" :handleOpenMenu="handleOpenMenu" />
</template>

我在父層先定義好了一個修改 isOpen 的 function 叫做 handleOpenMenu,然後把這個 function 也當成 props 給傳下去我的子組件。

header.vue

<script>
export default {
  props: {
    handleOpenMenu: {
      type: Function,
      default: () => {},
    },
  },
  setup(props) {
    return { props };
  },
};
</script>
<template>
  <header>
    <nav>
      <button @click="props.handleOpenMenu">menu</button>
    </nav>
  </header>
</template>

nav.vue

<script>
export default {
  props: {
    isOpen: {
      type: Boolean,
      default: false,
    },
    handleOpenMenu: {
      type: Function,
      default: () => {},
    },
  },
  setup(props, { emit }) {
    return { props };
  },
};
</script>
<template>
  <div :class="['nav', { open: props.isOpen }]">
    <a @click="props.handleOpenMenu">close</svg>
    </a>
  </div>
</template>

你會看到我直接把 props 傳遞下來的 handleOpenMenu 拿來使用,去觸發父層的 function 來修改父層的資料,這樣一來一樣可以達到相同的效果。

https://ithelp.ithome.com.tw/upload/images/20210928/20125854qG373HAb9N.jpg

你會看到這張圖的表示,你就會發現現在的資料流都只有從上面往下,flow也變簡單,這樣子在進行開發的時候可以統一就我們對於資料及操作放在上層去控管,子組件只要負責觸發即可。

codesandbox 範例 :https://codesandbox.io/s/one-way-data-flow-2-h0du4?file=/src/main.js

先告一個段落

其實蠻多剛入門的新手在開發上面蠻常會把 propsemit 大量的使用,導致後續專案難以維護,不過我們只要我們可以掌握 One-Way Data Flow 的原則,在組件上面的資料流程就會相對單純一點,也會變得比較好維護,但是如果專案的規模慢慢變大,建議還是使用 vuex 或是 composition API 來管理比較好。

QRcode

那如果對於Vue3不夠熟的話呢?

Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。

我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/packages/AYR5m7VR3

那如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/packages/Q9R4OYoyD

訂閱Mike的頻道享受精彩的教學與分享

Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng


上一篇
[重構倒數第04天] - 輪播套件難道只可以做圖片輪播嗎
下一篇
[重構倒數第02天] - Slots 與 Render Functions 的進階心法
系列文
[ 重構倒數30天,你的網站不Vue白不Vue ] 31

尚未有邦友留言

立即登入留言