使用 Function 思考問題

Vue 若要使用 OOP 風格開發,可以使用 Class Component,還可搭配 TypeScript,Vue CLI 預設已經整好環境。Vue 原本風格,介於 OOP 與 FP 之間,沒使用 class,但卻大量使用 this,這仍是 OOP 產物,若要更具 FP 風格,可以參考本文的方法。

Version


Vue 2.5.17
Vuex 3.0.1
Vue CLI 3.2.1

Component


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div>
<ul>
<li v-for="(item, index) in todos" :key="index">
{{ item.id }} : {{ item.title }}
</li>
</ul>
</div>
</template>

<script>
import axios from 'axios';

const url = 'https://jsonplaceholder.typicode.com/todos';

const mounted = function() {
axios.get(url)
.then(res => this.todos = res.data.slice(0, 3));
};

export default {
name: 'HelloWorld',
data: () => ({
todos: [],
}),
mounted,
};
</script>

16 行

1
2
3
4
const mounted = function() {
axios.get(url)
.then(res => this.todos = res.data.slice(0, 3));
};

mounted() 傳統都會寫在 object 內,這屬於 OOP 風格,建議可將 computedwatchmethodhooks … 等都拉出來獨立寫成 function,這樣會更有 FP 感覺。

因為 mouted() 要使用 this.todos,所以只能使用 Anonymous Function。

then() 之後可放心使用 Arrow Function,因為 Arrow Function 沒有自己的 this,而是 parent scope 的 this,也就是 mounted()this,因此沒有問題。

但實務上 then() 之後可能不只一行,可能會想抽出獨立的 function。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
<div>
<ul>
<li v-for="(item, index) in todos" :key="index">
{{ item.id }} : {{ item.title }}
</li>
</ul>
</div>
</template>

<script>
import axios from 'axios';

const url = 'https://jsonplaceholder.typicode.com/todos';

const saveTodos = vm => res => vm.todos = res.data.slice(0, 3);

const mounted = function() {
axios.get(url)
.then(saveTodos(this));
};

export default {
name: 'HelloWorld',
data: () => ({
todos: [],
}),
mounted,
};
</script>

16 行

1
2
3
4
5
6
const saveTodos = vm => res => vm.todos = res.data.slice(0, 3);

const mounted = function() {
axios.get(url)
.then(saveTodos(this));
};

為了希望抽出來的 saveTodos() 為 Arrow Function,也就是想徹底擺脫 this,因此將 saveTodos() 改為 Higher Order Function,將 this 傳入為 vm,如此就能將 this 全部改成 vm,徹底擺脫 this

若想在 Vue 在 component 寫法更 FP,應盡量使用 function,至於要使用 Anonymous Function 或 Arrow Function,取決於 3 個原則:

  • 若該 function 要使用 this 存取 Vue 的 datapropertycomputedmethod 等,要使用 Anonymous Function
  • 若該 function 沒用到 this 存取 Vue 的 datapropertycomputedmethod 等,可使用 Arrow Function
  • this 傳入抽出來的 Higer Order Function ,則可使用 Arrow Function

也就是盡量使用 Arrow Function,除非要使用 this

Vuex


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';

Vue.use(Vuex);

const url = 'https://jsonplaceholder.typicode.com/todos';

const setTodos = (state, payload) => state.todos = payload;

const saveTodos = commit => ({ data }) => commit('setTodos', data.slice(0, 3));

const getTodos = ({ commit }) => axios.get(url).then(saveTodos(commit));

export default new Vuex.Store({
state: {
todos: [],
},
mutations: {
setTodos,
},
actions: {
getTodos,
},
});

若 data 是放在 Vuex 的 store,則有另一番氣象。

第 9 行

1
const setTodos = (state, payload) => state.todos = payload;

Mutation 因為完全沒用到 this,可安心使用 Arrow Function。

13 行

1
const getTodos = ({ commit }) => axios.get(url).then(saveTodos(commit));

Action 也因為完全沒用到 this,可安心使用 Arrow Function。

11 行

1
const saveTodos = commit => ({ data }) => commit('setTodos', data.slice(0, 3));

至於 then() 的部分,也因為沒用到 this,使用 Arrow Function 完全沒問題。

在 Vuex 可大膽全部使用 Arrow Function,因為沒有 this 後顧之憂,並可搭配 Destructuring Assignment

Conclusion


  • 若要更接近 FP 風格,Vue 可全部使用 function,無論是 computedmethodwatch …,其本質都是 function,唯若要存取 Vue Instance 的 property,也就是要使用 this 時,就要使用 Anonymous Function,不能使用 Arrow Function,其餘之處應該盡量使用 Arrow Function
  • Vuex 內可大膽完全使用 Arrow Function,還可搭配 Destructuring Assignment

Sample Code


完整的範例可以在我的 GitHub 上找到

2018-12-28