vue3 の学習は常に vue2 から切り離せません。
vue2 コンポーネントの通信方法の概要:
1、props:可以实现父子组件,子父组件,甚至兄弟组件通信
2、自定义事件:实现子父组件通信
3、全局事件总线$bus:可以实现任意组件通信
4、pubsub:发布订阅模式实现任意组件通信
5、vuex: 集中式状态管理容器,实现任意组件通信
6、ref:父组件获取子组件实例VC,获取子组件的响应式数据及方法
7、slot:插槽(默认插槽,具名插槽,作用域插槽),实现父子组件通信
vue3 コンポーネントの通信方法の概要:
1、props
2、自定义事件
3、全局事件总线
4、v-model
5、useAttrs方法
6、ref与$parent
7、provide与inject
8、pinia组合式API
9、插槽
vue3コンポーネントの通信方法は以下の通りです。
1. 親子コンポーネントの通信プロパティ
props データは読み取り専用で、親コンポーネントから子コンポーネントに渡され、子コンポーネント内で直接変更することはできません。
子コンポーネントは、defineProps メソッドを使用して、親コンポーネントから渡されたデータを受け入れる必要があります。
親コンポーネント
<template>
<div class="box">
<Child :msg="msg"></Child>
</div>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
import {
ref } from "vue";
let msg= ref(10000);
</script>
サブアセンブリ
<template>
<div class="son">
<p>{
{
msg}}</p>
</div>
</template>
<script setup lang="ts">
let props = defineProps(['msg']); //数组|对象写法都可以
</script>
2. カスタムイベント値の受け渡し
vue フレームワークには 2 種類のイベントがあります: ネイティブ DOM イベントとカスタム イベントです。オリジナルの DOM イベントにより、ユーザーはクリック、dbclick、mouseenter、mouseleave などの Web ページと対話できます。カスタム イベントは、子コンポーネントを実装して、親コンポーネントにデータを渡すことができます
。
ネイティブ DOM イベントが親コンポーネントのコンポーネント タグで直接使用されている場合、vue2 の .native 修飾子を使用してネイティブ DOM イベントに変更できます。vue3 ではネイティブ イベントであり、子コンポーネントのルート ノードにバインドされているイベントと同等です。子コンポーネント内のすべてのコンテンツがこのイベントをトリガーできます。
<Event1 @click="handler2"></Event1>
vue3 には this がなく、コンポーネント インスタンスもありません。defineEmits メソッドを使用して関数を返し、カスタム イベントの
親コンポーネントをトリガーします。
<template>
<div>
<!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
<Event2 @xxx="handler3" @click="handler4"></Event2>
</div>
</template>
<script setup lang="ts">
//引入子组件
import Event2 from './Event2.vue';
const handler3 = (param1,param2)=>{
console.log(param1,param2);
}
const handler4 = (param1,param2)=>{
console.log(param1,param2);
}
</script>
サブアセンブリ
<template>
<div class="child">
<p>我是子组件2</p>
<button @click="handler">点击我触发自定义事件xxx</button>
//直接在模板区触发,当在子组件中定义事件名为click,在父组件中即为自定义事件,不再是原生DOM事件
<button @click="$emit('click','AK47','J20')">点击我触发自定义事件click</button>
</div>
</template>
<script setup lang="ts">
let $emit = defineEmits(['xxx','click']);
//按钮点击回调
const handler = () => {
//第一个参数:事件类型 第二个|三个|N参数即为注入数据
$emit('xxx','东风导弹','航母');
};
</script>
3. ブラザーコンポーネントのパス値
vue3 フレームワークには vue コンストラクターがないため、VM もプロトタイプ オブジェクトも存在しません。また、結合された API 書き込みメソッドのセットアップにはこれがありません。また、プラグイン mitt を使用してグローバル イベント バス機能を実装できます。
mit公式サイトアドレス
npm install --save mitt
src ディレクトリに新しいバス フォルダーを作成します (index.ts ファイル内)。
//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from 'mitt';
const $bus = mitt();
export default $bus;
送信するとイベントがトリガーされ、パラメータが受信されます
サブコンポーネント 1
import $bus from "../../bus";
import {
onMounted } from "vue";
//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() => {
//第一个参数:即为事件类型 第二个参数:即为事件回调
$bus.on("car", (car) => {
console.log(car);
});
});
サブコンポーネント 2
<template>
<div class="child2">
<button @click="handler">赠送车</button>
</div>
</template>
<script setup lang="ts">
//引入$bus对象
import $bus from '../../bus';
//点击按钮回调
const handler = ()=>{
$bus.emit('car',{
car:"法拉利"});
}
</script>
4. v-modelの親子コンポーネント通信
vue2 では親子コンポーネントの通信はできず、vue3 では props と Emit の作業を同時に行うことと同等になります。
5. useAttrsメソッド
useAttrsメソッドはコンポーネントタグの属性とイベントを取得できますが、
propsを併用する場合はuseAttrsよりpropsの優先順位が高くなります。useAttrs メソッドを受け入れた後、props は useAttrs メソッドを取得できません。useAttrs
メソッドはコンポーネント タグのネイティブ イベントだけでなく、カスタム イベントも取得できます。
親コンポーネント、親コンポーネントは子コンポーネントに値を渡す必要はなく、コンポーネントタグ上のタイプ、サイズ、アイコン、ネイティブイベント、カスタムイベントは useAttrs メソッドを通じて取得できます。
<template>
<div>
<!-- 自定义组件 -->
<HintButton type="primary" size="small" :icon="Edit" title="编辑按钮" @click="handler" @xxx="handler"></HintButton>
</div>
</template>
サブアセンブリ
<template>
<div :title="title">
//对象格式:{
type:'primary',size:'small',icon:'Edit',title:'编辑按钮'}
<el-button :="$attrs"></el-button>
</div>
</template>
//引入useAttrs方法:获取组件标签身上属性与事件
import {
useAttrs} from 'vue';
//此方法执行会返回一个对象
let $attrs = useAttrs();
小道具付きのタイトルを受け取った場合
let props =defineProps(['title']);
useAttrs メソッドを受け入れた後は Props を取得できません。
ネイティブ イベントとカスタム イベントの両方が useAttrs メソッドを通じて取得できます。
6. ref と $parent
ref は実際の DOM ノードを取得します。子コンポーネントのインスタンスを取得できます。VC
$parent は、子コンポーネント内の親コンポーネントのインスタンスを取得できます。
1. ref は、
親コンポーネントの ref を通じて子コンポーネントのインスタンスを取得します。
<template>
<div class="box">
<Son ref="son"></Son>
</div>
</template>
import {
ref} from 'vue';
let money = ref(100000000);
//获取子组件的实例,与组件标签上的ref同名,不然拿不到实例
let son = ref();
サブコンポーネントのインスタンスを取得した後、コンポーネントの内部データは外部に対して閉じられており、他の人がアクセスすることはできません。外部からアクセスしたい場合は、defineExpose メソッドを通じて外部に公開する必要があります
。
import {
ref} from 'vue';
//子组件钱数
let money = ref(666);
defineExpose({
money,
})
公開後、親コンポーネントで子コンポーネントのデータを変更できます。
<button @click="handler">修改</button>
//父组件内部按钮点击回调
const handler = ()=>{
//子组件钱数减去10
son.value.money-=10;
}
2. $parentは
サブコンポーネント内にあり、$parentはクリックイベントのパラメータに渡されます(書き方を修正)
サブアセンブリ
<template>
<div class="dau">
<button @click="handler($parent)">点击我</button>
</div>
</template>
import {
ref} from 'vue';
//子组件钱数
let money = ref(999999);
//子组件按钮点击回调
const handler = ($parent)=>{
money.value+=10000;
$parent.money-=10000;
}
$parent を印刷しても
親コンポーネントのデータは取得されないため、親コンポーネントでも同様に公開する必要があります
。
//对外暴露
defineExpose({
money
})
このとき、再度印刷するとデータが残ります。
7. 提供および注入
次世代コンポーネントの通信、Provide(提供)、Inject(注入)を実現できます。
1. 祖先コンポーネント:祖先コンポーネントは子孫コンポーネントにデータを提供します。
import {
ref, provide } from "vue";
let car = ref("法拉利");
Provide メソッドには、キーと値という 2 つのパラメータがあります。
//两个参数:第一个参数就是提供的数据key
//第二个参数:祖先组件提供数据
provide("getData", car);
2. 子孫コンポーネント、サブコンポーネント/孫コンポーネントの使用および値の変更が可能
<button @click="updateCar">更新数据</button>
import {
inject} from 'vue';
//注入祖先组件提供数据
//需要参数:即为祖先提供数据的key
let car = inject('getData');
const updateCar = ()=>{
car.value = '自行车';
}
8. ピニア選択API
vue3 のピニアは大きなパイナップルで、このようなかわいい漫画の画像を見ていると、ピニアの知識を学ぶのはそれほど退屈ではないようです。
Pinia 中国語のドキュメント
pinia は vue2 の vuex に似ています
vuex: 集中管理状態コンテナ、任意のコンポーネント間の通信を実現可能 中核
概念: 状態、突然変異、アクション、ゲッター、モジュール
pinia: 集中管理状態コンテナ、任意のコンポーネント間の通信を実現可能
中核概念: 状態、アクション、ゲッター
1. src ディレクトリの下に新しいストア フォルダーを作成し、新しいindex.ts ファイルと modules ファイルを作成します。ディレクトリは次のとおりです
。
//创建大仓库
import {
createPinia } from 'pinia';
//createPinia方法可以用于创建大仓库
let store = createPinia();
//对外暴露,安装仓库
export default store;
main.tsでの導入と利用
//引入仓库
import store from './store'
app.use(store)
導入が成功すると、
pinia の記述方法: 選択 API と結合 API
1 が表示されます。選択 API は、モジュール、情報に小さなウェアハウスを定義します。
//defineStore 用于定义小仓库,从pinia中获取
//定义info小仓库
import {
defineStore } from "pinia";
//defineStore需要传递两个参数,第一个参数:小仓库名字 第二个参数:小仓库配置对象
//defineStore方法执行会返回一个函数,函数作用就是让组件可以获取到仓库数据
let useInfoStore = defineStore("info", {
//存储数据:state,vuex中state是对象写法,pinia中是函数写法,函数返回的对象才是给组件提供的数据
state: () => {
return {
count: 99,
arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
},
actions: {
//vuex中commit提交,mutation去修改,pinia中没有mutation,直接在action中修改
//注意:函数没有context上下文对象,pinia中使用this获取state中的数据
//没有commit、没有mutations去修改数据
updateNum(a: number, b: number) {
this.count += a;
}
},
getters: {
total() {
let result:any = this.arr.reduce((prev: number, next: number) => {
return prev + next;
}, 0);
return result;
}
}
});
//对外暴露方法
export default useInfoStore;
コンポーネントで使用され、データが変更されると、使用されているすべてのコンポーネントのデータも一緒に変更されます。
import useInfoStore from "../../store/modules/info";
//获取小仓库对象
let infoStore = useInfoStore();
console.log(infoStore);
データを変更する
<button @click="updateCount">点击我修改仓库数据</button>
```bash
//修改数据方法
const updateCount = () => {
//可以直接这么写:
//infoStore.count++
//用$patch方法用一个新的对象替换原来的对象
infoStore.$dispath({
count:1111
})
//仓库调用自身的方法去修改仓库的数据,在info.ts的actions中定义这个方法
infoStore.updateNum(66,77);
};
2. 結合 API、モジュールで小さなウェアハウスを定義、todo
//定义组合式API仓库
import {
defineStore } from "pinia";
import {
ref, computed,watch} from 'vue';
//创建小仓库
let useTodoStore = defineStore('todo', () => {
let todos = ref([{
id: 1, title: '吃饭' }, {
id: 2, title: '睡觉' }, {
id: 3, title: '打豆豆' }]);
let arr = ref([1,2,3,4,5]);
const total = computed(() => {
return arr.value.reduce((prev, next) => {
return prev + next;
}, 0)
})
//务必要返回一个对象:属性与方法可以提供给组件使用
return {
todos,
arr,
total,
updateTodo() {
todos.value.push({
id: 4, title: '组合式API方法' });
}
}
});
export default useTodoStore;
コンポーネントで使用される
<p @click="updateTodo">{
{
todoStore.arr }}{
{
todoStore.total}}</p>
import useInfoStore from "../../store/modules/info";
//获取小仓库对象
let infoStore = useInfoStore();
//引入组合式API函数仓库
import useTodoStore from "../../store/modules/todo";
let todoStore = useTodoStore();
//点击p段落去修改仓库的数据
const updateTodo = () => {
todoStore.updateTodo();
};
9. スロット
スロットには、デフォルト スロット、名前付きスロット、スコープ指定スロットの 3 種類があります。
デフォルトのスロット。スロットは子コンポーネントに配置され、親コンポーネントの子コンポーネントのラベルに追加されたコンテンツはスロットの位置に表示されます。
1. デフォルトのスロット サブ
コンポーネント:
<template>
<div class="box">
<h1>我是子组件默认插槽</h1>
<!-- 默认插槽 -->
<slot></slot>
</div>
</template>
親コンポーネント
<template>
<div class="box">
<Test>
<div>
<pre>大江东去浪淘尽,千古分流人物</pre>
</div>
</Test>
</div>
</template>
2. 名前付きスロット 名前付き
スロットは、名前が付いたスロットです。子コンポーネントはホールを保持し、親コンポーネントはスロット名に従ってコンテンツを追加します。v-slot の記述方法は # に相当し、
v-slot=a は #a に相当します。
サブアセンブリ
<template>
<div class="box">
<h1>具名插槽填充数据a</h1>
<slot name="a"></slot>
<h1>具名插槽填充数据b</h1>
<slot name="b"></slot>
<h1>具名插槽填充数据</h1>
</div>
</template>
親コンポーネント
<template>
<div>
<h1>slot</h1>
<Test1 :todos="todos">
<template v-slot="{ $row, $index }">
<p :style="{ color: $row.done ? 'green' : 'red' }">
{
{
$row.title }}--{
{
$index }}
</p>
</template>
</Test1>
<Test>
<!-- 具名插槽填充a -->
<template #a>
<div>我是填充具名插槽a位置结构</div>
</template>
<!-- 具名插槽填充b v-slot指令可以简化为# -->
<template #b>
<div>我是填充具名插槽b位置结构</div>
</template>
</Test>
</div>
</template>
3. スコープ
スロット スコープ スロットを理解するのに少し時間がかかりましたが、これまでの vue2 の理解とは少し異なるようで、vue2 を使用する際にスコープ スロットの実装について特別な注意を払っていなかったのかもしれません。
スコープ付きスロットはデータを渡すことができるスロットであり、子コンポーネントは親コンポーネントにデータを返すことができます。親コンポーネントは、子コンポーネント内に表示される返されたデータの構造または外観を決定できます。
たとえば、クエリ入力ボックスやドロップダウン ボックスを使用してリスト コンポーネントをカプセル化する場合、コール ページにはユーザー名とユーザー部門の 2 つのクエリ条件が必要で、b コール ページには製品名と製品番号の 2 つのクエリ条件が必要です。クエリ条件が異なる以外はすべて同じです。多くのサブコンポーネントをカプセル化することは不可能なので、再利用の意味がありません。このとき、スコープスロットを使用して親ページの子コンポーネントにデータを渡すことができ、子コンポーネントはスコープスロットを介して親コンポーネントにデータを返し、親コンポーネントの子コンポーネントタグがテンプレートタグ内に表示されることで、さまざまなクエリ条件を表示します。
親コンポーネントは todos 配列を子コンポーネントに渡します
<Test1 :todos="todos">
</Test1>
import {
ref } from "vue";
//todos数据
let todos = ref([
{
id: 1, title: "吃饭", done: true },
{
id: 2, title: "睡觉", done: false },
{
id: 3, title: "打豆豆", done: true },
{
id: 4, title: "打游戏", done: false },
]);
子コンポーネントは内部で受信して使用します
<template>
<div class="box">
<ul>
<li v-for="(item, index) in todos" :key="item.id">
</li>
</ul>
</div>
</template>
defineProps(["todos"]);
サブコンポーネント内でスコープ付きスロットの使用を開始すると、オブジェクトはキーと値に対応し、$row と $index は定義したキーに相当し、その後に対応する値が続きます。
<template>
<div class="box">
<ul>
<li v-for="(item, index) in todos" :key="item.id">
<slot :$row="item" :$index="index"></slot>
</li>
</ul>
</div>
</template>
親コンポーネントの子コンポーネントタグ内のスロットを使用して返されるデータ
<Test1 :todos="todos">
<template v-slot="{ $row, $index }">
<p :style="{ color: $row.done ? 'green' : 'red' }">
{
{
$row.title }}--{
{
$index }}
</p>
</template>
</Test1>
親コンポーネントから渡され、その後渡される必要があるのに、なぜ記述がそれほど複雑なのか疑問があるかもしれません。a の呼び出しページでは緑と赤の 2 色の判定が必要で、b の呼び出しページでは紫と青の判定が必要な場合、あるいは c と d などの呼び出しページでも他の色が必要な場合があります。ラップされたタグでも p タグが必要で、これには div タグが必要です。また、別の表示を必要とするその他の外観や構造も必要です。
おそらく、私たちが慣れているのは、親コンポーネントから子コンポーネントに変数を渡し、v-if、v-else-if を使用して子コンポーネント内のさまざまな条件を判断することです。あまり複雑でない場合はどちらでも使用できますが、複雑な場合はスコープ付きスロットを検討することができ、もう 1 つの実装方法を使用する必要はありません。