iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
Modern Web

Vue.js 進階心法系列 第 6

深入淺出 Computed

Vue.js 的自我介紹中,只有說自己接近 MVVM 但不是嚴格的 MVVM。

我覺得只要會「自動更新畫面」就算是有 MVVM 的效果至於有沒有達到 MVVM pattern 的結構不用太計較。XDDD

接下來,我們來挑戰一個 computed 和 watch 的解釋,但是不一起比較,這次我們各別介紹,以免兩個人又再度難分難捨。

從資料開始了解 computed

今天的文章比較短,為了避免觀念混淆,所以特別縮短篇幅,集中一個概念的方式介紹。

參考自官網的例子: https://vuejs.org/v2/guide/computed.html

這個例子很好,只是分開講更清楚,避免不細節用掃視的,想快速學習的朋友們,看圖說故事時造成誤會。

<template>
  <div>
    <div><input type="text" v-model="user.firstName"></input></div>
    <div><input type="text" v-model="user.lastName"></input></div>
    <div>{{ fullName }}</div>
  </div>
</template>
export default {
  name: 'demo',
  data() {
    return {
      user: {
        firstName: 'Foo',
        lastName: 'Bar',
      },
    };
  },
  computed: {
    fullName() {
      return this.user.firstName + this.user.lastName;
    },
  }
}

在這個時候,想像一下 user 是來自 API 的 data。

fullName 對 API 來說是一個不存在的欄位。並且,這一個衍生資料只拿來顯示,不會寫入的資料,只透過其它欄位來改它。

整理一下重點

  • 衍生的欄位
  • 只顯示,不可寫入

至於它會連動畫面這件事,不用開發者操心,這件事就交給尤雨渓吧。我們只要負責看好文件寫 code 就好了。

以物件的設計來說

如果,我們要設計一個 User 的物件

class User {
    constructor() {
        this.firstName = 'Foo';
        this.lastName = 'Bar';
    }
    getFullName() {
        return this.firstName + this.lastName
    }
}

以物件的設計來理解,computed 是一種 getter 並且只能讀。
所以,在概念上,computed 屬於一種無法修改的回傳值。

不過實際上,它是一個被快取起來的值,所以依然存在著修改它的可能性,但是這是千千萬萬不要做的事情。要小心。

v-model 加上 computed 縮短在 html 上面的變數

原本

<div><input type="text" v-model="user.firstName"></input></div>

可以改成讓 v-model 裡的文字變少的寫法。

<div><input type="text" v-model="firstName"></input></div>

整體的程式碼如下面這樣

<template>
  <div>
    <div><input type="text" v-model="firstName"></input></div>
    <div><input type="text" v-model="lastName"></input></div>
    <div>{{ fullName }}</div>
  </div>
</template>
export default {
  name: 'demo',
  data() {
    return {
      user: {
        firstName: 'Foo',
        lastName: 'Bar',
      },
    };
  },
  computed: {
    firstName: {
      set(firstName) {
        this.user.firstName = firstName;
      },
      get() {
        return this.user.firstName;
      },
    },
    lastName: {
      set(lastName) {
        this.user.lastName = lastName;
      },
      get() {
        return this.user.lastName;
      },
    },
  }
}

如果 data 在 vuex

https://vuex.vuejs.org/guide/forms.html#two-way-computed-property

照著文件介紹,可以把 computed 的 get/set 讓 v-model 綁著一個名字,並且將 vuex 存取的方式 mutation 的 commit 和 getters (的 getters) 放在 computed 的 get/set 裡面。

會照著文件的指示,明明只是綁兩個欄位而已,卻寫得很多程式碼。如下:

<template>
  <div>
    <div><input type="text" v-model="firstName"></input></div>
    <div><input type="text" v-model="lastName"></input></div>
    <div>{{ fullName }}</div>
  </div>
</template>
export default {
  name: 'demo',
  computed: {
    firstName: {
      set(firstName) {
        this.$store.commit('firstName', firstName)
      },
      get() {
        return this.$store.getters.firstName;
      },
    },
    lastName: {
      set(lastName) {
        this.$store.commit('lastName', lastName)
      },
      get() {
        return this.$store.getters.lastName;
      },
    },
  }
}

若在這時候使用 pure component 就會出現在 pure component 裡面直接改 props 這樣做是不對的,這個之後會另外寫一篇專門介紹

也有另外一派,覺得如果要寫這多,也許就要槙重考慮是不是不要把所有的資料放在 vuex 裡面,必要的再放。這樣對狀態管理上來說是一件麻煩的事情,因為你只要用方便的 vuex 就會需要很麻煩的寫一堆 code

所以,在這個時候,我有延續著我那大膽的猜想,衍生出來的想法「 v-model 不是這麼必要使用呢?」

如果不用 v-model 呢?

這是我偏好的寫法,若把 v-model 解開,就可以不要靠 computed 的 get/set 串資料,直接把語法寫在 html 上面,省掉這些一對一欄位串接的寫法,允許 :value@input 的寫法不一樣,可以擁有不少彈性

這樣一來,上述的兩個麻煩就不再是麻煩了。所以把資料放進 vuex 裡面不是麻煩事,也不會擔心 pure component 的資傳遞是不是光綁欄位就超級麻煩。

而且 script 就可以放進真正複雜的程式碼,一行搞定的,都放 html 裡面

Components Basics — Vue.js中,除了前述的「v-model 可以拆開寫」之外,拆開寫還有提醒兩個要注意的事情。

  1. 接收到的值,在 html 中要用 $event 這個關鍵字
  2. 如果是 <input type="text" @input="$event">$event 要注意它是什麼樣的物件。如果是原生的 input 就可能是原生的 event 物件,如果是 UI component 套件提供的 input 像是 Bootstrap 的 <b-input> 就是 value 本身。
<template>
  <div>
    <div><input
      type="text"
      :value="$store.getters.firstName"
      @input="$store.commit('firstName', $event.target.value)"
    ></div>
    <div><input
      type="text"
      :value="$store.getters.lastName"
      @input="$store.commit('lastName', $event.target.value)"
    ></div>
    <div>{{ fullName }}</div>
  </div>
</template>
export default {
  name: 'demo',
  computed: {
    fullName() {
      return this.user.firstName + this.user.lastName;
    },
  }
}

萬一 @input 出現複雜的情況或非同步的情況。都可以再寫成 mehotd 處理。就會讓複雜的情況進入 script 處理。

在之後 debug 掃視程式碼,也比較快找到容易出錯的複雜程式碼。

這是第一步,讓我感受到不用 v-model 的好處。script 的 code 就變得,讓我感覺不會雜雜的,簡單程式碼不會出現在這。


上一篇
Component 的 Component (不是遞迴)
下一篇
用 watch 搭配服用 immutable
系列文
Vue.js 進階心法30

尚未有邦友留言

立即登入留言