Vue.js リアクティビティ内部解剖:Proxy/ref/computed を図で読み解く
Vue.js 3 を使っていると、reactive や ref を書くだけでデータの変更が画面に自動反映される「魔法」を体験しますよね。しかし、その内部でどのような仕組みが動いているのか、気になったことはありませんか?
この記事では、Vue.js 3 のリアクティビティシステムの心臓部である Proxy、ref、computed の内部実装を、図解を交えながら詳しく解説します。内部構造を理解することで、より効率的なコードが書けるようになり、予期せぬバグも回避できるようになるでしょう。
背景
Vue.js のリアクティビティシステムは、バージョン 2 から 3 への移行で大きく変化しました。Vue 2 では Object.defineProperty を使用していましたが、Vue 3 では ES6 の Proxy を採用することで、より強力で柔軟なリアクティビティを実現しています。
Vue 2 と Vue 3 のリアクティビティ比較
| # | 項目 | Vue 2 | Vue 3 |
|---|---|---|---|
| 1 | 実装技術 | Object.defineProperty | Proxy |
| 2 | 配列の検知 | 制限あり(7 つのメソッドのみ) | すべての操作を検知 |
| 3 | プロパティの追加 | Vue.set が必要 | 自動検知 |
| 4 | パフォーマンス | ゲッター/セッターを全プロパティに設定 | 必要な時のみトラッキング |
| 5 | TypeScript サポート | 限定的 | 完全サポート |
リアクティビティシステムの役割
Vue.js のリアクティビティシステムは、以下の 3 つの主要な役割を担っています。
依存関係の追跡(Dependency Tracking): データが読み取られた時、どのコンポーネントや computed がそのデータに依存しているかを記録します。
変更の検知(Change Detection): データが変更された時、その変更を検知して通知します。
更新のトリガー(Update Triggering): 依存している全てのコンポーネントや computed に対して、再計算や再レンダリングをトリガーします。
下図は Vue.js のリアクティビティシステムの基本的なデータフローを示しています。
mermaidflowchart TB
data["データ変更"]
proxy["Proxy による<br/>インターセプト"]
track["依存関係の<br/>追跡"]
trigger["更新トリガー"]
render["再レンダリング"]
data -->|set| proxy
proxy -->|notify| track
track -->|依存先を特定| trigger
trigger -->|更新指示| render
render -->|get| proxy
proxy -->|値を取得| data
データが変更されると、Proxy がその操作をキャッチし、追跡している依存関係に基づいて適切なコンポーネントだけを更新します。この仕組みにより、効率的な再レンダリングが実現されているのです。
課題
Vue.js のリアクティビティシステムを使う上で、開発者が直面する課題がいくつかあります。これらの課題を理解することで、より適切な実装ができるようになります。
リアクティビティが失われる問題
Vue.js を使っていると、意図せずリアクティビティが失われてしまう場面に遭遇することがあります。
typescriptimport { reactive } from 'vue';
// リアクティブオブジェクトの作成
const state = reactive({ count: 0 });
上記のようにリアクティブなオブジェクトを作成しても、以下のような操作でリアクティビティが失われてしまいます。
typescript// ❌ 分割代入でリアクティビティが失われる
const { count } = state;
console.log(count); // 0(ただの数値)
// ❌ プリミティブ値の取り出しでリアクティビティが失われる
let localCount = state.count;
localCount++; // state.count は更新されない
これらの操作では、リアクティブな Proxy オブジェクトから値を取り出してしまうため、リアクティビティの追跡ができなくなります。
パフォーマンスの課題
リアクティビティシステムは便利ですが、使い方を誤るとパフォーマンスの問題を引き起こします。
typescriptimport { reactive, computed } from 'vue';
const state = reactive({
items: [] as number[],
filter: '',
});
// ❌ 毎回配列全体を処理する非効率な computed
const filteredItems = computed(() => {
console.log('computed 再計算');
return state.items.filter((item) => item > 10);
});
上記のコードでは、state.items や state.filter のどちらが変更されても computed が再計算されます。実際には filter が変わった時には再計算が不要なのに、余分な処理が発生してしまうのです。
型安全性の課題
TypeScript を使用している場合、リアクティビティシステムの型推論が期待通りに動かないことがあります。
typescriptimport { ref } from 'vue';
// number 型の ref を作成
const count = ref(0);
// ❌ 型エラーが発生しない(期待と異なる動作)
count.value = '100' as any;
ref や reactive を使う際、適切な型定義をしないと、実行時エラーの原因となります。
下図は、リアクティビティが失われる主な原因を整理したものです。
mermaidflowchart LR
reactive[reactive オブジェクト]
destructure[分割代入]
primitive[プリミティブ値取り出し]
spread[スプレッド構文]
lost[リアクティビティ喪失]
reactive --|const {x} = obj|--> destructure
reactive --|let val = obj.x|--> primitive
reactive --|{...obj}|--> spread
destructure --> lost
primitive --> lost
spread --> lost
これらの課題を解決するためには、Vue.js のリアクティビティシステムの内部構造を理解することが不可欠です。
解決策
Vue.js のリアクティビティシステムが抱える課題を解決するには、Proxy、ref、computed の内部実装を理解し、適切に使い分ける必要があります。それぞれの仕組みを見ていきましょう。
Proxy によるリアクティビティの実現
Vue 3 のリアクティビティの核心は、JavaScript の Proxy オブジェクトです。Proxy は対象オブジェクトの操作をインターセプトし、カスタム動作を挿入できます。
typescript// Proxy の基本構造
const target = { count: 0 };
const handler = {
get(target, key) {
console.log(`getting ${String(key)}`);
return target[key];
},
set(target, key, value) {
console.log(`setting ${String(key)} to ${value}`);
target[key] = value;
return true;
},
};
上記のコードでは、ハンドラオブジェクトに get と set のトラップを定義しています。これにより、プロパティへのアクセスと代入を監視できるようになります。
typescriptconst proxy = new Proxy(target, handler);
// プロパティへのアクセスがインターセプトされる
console.log(proxy.count); // "getting count" と表示され、0 が返る
// プロパティへの代入がインターセプトされる
proxy.count = 1; // "setting count to 1" と表示される
Vue.js では、この Proxy の仕組みを使って、データの読み取り時に依存関係を記録し、データの変更時に更新をトリガーしています。
reactive 関数の内部実装
Vue 3 の reactive 関数は、内部で Proxy を使用してリアクティブなオブジェクトを作成します。
typescriptimport { reactive } from 'vue';
// reactive の簡易的な実装イメージ
function myReactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
// 依存関係の追跡
track(target, key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const result = Reflect.set(
target,
key,
value,
receiver
);
// 更新のトリガー
trigger(target, key);
return result;
},
});
}
track 関数は現在実行中のエフェクト(コンポーネントのレンダリング関数や computed など)を記録し、trigger 関数は記録されたエフェクトを再実行します。
typescript// 依存関係を管理するグローバル変数
let activeEffect = null;
const targetMap = new WeakMap();
function track(target, key) {
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
// 現在のエフェクトを依存関係に追加
dep.add(activeEffect);
}
上記の track 関数は、WeakMap と Map を使って、対象オブジェクトとプロパティごとに依存関係を管理しています。WeakMap を使うことで、オブジェクトがガベージコレクションの対象になった際、自動的にメモリが解放されます。
typescriptfunction trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (!dep) return;
// 依存しているエフェクトを全て実行
dep.forEach((effect) => effect());
}
trigger 関数は、記録された全てのエフェクトを実行することで、UI の更新をトリガーします。
下図は、Proxy を使った依存関係の追跡と更新のフローを示しています。
mermaidsequenceDiagram
participant C as コンポーネント
participant P as Proxy
participant T as track関数
participant D as 依存関係マップ
participant TR as trigger関数
C->>P: data.count にアクセス
P->>T: track(data, 'count')
T->>D: 依存関係を記録
Note over D: {data: {count: [effect]}}
C->>P: data.count = 1
P->>TR: trigger(data, 'count')
TR->>D: 依存関係を取得
D->>TR: [effect]
TR->>C: effect() 実行
Note over C: 再レンダリング
このシーケンス図から、データの読み取り時に依存関係が記録され、データの変更時に依存しているコンポーネントが更新される流れがわかります。
ref によるプリミティブ値のリアクティビティ
Proxy はオブジェクトにしか使えないため、数値や文字列などのプリミティブ値をリアクティブにするには別の方法が必要です。そこで登場するのが ref です。
typescriptimport { ref } from 'vue';
// ref の簡易的な実装イメージ
function myRef(value) {
return new RefImpl(value);
}
class RefImpl {
private _value;
constructor(value) {
this._value = value;
}
get value() {
// 依存関係の追跡
track(this, 'value');
return this._value;
}
set value(newValue) {
this._value = newValue;
// 更新のトリガー
trigger(this, 'value');
}
}
ref は、プリミティブ値をオブジェクトでラップすることで、Proxy と同様の仕組みでリアクティビティを実現しています。value プロパティへのアクセスをゲッター/セッターでインターセプトすることで、依存関係の追跡と更新のトリガーを行います。
typescript// ref の使用例
const count = ref(0);
// .value でアクセス(track が呼ばれる)
console.log(count.value); // 0
// .value で代入(trigger が呼ばれる)
count.value++;
console.log(count.value); // 1
ref を使うことで、プリミティブ値に対してもリアクティビティが提供され、値の変更を自動的に検知できるようになります。
computed による派生データの効率化
computed は、他のリアクティブデータから派生した値を効率的に計算するための仕組みです。computed は依存するデータが変更された時だけ再計算され、結果はキャッシュされます。
typescriptimport { reactive, computed } from 'vue';
const state = reactive({
firstName: 'Taro',
lastName: 'Yamada',
});
// computed の使用例
const fullName = computed(() => {
console.log('computed 再計算');
return `${state.firstName} ${state.lastName}`;
});
上記のコードでは、fullName は firstName または lastName が変更された時だけ再計算されます。
typescript// computed の簡易的な実装イメージ
class ComputedRefImpl {
private _value;
private _dirty = true;
private effect;
constructor(getter) {
this.effect = () => {
const oldEffect = activeEffect;
activeEffect = this.effect;
this._value = getter();
activeEffect = oldEffect;
};
}
get value() {
if (this._dirty) {
this.effect();
this._dirty = false;
}
track(this, 'value');
return this._value;
}
}
computed は _dirty フラグを使って、再計算が必要かどうかを管理しています。依存するデータが変更されると _dirty が true になり、次回アクセス時に再計算が行われます。
typescript// 依存データが変更された時の処理
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (!dep) return;
dep.forEach((effect) => {
// computed の場合は _dirty を true にする
if (effect.computed) {
effect.computed._dirty = true;
} else {
effect();
}
});
}
この仕組みにより、computed は不要な再計算を避けつつ、常に最新の値を提供できるのです。
下図は、computed のキャッシュと再計算の仕組みを示しています。
mermaidstateDiagram-v2
[*] --> Dirty
Dirty --> Calculating: value にアクセス
Calculating --> Clean: 計算完了
Clean --> Clean: value にアクセス<br/>(キャッシュ返却)
Clean --> Dirty: 依存データ変更
Dirty --> Dirty: 依存データ変更<br/>(すでに Dirty)
computed は Dirty 状態の時だけ再計算を行い、Clean 状態ではキャッシュされた値を返します。この状態管理により、パフォーマンスの最適化が実現されています。
リアクティビティ喪失の回避方法
reactive や ref を使う際、リアクティビティが失われないようにするには、以下の方法を使います。
typescriptimport { reactive, toRefs } from 'vue';
const state = reactive({
count: 0,
message: 'Hello',
});
// ✅ toRefs を使って分割代入してもリアクティビティを保持
const { count, message } = toRefs(state);
// count は ref になっているので .value でアクセス
console.log(count.value); // 0
count.value++; // state.count も更新される
toRefs は、reactive オブジェクトの各プロパティを ref に変換します。これにより、分割代入してもリアクティビティが維持されます。
typescript// toRefs の簡易的な実装イメージ
function toRefs(object) {
const ret = {};
for (const key in object) {
ret[key] = toRef(object, key);
}
return ret;
}
function toRef(object, key) {
return {
get value() {
return object[key];
},
set value(newValue) {
object[key] = newValue;
},
};
}
toRef は、元のオブジェクトへの参照を保持したまま、ref のインターフェースを提供します。
具体例
ここまで学んだ知識を活かして、実践的な例を見ていきましょう。リアクティビティシステムを正しく理解することで、より効率的なコードが書けるようになります。
ショッピングカートの実装
EC サイトのショッピングカート機能を例に、reactive と computed を組み合わせた実装を見てみましょう。
typescriptimport { reactive, computed } from 'vue';
// 商品の型定義
interface Product {
id: number;
name: string;
price: number;
quantity: number;
}
まず、商品の型を定義します。各商品には ID、名前、価格、数量の情報が含まれます。
typescript// ショッピングカートの状態管理
const cart = reactive({
items: [] as Product[],
discountRate: 0,
});
// カートに商品を追加する関数
function addToCart(product: Omit<Product, 'quantity'>) {
const existingItem = cart.items.find(
(item) => item.id === product.id
);
if (existingItem) {
// すでにカートにある商品は数量を増やす
existingItem.quantity++;
} else {
// 新しい商品をカートに追加
cart.items.push({ ...product, quantity: 1 });
}
}
addToCart 関数は、カートに商品を追加します。すでに同じ商品がある場合は数量を増やし、ない場合は新規追加します。reactive オブジェクトの配列を操作しているため、この変更は自動的に UI に反映されます。
typescript// 合計金額を計算する computed
const totalPrice = computed(() => {
const subtotal = cart.items.reduce((sum, item) => {
return sum + item.price * item.quantity;
}, 0);
// 割引を適用
return subtotal * (1 - cart.discountRate);
});
// 商品の総数を計算する computed
const totalItems = computed(() => {
return cart.items.reduce((sum, item) => {
return sum + item.quantity;
}, 0);
});
totalPrice と totalItems は computed プロパティとして定義されています。これらは cart.items や cart.discountRate が変更された時だけ再計算され、それ以外はキャッシュされた値が使われます。
typescript// 使用例
addToCart({ id: 1, name: 'ノートPC', price: 80000 });
addToCart({ id: 2, name: 'マウス', price: 2000 });
addToCart({ id: 1, name: 'ノートPC', price: 80000 }); // 数量が2になる
console.log(totalPrice.value); // 162000
console.log(totalItems.value); // 3
// 割引を適用
cart.discountRate = 0.1;
console.log(totalPrice.value); // 145800(10%割引)
この実装では、商品の追加や割引率の変更が自動的に合計金額に反映されます。computed を使うことで、計算ロジックを一箇所にまとめ、効率的な再計算が実現できています。
下図は、ショッピングカートのデータフローを示しています。
mermaidflowchart TB
user["ユーザー操作"]
add["addToCart<br/>関数呼び出し"]
cart["cart.items<br/>配列更新"]
total["totalPrice<br/>再計算"]
items["totalItems<br/>再計算"]
ui["UI 更新"]
user -->|商品追加| add
user -->|割引適用| cart
add -->|push/quantity++| cart
cart -->|依存トリガー| total
cart -->|依存トリガー| items
total --> ui
items --> ui
ユーザー操作により cart が更新されると、依存している computed が自動的に再計算され、UI が更新されます。
フォームバリデーションの実装
次に、ref と computed を使った実用的なフォームバリデーションを実装してみましょう。
typescriptimport { ref, computed } from 'vue';
// フォームの入力値を ref で管理
const email = ref('');
const password = ref('');
const confirmPassword = ref('');
各フォームフィールドを ref で管理します。ref を使うことで、プリミティブな文字列値をリアクティブにできます。
typescript// メールアドレスのバリデーション
const emailError = computed(() => {
if (email.value === '') {
return '';
}
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(email.value)) {
return 'メールアドレスの形式が正しくありません';
}
return '';
});
emailError は computed プロパティで、email の値が変更されるたびに自動的にバリデーションが実行されます。
typescript// パスワードのバリデーション
const passwordError = computed(() => {
if (password.value === '') {
return '';
}
if (password.value.length < 8) {
return 'パスワードは8文字以上で入力してください';
}
if (!/[A-Z]/.test(password.value)) {
return 'パスワードに大文字を含めてください';
}
if (!/[0-9]/.test(password.value)) {
return 'パスワードに数字を含めてください';
}
return '';
});
パスワードのバリデーションでは、長さや文字種別をチェックしています。computed を使うことで、password が変更されるたびに自動的にバリデーションが実行されます。
typescript// パスワード確認のバリデーション
const confirmPasswordError = computed(() => {
if (confirmPassword.value === '') {
return '';
}
if (password.value !== confirmPassword.value) {
return 'パスワードが一致しません';
}
return '';
});
確認用パスワードのバリデーションでは、元のパスワードと一致するかをチェックしています。
typescript// フォーム全体が有効かどうかを判定
const isFormValid = computed(() => {
return (
email.value !== '' &&
password.value !== '' &&
confirmPassword.value !== '' &&
emailError.value === '' &&
passwordError.value === '' &&
confirmPasswordError.value === ''
);
});
isFormValid は、全てのフィールドが入力され、かつエラーがない場合に true を返します。これを使ってフォームの送信ボタンの有効/無効を制御できます。
typescript// 使用例
function handleSubmit() {
if (isFormValid.value) {
console.log('フォーム送信:', {
email: email.value,
password: password.value,
});
} else {
console.log('バリデーションエラーがあります');
}
}
// 入力値の変更
email.value = 'test@example.com';
password.value = 'Password123';
confirmPassword.value = 'Password123';
console.log(isFormValid.value); // true
この実装では、ユーザーが入力するたびに自動的にバリデーションが実行され、リアルタイムでエラーメッセージを表示できます。
パフォーマンス最適化の実例
computed を使う際、依存関係を最小限にすることでパフォーマンスを向上させることができます。
typescriptimport { reactive, computed } from 'vue';
// 大量のデータを持つ state
const state = reactive({
items: Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i}`,
category: i % 5,
price: Math.random() * 1000,
})),
selectedCategory: 0,
searchTerm: '',
});
1 万件のアイテムを持つ状態を定義します。カテゴリとキーワードでフィルタリングする機能を実装しましょう。
typescript// ❌ 非効率な実装:複数の computed を連鎖させる
const filteredByCategory = computed(() => {
console.log('カテゴリフィルタ実行');
return state.items.filter((item) => {
return item.category === state.selectedCategory;
});
});
const filteredBySearch = computed(() => {
console.log('検索フィルタ実行');
return filteredByCategory.value.filter((item) => {
return item.name.includes(state.searchTerm);
});
});
上記の実装では、selectedCategory が変更されると両方の computed が再計算され、searchTerm が変更されても両方が再計算されます。
typescript// ✅ 効率的な実装:1つの computed で処理する
const filteredItems = computed(() => {
console.log('フィルタ実行');
return state.items.filter((item) => {
const matchCategory =
item.category === state.selectedCategory;
const matchSearch =
state.searchTerm === '' ||
item.name.includes(state.searchTerm);
return matchCategory && matchSearch;
});
});
1 つの computed にまとめることで、必要最小限の再計算で済むようになります。また、早期リターンを活用することで、さらに効率化できます。
typescript// ✅ さらに効率化:早期リターンを活用
const optimizedFilteredItems = computed(() => {
console.log('最適化フィルタ実行');
let result = state.items;
// カテゴリフィルタ
result = result.filter(
(item) => item.category === state.selectedCategory
);
// 検索キーワードが空の場合は早期リターン
if (state.searchTerm === '') {
return result;
}
// 検索フィルタ
return result.filter((item) =>
item.name.includes(state.searchTerm)
);
});
検索キーワードが空の場合は早期リターンすることで、不要な処理を省略できます。
typescript// パフォーマンス比較
console.time('非効率');
state.searchTerm = 'test';
console.log(filteredBySearch.value.length);
console.timeEnd('非効率');
console.time('効率的');
state.searchTerm = 'test';
console.log(optimizedFilteredItems.value.length);
console.timeEnd('効率的');
最適化された computed を使うことで、処理時間を大幅に短縮できます。
下図は、computed の最適化による処理フローの違いを示しています。
mermaidflowchart LR
subgraph 非効率な実装
A1["データ変更"]
A2["computed 1<br/>再計算"]
A3["computed 2<br/>再計算"]
A4["結果"]
A1 --> A2
A2 --> A3
A3 --> A4
end
subgraph 効率的な実装
B1["データ変更"]
B2["computed<br/>再計算"]
B3["結果"]
B1 --> B2
B2 --> B3
end
複数の computed を連鎖させると、それぞれが独立して再計算されるため非効率です。1 つの computed にまとめることで、処理が最適化されます。
watchEffect を使った副作用の管理
リアクティビティシステムは、副作用(サーバーへのリクエスト、ローカルストレージへの保存など)を管理するためにも使えます。
typescriptimport { reactive, watchEffect } from 'vue';
// アプリケーション設定
const settings = reactive({
theme: 'light' as 'light' | 'dark',
fontSize: 16,
language: 'ja',
});
アプリケーションの設定を reactive で管理します。
typescript// 設定が変更されたらローカルストレージに保存
watchEffect(() => {
console.log('設定を保存中...');
const settingsJson = JSON.stringify({
theme: settings.theme,
fontSize: settings.fontSize,
language: settings.language,
});
localStorage.setItem('app-settings', settingsJson);
});
watchEffect は、内部で使用されているリアクティブデータが変更されるたびに自動的に実行されます。computed と似ていますが、値を返すのではなく副作用を実行する点が異なります。
typescript// 使用例
settings.theme = 'dark'; // watchEffect が実行される
settings.fontSize = 18; // watchEffect が実行される
// 初回ロード時の設定復元
function loadSettings() {
const saved = localStorage.getItem('app-settings');
if (saved) {
const parsed = JSON.parse(saved);
Object.assign(settings, parsed);
}
}
この実装により、設定が変更されるたびに自動的にローカルストレージに保存されます。
typescript// クリーンアップ関数を使った例
import { ref, watchEffect } from 'vue';
const userId = ref(null);
const userData = ref(null);
watchEffect((onCleanup) => {
if (!userId.value) return;
console.log(
`ユーザー ${userId.value} のデータを取得中...`
);
// API リクエストの実行
const controller = new AbortController();
fetch(`/api/users/${userId.value}`, {
signal: controller.signal,
})
.then((res) => res.json())
.then((data) => {
userData.value = data;
})
.catch((err) => {
if (err.name !== 'AbortError') {
console.error('エラー:', err);
}
});
// クリーンアップ:前回のリクエストをキャンセル
onCleanup(() => {
console.log('前回のリクエストをキャンセル');
controller.abort();
});
});
onCleanup を使うことで、次の watchEffect が実行される前に、前回のリクエストをキャンセルできます。これにより、不要な API リクエストを防ぎ、パフォーマンスを向上させることができます。
typescript// 使用例
userId.value = 1; // API リクエスト開始
userId.value = 2; // 前回のリクエストがキャンセルされ、新しいリクエスト開始
userId.value = 3; // 前回のリクエストがキャンセルされ、新しいリクエスト開始
連続して値が変更された場合でも、最新のリクエストだけが有効になります。
まとめ
この記事では、Vue.js 3 のリアクティビティシステムの核心である Proxy、ref、computed の内部実装を詳しく解説しました。
Vue 3 のリアクティビティシステムは、JavaScript の Proxy を使ってデータの読み取りと変更をインターセプトし、依存関係を自動的に追跡します。reactive 関数はオブジェクトを Proxy でラップし、ref 関数はプリミティブ値をオブジェクトで包むことでリアクティビティを提供します。
computed は依存するデータが変更された時だけ再計算され、結果をキャッシュすることでパフォーマンスを最適化しています。この仕組みを理解することで、不要な再計算を避け、効率的なコードを書くことができます。
リアクティビティが失われる問題は、toRefs や toRef を使うことで回避できます。また、watchEffect を使えば、副作用を適切に管理し、クリーンアップ処理も実装できます。
内部実装を理解することで、Vue.js のリアクティビティシステムをより効果的に活用でき、パフォーマンスの問題やバグを未然に防ぐことができるでしょう。実際のプロジェクトでこれらの知識を活かし、より洗練されたアプリケーションを開発してください。
関連リンク
articleVue.js リアクティビティ内部解剖:Proxy/ref/computed を図で読み解く
articleVue.js 本番運用チェックリスト:CSP/SRI/Cache-Control/エラーログの要点
articleVue.js コンポーネント API 設計:props/emit/slot を最小 API でまとめる
articleVue.js `<script setup>` マクロ辞典:defineProps/defineEmits/defineExpose/withDefaults
articleVue.js を macOS + yarn で最短セットアップ:ESLint/Prettier/TS/パスエイリアス
articleVue.js の状態管理比較:Pinia vs Vuex 4 vs 外部(Nanostores 等)実運用レビュー
articleWebRTC が「connecting」のまま進まない:ICE 失敗を 5 分で切り分ける手順
articleWeb Components が “is not a constructor” で落ちる時:定義順序と複数登録の衝突を解決
articleVitest モジュールモック技術の基礎と応用:`vi.mock` / `vi.spyOn` を極める
articleVue.js リアクティビティ内部解剖:Proxy/ref/computed を図で読み解く
articleVite CSS HMR が反映されない時のチェックリスト:PostCSS/Modules/Cache 編
articleTailwind CSS 2025 年ロードマップ総ざらい:新機能・互換性・移行の見取り図
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来