これは、Vue3 + Vite + Pinia +TS + Element-Plus の一連のドキュメントです。最近忙しくて記事を書く時間があまりないので、このシリーズは早く終わらせてください〜
ライフサイクルとテンプレートのリファレンス
この章の前に、応答性の高い API と宣言的レンダリングによる DOM の更新について説明しましたが、DOM の手動操作が必要な一部の複雑な状況では、前の紹介だけでは十分ではありません。
ライフサイクル
各 Vue コンポーネントは、作成時に一連の初期化ステップを実行します。これらのステージで追加の操作を行う必要がある場合は、対応するステージのフックを呼び出す必要があります。
これらの段階には、データ リスナーの設定、テンプレートのコンパイル、DOM へのインスタンスのマウント、データ変更時の DOM の更新などが含まれます。Vue は、ライフサイクルをより深く理解するのに役立つ図を公式に提供しています。
- setup : 赤のすべてのライフサイクル API は、コンポーネントの setup() フェーズ中に同期的に呼び出されます。
- 赤いボックス: さまざまなステージで表されるライフ サイクル。作成した後続のライフ サイクル フックはこのステージで実行されます。
- 主軸の: は、初期化からアンロードまでのコンポーネントのメイン イベントを表します。
ここで簡単に紹介しますが、ライフサイクルフックを紹介すると、この絵がより理解できると思います。
ライフサイクルフック
上記のライフ サイクルを理解した後、対応するサイクルで何かを実行したい場合は、Vue3 の onXxx ライフ サイクル フックを使用します。たとえば、次のようになります。
<スクリプトの設定>
'vue' から { onMounted, onBeforeUnmount } をインポートします
onMounted(() => {
})
onBeforeUnmount(()=>{
})
</script>
Vue2 を使用したことがある場合は、違いに気づくでしょう。
<スクリプト>
デフォルトのエクスポート {
作成した() {
}、
マウントされた() {
}、
beforeDestroy() {
}
}
</script>
利用方法は上に書いた通りで、設定ではライフサイクルAPIにコールバックを注入するだけです。ここでは、Vue2 と Vue3 の比較は行いません。ライフサイクルの順序に従って、すべて新しいものです。
- setup : beforeCreate と created は setup メソッドに置き換えられます。
- onBeforeMount() : コンポーネントがマウントされる前に実行されるコールバック。コンポーネントはリアクティブ状態の設定を完了しましたが、DOM ノードはまだ作成されていません。
- onMounted() : コンポーネントがマウントされた後にコールバックを実行します。通常、コンポーネントによってレンダリングされる DOM ツリーへのアクセスを必要とする副作用を実行するために使用されます。
- onBeforeUpdate() : コンポーネントが反応状態の変化により DOM ツリーを更新する直前にコールバックが実行されます。通常、Vue が DOM を更新する前に DOM 状態にアクセスするために使用されます。
- onUpdated() : コンポーネントが反応状態の変化により DOM ツリーを更新した後に実行されるコールバック。これは、コンポーネントの DOM が更新された後に呼び出されます。通常、更新された DOM にアクセスするために使用されます。ここで DOM を更新するために使用することはできません。ループが発生する可能性があります。
- onBeforeUnmount() : コンポーネント インスタンスがアンマウントされる前に実行されるコールバック。
- onUnmounted() : コンポーネント インスタンスがアンマウントされた後に実行されるコールバック。通常は、タイマー、DOM イベント リスナー、サーバーへの接続などの副作用を手動でクリーンアップするために使用されます。
- onActivated() : コンポーネントがキープアライブとしてアクティブ化されたときに実行されるコールバック。
- onDeactivated() : コンポーネントがキープアライブとして非アクティブ化されたときに実行されるコールバック。
- onErrorCaptured() : 子孫コンポーネントによって渡されたエラーがキャプチャされたときに実行されるコールバック。通常、コンポーネントの状態を変更してユーザーにエラー状態を表示するために使用されます。
- onRenderTracked() は開発モードでのみ使用されます。コールバックは、コンポーネントのレンダリング中にリアクティブな依存関係が追跡されるときに実行されます。通常、依存関係を追跡するためのデバッグに使用されます。
- onRenderTriggered() は開発モードでのみ使用されます。リアクティブな依存関係の変更によりコンポーネントのレンダリングがトリガーされると、コールバックが実行されます。通常は、更新されたデバッグをトリガーするために使用されます。
上記はすべてのライフ サイクルとその呼び出し可能なライフ サイクル フックです。上記のフックでコールバックを渡すと、Vue がライフ サイクルでコールバックをトリガーします。
テンプレートリファレンス参照
ref は、要素またはサブコンポーネントへの参照を登録するために使用されます。
テンプレート参照は、たとえば、データのロード後にテキストまたは説明情報を変更する場合、名前に一致する ref に保存されます。
<スクリプトの設定>
import { ref, onMounted } from "vue";
const h2 = ref(null);
const img = ref(null);
onMounted(() => {
setTimeout(() => {
h2.value.textContent = "データの読み込みが完了しました";
img.value.src = "/src/assets/logo.svg";
}, 3000);
});
</script>
<テンプレート>
<h2 ref="h2">データをロード中...</h2>
<img ref="img" src="./assets/load.svg" alt="" />
</テンプレート>
もちろん、上記のコードの h2 をレスポンシブにし、 h2 で { {}} テンプレート構文を使用してそれを実現することもできます。
コンポーネントリファレンス ref
ref はサブコンポーネントでも使用できますが、これは比較的一般的です。
まずコンポーネントとは何かを説明します。
その前は、単一ファイルのApp.vueを使用していました。プロジェクトがこのファイルにすべてのコードを記述すると、メンテナンスが非常に困難になります。そこで、再利用可能なページをコンポーネント化し、ページをロジックから分離しました。コンポーネントをインポートしてページを作成します。
<スクリプトの設定>
'./Child.vue' から子をインポートします
</script>
<テンプレート>
<子 ref="子" />
</テンプレート>
ref を使用すると、親コンポーネントは子コンポーネントを取得できます。 例:
<スクリプトの設定>
'vue' からインポート { ref, onMounted }
'./Child.vue' から子をインポートします
const 子 = ref(null)
onMounted(() => {
})
</script>
<テンプレート>
<子 ref="子" />
</テンプレート>
子コンポーネントは <script setup> を使用せず、参照されるコンポーネントのインスタンスは子コンポーネントのインスタンスとまったく同じであり、親コンポーネントは子コンポーネントのすべてのプロパティとメソッドにアクセスできることに注意してください。
<script setup> が使用される場合、defineExpose を使用して明示的に公開されない限り、サブコンポーネントはデフォルトでプライベートになります。
もちろん、ほとんどの場合、親コンポーネントと子コンポーネントの間の対話は、ref を使用せずに props と Emit を使用して実現できます。
コンポーネントのパス値の小道具
コンポーネント間での値の受け渡し方法は、親コンポーネントと子コンポーネント間での値の受け渡し、兄弟コンポーネント間での値の受け渡し、遠い相対コンポーネント間での値の受け渡しの 3 つのカテゴリに要約できます。
Vue が値を渡すためにコンポーネントに提供する API は、props と Emit の 2 つです。その中で、親 => 子コンポーネントから props を介して値を渡すことができます。
開発プロセス中に、defineProps() を通じてサブコンポーネントの props を指定する必要があります。親コンポーネントは、HTML パラメーターを宣言するのと同じように値を渡すことも、(v-bind の省略表現) を使用して値を動的に渡すこともできます。
<!-- 親コンポーネント -->
<スクリプトの設定>
「vue」から { ref } をインポートします。
"./components/Children.vue" から子をインポートします。
const hello = ref("こんにちは");
</script>
<テンプレート>
<input v-model="hello" />
<Children msg="hh" :activeMsg="hello" />
</テンプレート>
<!-- サブコンポーネント -->
<script lang="ts" セットアップ>
「vue」から { onMounted } をインポートします。
const props =defineProps({
msg: 文字列、
activeMsg: 文字列
});
onMounted(() => {
console.log("小道具", 小道具);
});
</script>
<テンプレート>
<span>メッセージ: { { msg }}</span>
<span>activeMsg: { { activeMsg }}</span>
</テンプレート>
defineProps() 宣言の後、その中のデータを子コンポーネント テンプレートで使用できます。JavaScript でアクセスするには、defineProps() によって返されるオブジェクトを介してアクセスする必要があります。
知らせ:
- props は読み取り専用であり、単一項目のデータ フローに従います。props を変更しようとすると、prop read-only という警告が表示されます。
- jsで定義したデータとpropsで定義したデータが同じ名前の場合は、jsで定義したものが使用されます。
const activeMsg = "hl";
<span>activeMsg: { { activeMsg }}</span>
- props は、プロジェクトが型検出に TypeScript を使用していないことを確認するための検証オプションを提供します。また、型要件を満たさないデータを回避するために特定のデータ型を決定することもできます。(Vue3 が TypeScript をサポートするようになったので、これはあまり役に立たないと言えます)
インターフェース DataProps {
msg: 文字列;
activeMsg: 文字列;
}
const props =defineProps<DataProps>();
コンポーネントリスナーイベントの発行
子コンポーネントは、emit() を使用して親コンポーネントにデータを渡します。最初のパラメータはイベント名で、その他の追加パラメータは親コンポーネントのリスナー関数に直接渡されます。
親コンポーネントは、@ (v-on) を使用して子コンポーネントの時間を監視し、子コンポーネントによって渡されたパラメータを受け取ることができます。
<!-- 親コンポーネント -->
<テンプレート>
<Children @response="(msg) => (hello = msg)" />
{ { hello }}// サブコンポーネントボタンをクリックすると、子から hello になります
</テンプレート>
<スクリプトの設定>
「vue」から { ref } をインポートします。
"./components/Children.vue" から子をインポートします。
const hello = ref("こんにちは");
</script>
<!-- サブコンポーネント -->
<テンプレート>
<button @click="emit('response', 'hello from child')">発行</button>
</テンプレート>
<script lang="ts" セットアップ>
const Emit = defineEmits(["response"]);
</script>
知らせ:
- $emit の構文はテンプレートで使用でき、イベントをトリガーするオブジェクト Emit を返すために js で使用できるのは、defineEmits のみです。
- トリガー イベントをタイプでマークして、トリガーされたイベントをより正確に制御できます。
const Emit = defineEmits<{
(e: '応答'、msg: 文字列): void
}>()
転送テンプレート - スロットスロット
データを渡すだけでなく、親コンポーネントはスロットを通じて子コンポーネントにテンプレートを渡すこともできます。
<!-- 親コンポーネント -->
<テンプレート>
<Children>スロット ボタンのコンテンツ</Children>
</テンプレート>
<スクリプトの設定>
"./components/Children.vue" から子をインポートします。
</script>
<!-- サブコンポーネント -->
<テンプレート>
<ボタン><スロット /></ボタン>
</テンプレート>
<script lang="ts" セットアップ></script>
デフォルトのコンテンツ
たとえば、デフォルトのコンテンツを設定する場合、親コンポーネントはテンプレート文字を子コンポーネントに渡しませんが、子コンポーネントのボタンのコンテンツにはデフォルトのコンテンツが含まれます。
<!-- 親コンポーネント -->
<テンプレート>
<子供></子供>
</テンプレート>
<スクリプトの設定>
"./components/Children.vue" から子をインポートします。
</script>
<!-- サブコンポーネント -->
<テンプレート>
<ボタン>
<スロット>
コンテンツ
</スロット>
</ボタン>
</テンプレート>
<script lang="ts" セットアップ></script>
名前付きスロット
コンポーネントに複数のソケット アウトレットが含まれている場合は、名前付きソケットを使用してソケットに一意の ID を与え、さまざまなアウトレットによってレンダリングされるコンテンツを決定する必要があります。
<!-- 親コンポーネント -->
<テンプレート>
<子供>
<template v-slot:header> ヘッダー </template>
<template v-slot:button> スロット ボタンの内容 </template>
</子供>
</テンプレート>
<スクリプトの設定>
"./components/Children.vue" から子をインポートします。
</script>
<テンプレート>
<div><スロット名="ヘッダー" /></div>
<ボタン><スロット名="ボタン" /></ボタン>
</テンプレート>
<script lang="ts" セットアップ></script>
v-slot は #、v-slot:header => #header と省略できます。また、v-slot は動的パラメーター (動的スロット名) #[dynamicSlotName] を受け入れることもできます。
スコープ付きスロット
上記のスロットはサブコンポーネントの状態にアクセスできません。シナリオによっては、サブコンポーネントがスロットにデータを渡すことが必要であり、スコープ付きスロットはこの要件を満たすことができます。
<!-- 親コンポーネント -->
<テンプレート>
<子供>
<template v-slot:header="slotProps"> { { slotProps.msg }}</template>
</子供>
</テンプレート>
<スクリプトの設定>
"./components/Children.vue" から子をインポートします。
</script>
<!-- サブコンポーネント -->
<テンプレート>
<input type="text" v-model="msg" />
<div><スロット名="ヘッダ" :msg="msg" /></div>
</テンプレート>
<script lang="ts" セットアップ>
「vue」から { ref } をインポートします。
const msg = ref("");
</script>
正確に言うと、上記の例は名前付きスコープ付きスロットです。共通スコープ スロットの場合は、テンプレートの名前付きスロットを共通スロットに変更するだけで十分です。
戦闘
前のセクションでは、コンポーネントのライフサイクル フック、コンポーネント値の受け渡しのプロパティ、トリガーとなるイベントの発行、テンプレート スロットの理解と応用について説明しました。
ブログの一覧表示機能は分類機能を提供します。
- 分類されたデータは親コンポーネントから取得され、選択されたタイプはコールバックを通じて親コンポーネントに通知されます。
- リストの親コンポーネントは要素のコンテンツとスタイルを制御するために使用され、子コンポーネントはリストの基本サイクルなどの一般的な操作に使用されます。
例にはこの章で学んだ知識が可能な限り含まれていますが、次のコード部分が理解できない場合は、その部分の知識が明確に理解されていないことを意味します。
このセクションの例には、いくつかの ts の型確認が含まれています。
親コンポーネント App.vue :
<script lang="ts" セットアップ>
import { reactive, ref, onMounted } from "vue";
"./components/Children.vue" から子をインポートします。
"./components/ClassifyHeader.vue" から ClassifyHeader をインポートします。
const タグ = リアクティブ({
リスト: ["vue", "react"],
チェック済み: []、
});
関数checkedTags(checked) {
tags.checked = チェック済み;
getData();
}
onMounted(() => {
getData();
});
const listRef = ref();
関数 getData() {
const params = { タグ: tags.checked、ページ: 1 };
console.log("リクエストパラメータ:", params);
setTimeout(() => {
定数データ = [
{ タイトル: "JavaScript 初心者からマスターまで"、ユーザー名: "Chocolate 1999"、日付: "2023-02-11" },
{ タイトル: "Vue3 实战"、ユーザー名: "HearLing"、日付: "2023-03-09" },
];
listRef.value.loadData(データ);
}, 1000);
}
</script>
<テンプレート>
<ClassifyHeader :tags="tags.list" @select="checkedTags" />
<子参照="listRef">
<template #item="{ タイトル、ユーザー名、日付 }">
<div class="アイテム">
<p>{ { タイトル }}</p>
<p class="meta">作成者: { { ユーザー名 }} | 時刻: { { 日付 }}</p>
</div>
</テンプレート>
</子供>
</テンプレート>
<スタイルスコープ></style>
親コンポーネントには次のナレッジ ポイントが含まれます。
- ClassifyHeader 分類コンポーネントで値を渡します。小道具と放出。
- ライフサイクルはマウントされています。コンポーネントがマウントされた後にデータを要求します。
- コンポーネントの参照番号 子コンポーネントを使用する方法。
- 名前付きスコープ付きスロット。
親コンポーネントとカテゴリコンポーネント
親コンポーネントでは、初期の tags.list 値が props を通じて子コンポーネントに渡され、select イベントがリッスンされます。選択トリガーはcheckedTags関数を実行し、親コンポーネントはchecked値を取得してリストデータを再リクエストする操作を実行します。
コンポーネント/ ClassifyHeader.vue コンポーネントを分類します。
<!-- カテゴリ-->
<テンプレート>
<div v-for="props.tags の項目">
<input type="checkbox" :value="item" @click="select(item)" />
{ { アイテム }}
</div>
</テンプレート>
<script lang="ts" セットアップ>
const props =defineProps(["タグ"]);
const Emit = defineEmits<{
(e: "選択"、チェック: string[]): void;
}>();
const チェック: string[] = [];
関数選択(項目) {
const インデックス =checked.indexOf(項目);
if (インデックス !== -1) {
チェックされた.splice(インデックス、1);
} それ以外 {
チェック済み.push(項目);
}
放出(「選択」、チェック済み);
}
</script>
<style lang="scss" スコープ></style>
ClassifyHeader 分類コンポーネントは主に、選択されたカテゴリを記録し、エミットを通じて親コンポーネントにデータを渡します。
親コンポーネントとリストコンポーネント
親コンポーネントは、ref を通じて子コンポーネントのインスタンスを取得し、子コンポーネントによって公開されるloadData メソッドを使用してデータを読み込みます。子コンポーネントはスコープ スロットを通じて親コンポーネントにデータを渡し、親コンポーネントはコンテンツ レイアウトを制御します。
コンポーネント/Children.vueリスト コンポーネント:
<!-- サブコンポーネント -->
<script lang="ts" セットアップ>
「vue」から { ref } をインポートします。
インターフェース項目 {
タイトル: 文字列;
ユーザー名: 文字列;
日付: 文字列;
}
const items = ref<Item[]>([]);
constloadData = (データ) => {
items.value = データ;
};
定義公開({
データを読み込む、
});
</script>
<テンプレート>
<ul>
<li v-if="!items.length">読み込み中...</li>
<li v-for="アイテム内のアイテム">
<スロット名="アイテム" v-バインド="アイテム" />
</li>
</ul>
</テンプレート>
<スタイルスコープ></style>
サブコンポーネントは名前付きスロットを提供し、defineExpose を使用して親コンポーネントのloadData メソッドをスローし、コンポーネントの例を実行します。
上記の例では、実際の知識のほとんどを確認しましたが、印象をさらに深めるために、これらのコードを自分で作成することをお勧めします。
値を渡すその他の方法
上記の props に加えて、値を渡す親子コンポーネントを出力します。依存関係注入 API (provide、inject) を使用することもできます。
- Provide() : 子孫コンポーネントによって注入できる値を提供します。使用法: Provide(/* インジェクション名 /'メッセージ', / 値*/'hello!')。
- inject() : 上位コンポーネントによって提供されたデータを注入します。使用法: const message = inject('message') 。
ブラザー コンポーネントは、親コンポーネントを通じてデータ転送を制御できます。
コンポーネント間の通信には、後で学習する状態管理フレームワークである Pinia などの状態管理ツールを使用できます。
要約する
この章では、まず Vue のライフ サイクルのフローチャートを組み合わせて、各ライフ サイクル フックのトリガー タイミングといくつかのフックの使用シナリオの例を示します。次にrefの役割について話して、最後に話が終わってコンポーネント通信に関するAPIの練習をしました。
ここまでで Vue3 の基礎知識は終了です。実戦講座ではまだ少しだけ勉強する部分が残っています。
TypeScript を学習したことがない方のために、TypeScript の基礎知識も用意しました。コースはそれほど長くはありませんが、実戦に備えて TypeScript の一般的に使用される知識をいくつか紹介します。