點燈坊

失くすものさえない今が強くなるチャンスよ

Vue 之 Computed

Sam Xiao's Avatar 2023-10-14

Computed 為 Vue 的獨門武功,當 Model 改變時,Computed 可以 Reactive 隨之改變,是 Vue 的 Reactive Programming 實踐。

Version

Vue 2.6.11

Computed

computed000

Model 為 Sam,但所顯示的與 Model 不同,此時可透過 Computed 加以修飾。

Implicit Argument

<template>
  <div>
    {{ greeting }}
  </div>
</template>

<script>
let greeting = function() {
  return `Hello ${this.name}`
}

export default {
  name:'App',
  data:() => ({
    name: 'Sam'
  }),
  computed: {
    greeting
  }
}
</script>

第 2 行

<div>
  {{ greeting }}
</div>

HTML template 綁定的 greeting 為 computed,而不是 model。

14 行

data:() => ({
  name: 'Sam'
}),

data 宣告 name model,其值為 Sam

17 行

computed: {
  greeting
}

computed 宣告 greeting computed,如同 data 一樣,computed 本質是 function。

第 8 行

let greeting = function() {
  return `Hello ${this.name}`
}

實作 greeting computed,以 implicit argument this 直接讀取 model,因此一定要使用 function expression。

name model 改變時,greeting computed 會 reactive 跟著改變。

Explicit Argument

<template>
  <div>
    {{ greeting }}
  </div>
</template>

<script>
let greeting = function({ name }) {
  return `Hello ${name}`
}

export default {
  name:'App',
  data:() => ({
    name: 'Sam'
  }),
  computed: {
    greeting
  }
}
</script>

第 8 行

let greeting = function({ name }) {
  return `Hello ${name}`
}

Implicit argument 方式雖然方便,但使用 this 是它的至命傷,這使得 computed 無法抽成 free function,也無法 Function Pipeline。

Computed 也支援 explicit argument 寫法,在 argument 內使用 object destructuring 取得 model。

Arrow Function

<template>
  <div>
    {{ greeting }}
  </div>
</template>

<script>
let greeting = ({ name }) => `Hello ${name}`

export default {
  name:'App',
  data:() => ({
    name: 'Sam'
  }),
  computed: {
    greeting
  }
}
</script>

第 8 行

let greeting = ({ name }) => `Hello ${name}`

既然沒使用 this,就可安心使用 arrow function。

Function Pipeline

<template>
  <div>
    {{ greeting }}
  </div>
</template>

<script>
import { pipe, prop, concat } from 'ramda'

let greeting = pipe(
  prop('name'),
  concat(`Hello `)
)

export default {
  name:'App',
  data:() => ({
    name: 'Sam'
  }),
  computed: {
    greeting
  }
}
</script>

第 10 行

let greeting = pipe(
  prop('name'),
  concat(`Hello `)
)

既然 model 是來自 argument,可進一步使用 Ramda 的 prop() 使其 point-free。

Method

<template>
  <div>
    {{ greeting(name) }}
  </div>
</template>

<script>
let greeting = name => `Hello ${name}`

export default {
  name:'App',
  data:() => ({
    name: 'Sam'
  }),
  methods: {
    greeting
  }
}
</script>

第 2 行

<div>
  {{ greeting(name) }}
</div>

Computed 的一個限制是不能自行傳遞 argument,若想傳遞 argument,則要使用 method。

15 行

methods: {
  greeting
}

method 宣告 greeting method,其本質也是 function。

第 8 行

let greeting = name => `Hello ${name}`

由於 method 使用 explicit argument,而沒使用 this,因此可使用 arrow function。

Point-free

<template>
  <div>
    {{ greeting(name) }}
  </div>
</template>

<script>
import { concat } from 'ramda'

let greeting = concat('Hello ')

export default {
  name:'App',
  data:() => ({
    name: 'Sam'
  }),
  methods: {
    greeting
  }
}
</script>

10 行

let greeting = concat('Hello ')

由於 method 為 explicit argument,沒使用 this 為 pure function,因此也可使用 Ramda 的 concat() 使其 point-free。

Prop

<template>
  <div>
    <Greeting :name="name"></Greeting>
  </div>
</template>

<script>
import Greeting from '@/components/Greeting'

export default {
  name:'App',
  components: {
    Greeting
  },
  data:() => ({
    name: 'Sam'
  })
}
</script>

第 3 行

<Greeting :name="name"></Greeting>

Computed 雖然不能接受 explicit argument,但卻能接受 prop,將 name model 以 prop 傳入 Greeting component。

Greeting.vue

<template>
  <div>{{ greeting }} </div>
</template>

<script>
let greeting = function() {
  return `Hello ${this.name}`
}

export default {
  name: 'Greeting',
  props: {
    name
  },
  computed: {
    greeting
  }
}
</script>

12 行

props: {
  name
},

宣告 name prop。

第 6 行

let greeting = function() {
  return `Hello ${this.name}`
}

greeting computed 可藉由 this 讀取 name prop。

Conclusion

  • Computed 傳統寫法是使用 implicit argument 讀取 model,這使得 computed 只能使用 function expression,且不方便抽成 free function 與使用 Function Pipeline
  • Computed 後來也支援 explicit argument 寫法,這使得 computed 也能使用 arrow function 實現,也方便抽成 free function 與使用 Function Pipeline
  • 若不帶 argument,computed 與 method 功能相同,優先考慮使用 computed,因為其有 cache,效能較 method 優
  • Computed 雖然不能接受 argument,但卻能接受 prop,因此在建立自已的 component 時,computed 非常實用

Reference

Vue, computed