コード設計を学びたい人におすすめの本はこちら
記事をご覧いただきありがとうございます。
この記事では、Vue3で導入された Composition APU について、従来のAPI スタイルである、Options API と比較して解説しています。
Vueの学習を始めたばかりの人や Options API から移行を考えている人などは有益な情報になると思います。
英語で学習したい方はまずはこちらの記事をご覧ください。
英文の読み方について解説しています。
Vue コンポーネントは、Options API とComposition API という2 つの異なる API スタイルで作成できます。Options APIはVue 2の従来の設計アプローチで、dataやmethodsなどをオプションとして使用します。Composition APIはVue 3の新しい方法で、関連するロジックを関数でまとめ、再利用性と可読性を向上させます。
英語で学ぶ
Vue components can be created < using two different API styles: [ the Options API and the Composition API. ]> ( -⑤ ) The Options API is <[ the traditional design ] approach [ used ( in Vue 2 )] >, [ where < data and methods > are defined ( as options within the component. )] ( ② ) ( On the other hand, ) the Composition API is a new method [ introduced ( in Vue 3, )] [ which allows you < to encapsulate < related logic within functions, >>] [ improving reusability and code readability. ]( ② ) This article will provide a detailed explanation [ of these two API styles. ] ( ③ )
リスニングする
Options API とは
Options API は Vue コンポーネントを構築する従来の方法であり、Vue の当初からコンポーネントを構築するための主要な方法でした。これは、data, methods, computed, watch などのオプションを使用して、コンポーネントの動作と状態を定義することができます。
英語で学ぶ
The Options API is the traditional method [ for building Vue components ] and has been a primary approach [ for constructing components ( in Vue from the beginning. )]( ② ) It allows you < to define the behavior and state [ of a component ] ( using options [ such as data, methods, computed, watch, and more. ])> ( ⑤ )
リスニングする
<template>
<div>
<p>{{ message }}</p>
<p>Message Length: {{ messageLength }}</p>
<button @click="reverseMessage">Reverse Message</button>
<div>
<p>Log Output:</p>
<p v-for="log in logs" :key="log">{{ log }}</p>
</div>
</div>
</template>
<script>
export default {
// A function that returns the initial reactive state for the component instance.
// After the instance is created, the reactive data object can be accessed as this.$data
data() {
return {
message: "Hello, Vue!",
logs: []
};
},
// Declare methods to be mixed into the component instance.
// Declared methods can be directly accessed on the component instance, or used in template expressions.
methods: {
reverseMessage() {
this.message = this.message.split('').reverse().join('');
}
},
// Computed is an option in Vue components used to define reactive computed properties
// These computed properties automatically recalculate when dependent data properties change, providing a new value.
computed: {
messageLength() {
return this.message.length;
}
},
// Watch is an option in the Vue Options API used to monitor specific data properties and execute custom logic when those properties are modified.
watch: {
message(newValue, oldValue) {
this.logs.push(`Message changed from "${oldValue}" to "${newValue}" (Watch Triggered)`);
}
},
// Mounted is a part of the lifecycle hooks in Vue's Options API.
// This option allows you to execute custom logic immediately after a Vue component has been successfully attached to the DOM and rendered.
mounted() {
this.logs.push("Component has been mounted (Mounted Hook Triggered). ");
}
};
</script>
Options API の問題点
コンポーネント間のロジックの再利用性の欠如
Options API ではリアクティブな値にアクセスするためにthisを経由する必要があるため、viewからロジックを切り離すことができず、コンポーネントのロジックを再利用することが難しくなります。Option APIでもmixinの機能を使えばdataやmethodsをまとめ、再利用することが可能ですが、いくつかの欠点があります。
英語で学ぶ
( Because you need < to access reactive values ( through ‘this’, )>) you cannot separate the logic ( from the view, ) [ which imposes constraints ( when reusing component logic. )]( ③ )( Although you can use mixins ( in the Options API ) ( to bundle ‘data‘ and ‘methods‘ for reuse, ) there are several drawbacks. ( ② )
リスニングする
・Mixin の欠点
mixinのthisはコンポーネントを参照するため疎結合になっていない
export default {
data () {
return {
greeting: '',
world: 'World'
}
},
computed: {
hello: function () {
return this.greeting + ' ' + this.world + '!'
}
}
}
<template>
<p>{{hello}}</p>
</template>
<script>
import mixin from './mixins/mixin'
export default {
data: function () {
return {
greeting: 'Hello'
}
},
mixins: [mixin]
}
</script>
mixin.jsファイルに定義されたmixinは、greetingとworldというデータを提供し、computedプロパティhelloを計算します。このmixinはHello.vueコンポーネントで使用されています。
Hello.vueコンポーネント内では、mixinsオプションを使用してmixinを取り込み、greetingデータを’Hello’に設定します。そして、mixinで定義されたhelloをテンプレート内で表示します。
ここで問題が生じます。mixin内のcomputedプロパティhelloは、greetingとworldデータを参照しています。しかし、greetingデータはHello.vueコンポーネント内で再定義されており、mixinのgreetingとは別のものです。このため、mixin内のcomputedプロパティhelloは、Hello.vueコンポーネント内のgreetingデータに依存しています。
これにより、mixinとHello.vueコンポーネント間の結合が生じ、mixinはコンポーネントの内部状態に依存しています。このような結合は疎結合の原則に違反しており、実装によってはコンポーネントと mixin 側どちらを変更すると壊れてしまう可能性があります。
英語で学ぶ
< The mixin [ defined ( in the ‘mixin.js’ file )]> provides data, [ ‘greeting,’ and ‘world,’ ] and calculates the computed property ‘hello.’ ( ③ ) This mixin is used ( in the ‘Hello.vue’ component. ) ( –③ )
( Within the ‘Hello.vue’ component, ) the ‘mixins’ option is used ( to incorporate the mixin, setting the ‘greeting’ data ( to ‘Hello.’ )) ( –③ ) < The ‘hello’ property [ defined ( in the mixin )]> is then displayed ( in the template. ) ( ② )
A problem arises here.( ① ) < The computed property ‘hello’ [ inside the mixin ]> references the ‘greeting’ and ‘world’ data.( ③ ) However, the ‘greeting’ data is redefined ( within the ‘Hello.vue’ component ) ( –③ ) and is different ( from the ‘greeting’ in the mixin. ) ( ③ ) ( As a result, ) < the computed property ‘hello’ [ inside the mixin ] > depends ( on the ‘greeting’ data [ within the ‘Hello.vue’ component. ]) ( ① )
This creates a coupling [ between the mixin and the ‘Hello.vue’ component, ] ( as the mixin is now dependent ( on the internal state [ of the component. ])) ( ③ ) Such coupling violates the principle [ of loose coupling ] ( ③ ) and depending ( on the implementation, ) < changes [ to either the component or the mixin ]> may break the system.( ③ )
リスニングする
・名前空間の競合
export default {
data() {
return {
message: 'Mixin A Message',
};
},
};
// MixinB.js
export default {
data() {
return {
message: 'Mixin B Message',
};
},
};
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
import MixinA from './MixinA';
import MixinB from './MixinB';
export default {
mixins: [MixinA, MixinB],
data() {
return {
message: 'Component Message',
};
},
};
</script>
このコードでは、MyComponent コンポーネントが MixinA と MixinB の2つの異なるミックスインを使用しています。さらに、コンポーネント自体も message データを持っています。
この状況で、名前空間の衝突が発生します。MixinA と MixinB はそれぞれ message データを提供し、MyComponent コンポーネントも同じ名前の message データを持っています。
この結果、コンポーネント内での message データの挙動は不明確になり、予測不能な結果が生じる可能性があります。
英語で学ぶ
( In this code, ) the MyComponent component is using two different mixins, ( MixinA and MixinB. )( ③ ) Additionally, < the component itself also > has a message data property.( ③ )
( In this scenario, ) namespace collisions occur. ( ① ) < MixinA and MixinB > each provide message data, and the MyComponent component also defines a message data [ with the same names. ]) ( ③ )
( As a result, ) < the behavior [ of the message data ] ( within the component )> becomes unclear, ( leading ( to unpredictable outcomes. )) ( ② )
リスニングする
可読性の低下
Vue.jsのOptions APIでは、コンポーネントの関連要素(データ、メソッド、ライフサイクルフックなど)が単一のオブジェクト内に格納されます。このアプローチは小規模なアプリケーションでは問題ありませんが、大規模なプロジェクトでは関連要素が分散してしまう可能性があります。
例えば、大規模なプロジェクトでは、1つのコンポーネントに多くのデータやメソッドが含まれることが一般的です。Options APIを使用する場合、これらのデータとメソッドは同じオブジェクト内に存在しますが、コードが成長するにつれてそのオブジェクトは膨れ上がり、個々の要素がどこにあるのか特定するのが難しくなります。関連要素が散在し、コンポーネントが複雑になると、コードの可読性が低下し、メンテナンスやデバッグが難しくなります。
英語で学ぶ
( In Vue.js’s Options API, ) < related components elements [ such as data, methods, lifecycle hooks, etc. ]> are stored ( within a single object. ) ( –③ ) ( While this approach works well ( for small-scale applications, )) it can lead ( to the dispersion [ of related elements ] [ in larger projects. ]) ( ① )
( For instance, ) ( in larger projects, ) it (仮主語) is common ( to have many data and methods [ within a single component. (真主語)]) ( ② ) ( When using the Options API, ) < these data and methods > are all ( within the same object. ) ( As the codebase grows, ) this object can become unwieldy, ( making it challenging ( to pinpoint [ where individual elements are located. ])) ( ② ) ( As related elements become scattered and the component grows ( in complexity, )) code readability diminishes, ( making maintenance and debugging more difficult. ) ( ① )
リスニングする
Composition API
Compostion APIとはリアクティブな値やリアクティブな値に関連した処理をコンポーネントから分割して扱えるようにしたAPI 形式です。 コンポーネントのロジックをカプセル化することで、ロジックの再利用と効果的な管理を可能です。
英語で学ぶ
The Composition API is an API format [ that allows ( for the separation and handling of reactive values and their associated logic in a component. )] ( ② ) ( By encapsulating component logic, ) it enables the effective management and reusability [ of logic. ] ( ③ )
リスニングする
Composition API サンプルコード
Options API と同じ内容のサンプルコード
<template>
<div>
<p>{{ message }}</p>
<p>Message Length: {{ messageLength }}</p>
<button @click="reverseMessage">Reverse Message</button>
<div>
<p>Log Output:</p>
<p v-for="log in logs" :key="log">{{ log }}</p>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted } from "vue"
const message = ref("Hello, Vue!");
const reverseMessage = () => {
message.value = message.value.split('').reverse().join('');
};
const messageLength = computed(() => message.value.length);
const logs = ref([]);
watch(message, (newValue, oldValue) => {
logs.value.push(`Message changed from "${oldValue}" to "${newValue}"`);
});
onMounted(() => {
logs.value.push("Component has been mounted (Mounted Hook Triggered).");
});
</script>
なぜ Composition API を使うのか
Vue.jsのComposition APIは、コンポーネント内のロジックを抽出し、再利用可能なコードブロックとして定義するのに非常に優れた手段を提供します。この概念は、コードの保守性向上、効率的な開発、バグの減少など、多くの利点をもたらします。
英語で学ぶ
The Vue.js Composition API provides an excellent means [ to extract and define component logic [ as reusable code. blocks ]] ( ③ ) This concept brings numerous advantages, [ including improved code maintainability, efficient development, and reduced bugs. ] ( ③ )
リスニングする
1. ロジックの抽出
Composition APIを使用することで、コンポーネント内のロジックを抽出することができます。これにより、コンポーネントが複雑化するのを防ぎ、コードが分かりやすく整理されます。
英語で学ぶ
The Composition API allows ( for the extraction [ of component logic, ]) ( preventing components ( from becoming overly complex and making the code more organized. )) ( ① ) ( For example, ) < logic [ that handles form validation, data filtering, or API request processing, ]> [ which can be shared ( across different components, )] can be extracted ( as separate functions. )( –③ )
リスニングする
2. ロジックの再利用性
Composition APIのもう1つの大きな利点は、開発者が複数のコンポーネントにわたってロジックを抽出して再利用できることです。これは、同じ機能を共有する必要がある複数のコンポーネントが一般的である大規模なアプリケーションで特に役立ちます。
英語で学ぶ
< Another significant advantage [ of the Composition API ]> is the ability [ for developers [ to extract and reuse logic ( across multiple components. )]] ( ② ) This is particularly beneficial ( in large-scale applications [ where < the need to share the same functionality across several components > is common. ])( ② )
リスニングする
Composables
Vue アプリケーションの文脈で「コンポーザブル(composable)」とは、Vue の Composition API を活用して状態を持つロジックをカプセル化して再利用するための関数です。
以下のサンプルコードを通じて、状態を持つロジックをカプセル化して再利用する関数の使用例を示します。
英語で学ぶ
( In the context [ of Vue applications, ]) a “composable” is a function [ that leverages Vue’s Composition API ( to encapsulate and reuse stateful logic. )] ( ② )
The sample code below demonstrates the use [ of function ] [ to encapsulate and reuse stateful logic. ] ( ③ )
リスニングする
import { ref } from 'vue';
// Function encapsulating stateful counter logic
export function useCounter() {
// Counter state
const counter = ref(0);
// Action to increment the counter
const incrementCounter = () => {
counter.value++;
}
// Action to reset the counter
const resetCounter = () => {
counter.value = 0;
}
// Return the counter state and actions
return {
counter,
incrementCounter,
resetCounter
};
}
この useCounter は、カウンターの状態(ステート)を管理し、増加とリセットのアクションを提供します。
このポーザブルは、状態を持つロジックをカプセル化し、再利用可能な関数として提供しています。これにより、同じカウンターロジックを持つ複数のコンポーネントで再利用でき、コードの保守性が向上します。
英語で学ぶ
The useCounter manages the state [ of a counter ] and provides actions [ for incrementing and resetting it. ] ( ③ ) This composable encapsulates stateful logic, ( providing it ( as a reusable function. )) ( ③ ) ( As a result, ) the same counter logic can be reused ( across multiple components, improving code maintainability. ) ( –③ )
リスニングする
<template>
<div>
<p>Counter: {{ counter }}</p>
<button @click="incrementCounter">Increment</button>
<button @click="resetCounter">Reset</button>
</div>
</template>
<script setup>
import { useCounter } from './useCounter'; // Import custom composition
const { counter, incrementCounter, resetCounter } = useCounter();
</script>
コンポーネント内で、useCounter を使用し、カウンターの状態を管理するための関数を再利用しています。これにより、同じ状態を持つ他のコンポーネントでも同様のカウンターロジックを再利用できるため、コードがより効率的になります。
英語で学ぶ
( Within the components, ) useCounter is used ( to manage the counter’s state, ) ( allowing the functions ( for its management ) ( to be reused. ) ( –③ ) This enables the same counter logic [ to be reused ( in other components )] [ that share the same state, resulting ( in more efficient and maintainable code. )]( ③ )
リスニングする
この記事で出てきた英語5選
- composable… → 構成[組み立て]可能な
- composition… → 構成
- On the other hand… → 一方で
- As a result… → 結果として
- such as 名詞… → 名詞のような