コードリポジトリ
gitee
プロジェクトの作成
まず、Nodeがインストールされていることを確認し、その後viteを使用してプロジェクトを作成します。
vite
1 2 3
| npm create vite vue-learn cd vue-learn npm i
|

プロジェクトに入って、helloコンポーネントを削除します。

そしてapp.vueの内容を空にして、以下のように変更します。
1 2 3 4
| <template> <div>hello world</div> </template>
|
実行します。
Optionスタイル
Vue2とVue3では大きな変化があります。一般的に、Vue2はオプションスタイルを使用し、Vue3はコンポジションスタイルを使用します。
data レスポンシブデータ
まず、非レスポンシブデータを見てみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div> <div id="counter">count</div> <button id="btn">+1</button> </div> </template>
<script> export default { mounted() { let count = 0; const counter = document.getElementById("counter"); counter.innerHTML = count; const btn = document.getElementById("btn"); btn.addEventListener("click", () => { count += 1; console.log(count); }); } }; </script>
|
この場合、ボタンをクリックしてもページは変わりませんが、countが増えていることがわかります。

ページとデータが同期して変わるようにするためには、レスポンシブデータが必要です。dataを使って定義できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div> <div id="counter">{{ count }}</div> <button id="btn" @click="count += 1">+1</button> </div> </template>
<script> export default { data() { return { count: 0 }; } }; </script>
|
これでページを開き、ボタンをクリックすると、countの値が同期して増えるようになります。
テンプレート構文
インターポレーション
はVueのインターポレーション構文で、データをページに表示することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> <div>{{ text }}</div> </template>
<script> export default { name: "O1_template-grammar", data() { return { text: "インターポレーション構文" }; } }; </script>
|
ディレクティブ
コンテンツレンダリング
v-text v-html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <p v-text="textContent"></p> <p v-html="htmlContent"></p> </div> </template>
<script> export default { data() { return { textContent: "これはv-textでレンダリングされたコンテンツです", htmlContent: "<strong>これはv-htmlでレンダリングされた<strong>HTML</strong>コンテンツです</strong>" }; } }; </script>
|
属性バインディング
v-bind :attrName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <img v-bind:src="imageUrl" alt="画像"> <a :href="linkUrl">クリックしてジャンプ</a> </div> </template>
<script> export default { data() { return { imageUrl: "https://raw.githubusercontent.com/malred/malcode-site-photos/master/img/getqrcode.png", linkUrl: "https://malcode-site.github.io/", }; } }; </script>
|
イベントバインディング
v-on @eventName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div> <button v-on:click="()=>{console.log('v-on')}">クリック(v-on)</button> <button @click="()=>{console.log('@')}">クリック(@ショートカット)</button> </div> </template>
<script> export default { data() { return { message: "" }; } }; </script>
|
フォーム双方向バインディング
v-model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div> <input type="text" v-model="inputValue"> <p>入力ボックスの値は:{{ inputValue }}</p> </div> </template>
<script> export default { data() { return { inputValue: "" }; } }; </script>
|
条件付きレンダリング
v-if v-else v-else-if v-show
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <p v-if="isVisible">コンテンツが表示されました(v-if)</p> <p v-show="isVisible">コンテンツが表示されました(v-show)</p> <button @click="()=>this.isVisible = !this.isVisible">可視性の切り替え</button> </div> </template>
<script> export default { data() { return { isVisible: true }; } }; </script>
|
リストレンダリング
v-for
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div> <ul> <li v-for="(item, index) in items" :key="index"> {{ item }} </li> </ul> </div> </template>
<script> export default { data() { return { items: ["リンゴ", "バナナ", "オレンジ"] }; } }; </script>
|
computed 計算プロパティ
- 説明: computedは、依存するレスポンシブデータに基づく計算プロパティを宣言するためのVueの機能です。依存関係に基づいて結果をキャッシュし、依存するデータが変わったときにのみ再計算されます。
- 使用例:
テンプレート内の複雑な論理式を処理する(文字列連結、フィルタリング、フォーマットなど)。複数のレスポンシブデータから派生した新しい値が必要で、かつ重複計算を避ける場合。テンプレート内に直接書くには適さない複雑な論理を、computedを使ってテンプレートに返す場合。
ケーススタディ: 姓と名を連結してフルネームを作る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div> <input v-model="firstName" placeholder="姓を入力"> <input v-model="lastName" placeholder="名を入力"> <p>フルネーム: {{ fullName }}</p> </div> </template>
<script> export default { data() { return { firstName: "", lastName: "" }; }, computed: { fullName() { return this.firstName + " " + this.lastName; } } }; </script>
|
watch ウォッチャー
- 説明: watchは、あるレスポンシブデータの変化を監視し、データが変わったときに副作用操作(例えば非同期リクエストやデータクリーニングなど)を実行することを可能にします。
- 使用例:
データが変わったときに非同期またはコストの高い操作(API呼び出しなど)を実行したい場合。特定のフィールドの変化を監視して対応する処理を行いたい場合。オブジェクトや配列内部の値の深い変化を監視したい場合(deep:
trueと一緒に使用)。ルートが変わったときにページコンテンツを更新したい場合。
ケーススタディ: 検索ボックスに入力時にAPIリクエストをトリガーする。
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
| <template> <div> <input v-model="searchQuery" placeholder="キーワードを入力して検索"> <p>現在検索中: {{ searchQuery }}</p> </div> </template>
<script> export default { data() { return { searchQuery: "" }; }, watch: { searchQuery(newVal) { if (newVal) { console.log("API検索を呼び出します", newVal); } } } }; </script>
|
メソッド(methods)
説明
methods
は、テンプレート内でイベントバインディングや直接呼び出し可能なメソッドを定義するためのものです。これらのメソッドは、ユーザーの操作処理やデータ変換などの操作に一般的に使用されます。
使用例
- ボタンクリックなどのイベントトリガーのアクションバインディング。
- データ処理やフォーマット出力など、非反応的な計算。
- ユーザーアクションに関連する関数呼び出し。
例: ボタンをクリックしてカウントし、その回数を表示する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div> <button @click="increment">+1</button> <p>クリック回数: {{ count }}</p> </div> </template>
<script> export default { data() { return { count: 0 } }, methods: { increment() { this.count++; } } } </script>
|
ライフサイクル(lifecycle)
説明
Vueインスタンスが作成されてから破棄されるまでのプロセスをライフサイクルと呼びます。Vueは、異なるステージで特定のロジックを実行できるように、いくつかのフック関数を提供しています。
一般的なライフサイクルフックと使用例
フック関数 |
トリガータイミング |
使用例 |
beforeCreate |
インスタンス初期化後、データオブザーバーの前 |
ほとんど使用されない |
created |
インスタンスが既に作成され、データが準備完了 |
データの初期化、API呼び出しによるデータ取得 |
beforeMount |
マウント開始前 |
ほとんど使用されない |
mounted |
マウント完了後 |
DOM操作、第三者ライブラリの初期化、要素サイズの取得 |
beforeUpdate |
データ更新前 |
パフォーマンス最適化、状態の記録 |
updated |
データ更新後 |
DOM更新後の操作 |
beforeUnmount |
インスタンス破棄前 |
タイマーのクリア、イベントのアンバンド |
unmounted |
インスタンス破棄後 |
リソースの解放 |
例: ライフサイクルデモコンポーネント
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| <template> <div> <h2>ライフサイクルデモコンポーネント</h2> <p>コンポーネント読み込み時間:{{ loadedTime }} 秒</p> </div> </template>
<script> export default { data() { return { loadedTime: 0, showContent: true, timer: null }; }, beforeCreate() { console.log("🚀 beforeCreate: インスタンス初期化完了、データとメソッドはまだ初期化されていない"); }, created() { console.log("✅ created: データがロードされた、API呼び出しやデータ初期化が可能"); setTimeout(() => { console.log("シミュレートされたAPIリクエスト完了"); }, 1000); }, beforeMount() { console.log("🧭 beforeMount: テンプレートのコンパイルが完了したが、まだDOMにマウントされていない"); }, mounted() { console.log("🎉 mounted: コンポーネントがページにレンダーされた、DOM操作やタイマーのスタートが可能"); this.timer = setInterval(() => { this.loadedTime += 1; }, 1000); }, beforeUpdate() { console.log("🔄 beforeUpdate: データが更新される前に、DOMはまだ更新されていない"); }, updated() { console.log("✅ updated: データとDOMが同期して更新された"); }, beforeUnmount() { console.log("🛑 beforeUnmount: コンポーネントが破棄される前に、タイマーやイベントリスナーなどのリソースをクリーンアップ"); clearInterval(this.timer); }, unmounted() { console.log("👋 unmounted: コンポーネントが破棄され、すべてのバインディングが解除された"); } }; </script>
|
コンポーネント(components)
コンポーネント間のデータ渡し
親から子へ(props)
親コンポーネントはpropsを通じてデータを子コンポーネントに渡します。これは一方向のデータフローです。
親コンポーネント(Parent.vue)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div> <h2>親コンポーネント</h2> <Child :message="parentMsg"/> </div> </template>
<script> import Child from './Child.vue';
export default { components: {Child}, data() { return { parentMsg: 'これは親コンポーネントからのメッセージ' }; } }; </script>
|
子コンポーネント(Child.vue)
1 2 3 4 5 6 7 8 9 10 11 12
| <template> <div> <p>子コンポーネントが受信したメッセージ:{{ message }}</p> </div> </template>
<script> export default { props: ['message'] }; </script>
|
子から親へ($emit)
子コンポーネントは$emitを使ってイベントをトリガーし、データを親コンポーネントに送信します。
親コンポーネント(Parent.vue)
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
| <template> <div> <h2>親コンポーネント</h2> <Child @from-child="handleFromChild"/> <p>子コンポーネントからのメッセージ:{{ childMsg }}</p> </div> </template>
<script> import Child from './Child.vue';
export default { components: {Child}, data() { return { childMsg: '' }; }, methods: { handleFromChild(msg) { this.childMsg = msg; } } }; </script>
|
子コンポーネント(Child.vue)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <div> <button @click="sendToParent">クリックして親コンポーネントに送信</button> </div> </template>
<script> export default { methods: { sendToParent() { this.$emit('from-child', 'こんにちは、私は子コンポーネントです'); } } }; </script>
|
兄弟間の通信(Event Bus)
非親子コンポーネント間の通信は、「イベントバス」またはVuex状態管理を使用して実現できます。
EventBusの作成(event-bus.js)※Vue2でのみサポートされています。Vue3では別の方法を使用してください。
1 2 3 4
| import Vue from 'vue';
export const EventBus = new Vue();
|
兄弟コンポーネントA - Sender.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <button @click="sendMessage">兄弟コンポーネントにメッセージを送る</button> </div> </template>
<script> import {EventBus} from '../event-bus.js';
export default { methods: { sendMessage() { EventBus.$emit('brother-msg', 'コンポーネントAからのメッセージ'); } } }; </script>
|
兄弟コンポーネントB - Receiver.vue
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
| <template> <div> <p>兄弟コンポーネントからのメッセージ:{{ msg }}</p> </div> </template>
<script> import {EventBus} from '../event-bus.js';
export default { data() { return { msg: '' }; }, mounted() { EventBus.$on('brother-msg', (msg) => { this.msg = msg; }); }, beforeUnmount() { EventBus.$off('brother-msg'); } }; </script>
|
親子間の値渡し
祖先コンポーネントはprovideでデータを提供し、子孫コンポーネントはinjectで受け取ります。逐次的に渡す必要はありません。
祖先コンポーネント GrandParent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div> <h2>祖先コンポーネント</h2> <Parent/> </div> </template> <script> import Parent from './Parent.vue';
export default { components: {Parent}, provide() { return { theme: 'dark', appName: 'MyApp' }; } }; </script>
|
中間コンポーネント Parent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <div> <h3>中間コンポーネント</h3> <Child/> </div> </template> <script> import Child from './Child.vue';
export default { components: {Child} }; </script>
|
子コンポーネント Child.vue
1 2 3 4 5 6 7 8 9 10 11 12
| <template> <div> <p>祖先コンポーネントから注入されたテーマ:{{ theme }}</p> <p>アプリ名:{{ appName }}</p> </div> </template> <script> export default { inject: ['theme', 'appName'] }; </script>
|
Composition(組合せ)スタイル
Vue 3はComposition APIを導入し、より柔軟で再利用可能な方法でロジックコードを整理する手段を提供します。特に大規模プロジェクトやロジックの再利用シナリオに適しています。
🧩 基本概念
- Vue 2のOption APIとは異なり、Composition APIはより関数型プログラミングに近いです。
- setup()関数を使用してdata、methods、createdなどのオプションを置き換えます。
- ref、reactive、computed、watchなどの関数を使用して反応型データを管理できます。
- カスタムロジックのカプセル化(Custom Hooks)をサポートします。
📦 setup()関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script> import {ref} from 'vue';
export default { setup() { const count = ref(0);
function increment() { count.value++; }
return {count, increment}; } } </script>
|
Vue 3はさらにsetupのシンタックスシュガーを提供し、Composition
APIを使用するためのより簡潔な方法を提供します。これにより、コンポーネントの定義方法が大幅に簡素化され、開発者はより直感的で効率的に反応型ロジックを記述できます。
このモードでは、setup()関数を明示的にエクスポートしたり、テンプレートで使用する変数を返したりする必要はありません。setup内で宣言されたすべての変数と関数は、テンプレート内で直接使用できます。
1 2 3 4 5 6 7 8
| <script setup> const count = ref(0);
function increment() { count.value++; } </script>
|
🔁 refとreactive
種類|用途説明|
ref|基本型の反応型データ(例:number、string)を作成|
reactive|オブジェクトや配列の反応型参照を作成|
1 2
| const count = ref(0); const user = reactive({name: '張三', age: 25});
|
🧠 computed計算プロパティ
1 2 3
| import {computed} from 'vue';
const fullName = computed(() => firstName.value + ' ' + lastName.value);
|
👀 watch監視器
1 2 3 4 5
| import {watch} from 'vue';
watch(count, (newVal, oldVal) => { console.log('countが変更されました:', newVal); });
|
⏱ ライフサイクルフック
Vue 3のComposition APIは対応するライフサイクルフック関数を提供します:
オプションスタイルフック |
組合せスタイル |
beforeCreate |
❌ 不要 |
created |
❌ 不要 |
beforeMount |
onBeforeMount |
mounted |
onMounted |
beforeUpdate |
onBeforeUpdate |
updated |
onUpdated |
beforeUnmount |
onBeforeUnmount |
unmounted |
onUnmounted |
1 2 3 4 5
| import {onMounted} from 'vue';
onMounted(() => { console.log('コンポーネントがマウントされました'); });
|
コンポーネント間の値渡し
この部分ではPinia/Vuexなどの状態管理ライブラリを使用して実装できます。基本的な理解で十分です。
🧱 一、親コンポーネント → 子コンポーネント(props)
definePropsを使用して親コンポーネントから渡されたデータを受け取ります。
親コンポーネント Parent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13
| <template> <div> <h2>親コンポーネント</h2> <Child :message="parentMsg"/> </div> </template> <script setup> import {ref} from 'vue'; import Child from './Child.vue';
const parentMsg = ref('これは親コンポーネントからのメッセージです'); </script>
|
子コンポーネント Child.vue
1 2 3 4 5 6 7 8 9 10
| <template> <div> <p>子コンポーネントが受け取ったメッセージ:{{ message }}</p> </div> </template> <script setup> const props = defineProps(['message']); </script>
|
🔁 二、子コンポーネント → 親コンポーネント(emit / defineEmits)
defineEmitsを使用してイベントをトリガーし、親コンポーネントに通知します。
親コンポーネント Parent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <h2>親コンポーネント</h2> <Child @from-child="handleFromChild"/> <p>子コンポーネントから受け取ったメッセージ:{{ childMsg }}</p> </div> </template> <script setup> import {ref} from 'vue'; import Child from './Child.vue';
const childMsg = ref('');
function handleFromChild(msg) { childMsg.value = msg; } </script>
|
子コンポーネント Child.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <div> <button @click="sendToParent">親コンポーネントに送信</button> </div> </template> <script setup> const emit = defineEmits(['from-child']);
function sendToParent() { emit('from-child', 'こんにちは、私は子コンポーネントです'); } </script>
|
👯♂️ 三、兄弟コンポーネント間の値渡し(EventBus / mitt)
Vue 3はVue 2のEventBusをサポートしていません。共通の親コンポーネントを通じてデータを渡すことができます。
🌳 四、階層間の値渡し(provide / inject)
provideを使用して深いコンポーネントにデータを注入し、injectで受け取ります。
祖先コンポーネント GrandParent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <div> <h2>祖先コンポーネント</h2> <Parent/> </div> </template> <script setup> import {provide} from 'vue'; import Parent from './Parent.vue'; provide('theme', 'dark'); provide('appName', 'MyApp'); </script>
|
中間コンポーネント Parent.vue
1 2 3 4 5 6 7 8 9 10
| <template> <div> <h3>中間コンポーネント</h3> <Child/> </div> </template> <script setup> import Child from './Child.vue'; </script>
|
子コンポーネント Child.vue
1 2 3 4 5 6 7 8 9 10 11 12 13
| <template> <div> <p>祖先コンポーネントから注入されたテーマ:{{ theme }}</p> <p>アプリ名:{{ appName }}</p> </div> </template> <script setup> import {inject} from 'vue'; const theme = inject('theme'); const appName = inject('appName'); </script>
|
社群
你可以在这些平台联系我: