Vue3
元件通訊和Vue2
的區別:
- 移出事件匯流排,使用
mitt
代替。
-
vuex
換成了pinia
-
把
.sync
最佳化到了v-model
裡面了 -
把
$listeners
所有的東西,合併到$attrs
中了 -
$children
被砍掉了
常見搭配形式
props - 【父傳子 子傳父】
若 父傳子:屬性值是非函式
若 子傳父:屬性值是函式
一般都用於 父傳子
父傳子
父元件
<template>
<Child :car="car" :obj="obj" :list="list" />
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import Child from '@/components/Child.vue';
const car = ref('賓士');
// 定義資料型別-物件
type personsType = {
id: string;
name: string;
age?: number;
};
// 定義資料型別-陣列
// type ArrayPer = Array<persons>;
type ArrayPer = personsType[];
let obj = reactive<personsType>({ id: 'asdfj01', name: '張三' });
let list = reactive<ArrayPer>([
{ id: 'asdfj01', name: '張三', age: 10 },
{ id: 'asdfj02', name: '李四' },
{ id: 'asdfj03', name: '王五' },
]);
</script>
子元件-接收資料
<template>
<h1>接收來自父元件的值:</h1>
數值: {{ car }}
<hr />
物件:{{ obj }}
<hr />
陣列:{{ list }}
<hr />
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
// 定義資料型別-物件
type personsType = {
id: string;
name: string;
age?: number;
};
// 方式一:直接接收
// defineProps(['car','obj','list']);
// 方式一:設定資料型別
defineProps<{
car: string;
obj: personsType;
list: personsType[];
}>();
</script>
子傳父
父元件
<template>
# 子傳父:屬性值是 函式
<Child :getMsg="getMsg" />
</template>
<script lang="ts" setup>
import Child from '@/components/Child.vue';
function getMsg(val: string) {
console.log('收到了來自子元件的資料:' + val);
}
</script>
子元件
<template>
<h1>點選按鈕向父元件傳送資料:</h1>
<button @click="sentMsg">傳送資料</button>
<hr />
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
// 方式一:直接接收
let props = defineProps(['getMsg']);
// // 方式一:設定資料型別
// let props = defineProps<{
// getMsg: Function;
// }>();
function sentMsg() {
props.getMsg('哈哈哈');
}
</script>
自定義事件--【子傳父】 defineEmits
父元件--接收資料
<template>
// customerEventName 子元件裡面的 自定義事件
<Child @customerEventName="getMsg" />
</template>
<script lang="ts" setup>
import Child from '@/components/Child.vue';
function getMsg(val: string) {
console.log('收到了來自子元件的資料:' + val);
}
</script>
子元件-傳送資料
<template>
<h1>點選按鈕向父元件傳送資料:</h1>
<button @click="sentMsg">傳送資料</button>
<hr />
</template>
<script lang="ts" setup>
// customerEventName 是自定義事件的名稱
const emit = defineEmits(['customerEventName']);
function sentMsg() {
const msg = '哈哈哈';
// 向父元件傳遞引數
emit('customerEventName', msg);
}
</script>
mitt-- 任意元件間通訊
與訊息訂閱與釋出(pubsub
)功能類似,可以實現任意元件間通訊
1. 安裝
npm i mitt
2. 構建 src\utils\emitter.ts
// 引入mitt
import mitt from 'mitt';
// 建立emitter
const emitter = mitt();
// 建立並暴露mitt
export default emitter;
3. mitt 繫結on-呼叫-釋出-接收資料
和 觸發emit-定義-訂閱-發資料
事件
// emitter表示的是 emitter物件
// 繫結事件--呼叫事件
emitter.on('abc', (value) => {
console.log('abc事件被觸發', value);
});
emitter.on('xyz', (value) => {
console.log('xyz事件被觸發', value);
});
setInterval(() => {
// 觸發事件--定義事件
emitter.emit('abc', 666);
emitter.emit('xyz', 777);
}, 1000);
setTimeout(() => {
// 清理事件
emitter.all.clear();
// 移除事件
emitter.off('abc')
}, 3000);
4. 一個案例
父元件--監聽事件--接收資料
<template>
<Child />
</template>
<script lang="ts" setup>
import Child from '@/components/Child.vue';
import { onMounted, onUnmounted } from 'vue';
import mittBus from '@/utils/emitter';
onMounted(() => {
getMsg();
});
onUnmounted(() => {
mittBus.off('customerEventName');
});
const getMsg = () => {
mittBus.on('customerEventName', (val) => {
console.log('收到了來自子元件的資料:' + val);
});
};
</script>
子元件--觸發事件--發資料
<template>
<h1>點選按鈕向父元件傳送資料:</h1>
<button @click="sentMsg">傳送資料</button>
<hr />
</template>
<script lang="ts" setup>
import mittBus from '@/utils/emitter';
// customerEventName 是自定義事件的名稱
const sentMsg = () => {
const msg = '哈哈哈';
// 向父元件傳遞引數msg
mittBus.emit('customerEventName', msg);
};
</script>
v-model -- 父傳子 子傳父
v-model
的雙向資料繫結
v-model的雙向資料繫結本質就是 input 繫結一個value值,同時監聽input的value變化,重新賦值
<template>
<h2>父元件</h2>
<input type="text" v-model="username" />
<!-- v-model的雙向資料繫結本質就是 input 繫結一個value值,同時監聽input的value變化,重新賦值 -->
<input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
let username = ref('zhangsan');
</script>
元件標籤上的v-model
的本質
:moldeValue
+ update:modelValue
事件
<!-- 透過 v-model 將username 傳遞給 Child 子元件 -->
<Child v-model="username" />
<!-- 元件標籤上v-model的本質 -->
<!-- 傳遞資料modelValue,同時繫結事件 update:modelValue -->
<Child :modelValue="username" @update:modelValue="username = $event" />
父子互相通訊
父元件--傳送資料
<template>
<h2>父元件</h2>
<input type="text" v-model="username" />
<!-- 透過 v-model 將username 傳遞給 Child 子元件 -->
<Child v-model="username" />
</template>
<script lang="ts" setup>
import Child from '@/components/Child.vue';
import { ref } from 'vue';
let username = ref('zhangsan');
</script>
子元件-接收資料
透過 defineProps 接收 名為 modelValue
的資料,就是父元件傳遞過來的 username
<template>
<hr />
<h2>子元件</h2>
<h3>透過v-model接收父元件的資料:</h3>
{{ modelValue }}
<input type="text" :value="modelValue" @input="emit('update:modelValue', (<HTMLInputElement>$event.target).value)" />
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
// 接收資料 modelValue
defineProps(['modelValue']);
// 自定義觸發事件 pdate:modelValue
const emit = defineEmits(['update:modelValue']);
</script>
更換 modelValue 的命名
父元件
<template>
<h2>父元件</h2>
<input type="text" v-model="username" />
<input type="text" v-model="pwd" />
<!-- 透過 v-model 將username 傳遞給 Child 子元件 -->
<Child v-model:abc="username" v-model:xyz="pwd" />
</template>
<script lang="ts" setup>
import Child from '@/components/Child.vue';
import { ref } from 'vue';
let username = ref('zhangsan');
let pwd = ref('12356');
</script>
子元件
<template>
<hr />
<h2>子元件</h2>
<h3>透過v-model接收父元件的資料:</h3>
{{ abc }}=={{ xyz }}
<input type="text" :value="abc" @input="emit('update:abc', (<HTMLInputElement>$event.target).value)" />
<input type="text" :value="xyz" @input="emit('update:xyz', (<HTMLInputElement>$event.target).value)" />
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
// 接收資料 modelValue
defineProps(['abc', 'xyz']);
// 自定義觸發事件 pdate:modelValue
const emit = defineEmits(['update:abc', 'update:xyz']);
</script>
$attrs -- 祖孫間傳遞資料
$attrs
用於實現當前元件的父元件,向當前元件的子元件通訊(祖→孫)
$attrs
是一個物件,包含所有父元件傳入的標籤屬性
注意:
$attrs
會自動排除props
中宣告的屬性(可以認為宣告過的props
被子元件自己“消費”了)
父元件
<template>
<h2>父元件</h2>
<h3>a={{ a }}</h3>
<h3>b={{ b }}</h3>
<h3>c={{ c }}</h3>
<h3>num={{ num }}</h3>
<Child :a="a" :b="b" :c="c" v-bind="{ x: 100, y: 200, z: 300 }" :updateSum="updateSum" />
</template>
<script lang="ts" setup>
import Child from '@/components/Child.vue';
import { ref } from 'vue';
let num = ref(1);
let a = ref('a');
let b = ref('b');
let c = ref('c');
const updateSum = (val: number) => {
num.value = val;
};
</script>
子元件 : 子元件不做任何事情,直接將 $attrs 傳遞給孫子元件
<template>
<hr />
<h2>子元件不做任何事情,直接將 $attrs 傳遞給孫子元件</h2>
<GrandChild v-bind="$attrs" />
</template>
<script lang="ts" setup>
import GrandChild from '@/components/GrandChild.vue';
</script>
孫元件
<template>
<hr />
<h2>孫子元件</h2>
<h3>a={{ a }}</h3>
<h3>b={{ b }}</h3>
<h3>c={{ c }}</h3>
<h3>x={{ x }}</h3>
<h3>y={{ y }}</h3>
<h3>z={{ z }}</h3>
<button @click="updateSum(666)">點選將 num 的值 改為 666</button>
</template>
<script lang="ts" setup>
defineProps(['a', 'b', 'c', 'x', 'y', 'z', 'updateSum']);
</script>
$refs
父傳子 $parent
子傳父
$refs
父傳子,結合ref元件標籤
$refs
值為物件,包含所有被ref
屬性標識的DOM
元素或元件例項
父元件:只可以修改 子元件向外暴露的資料
<template>
<h2>父元件</h2>
<!-- $refs 子元件所有的 例項物件 -->
<button @click="getAllChilds($refs)">獲取所有子元件的例項物件,並修改資料</button>
<Child1 ref="c1" />
<Child2 ref="c2" />
</template>
<script lang="ts" setup>
import Child1 from '@/components/Child1.vue';
import Child2 from '@/components/Child2.vue';
import { ref } from 'vue';
let c1 = ref();
let c2 = ref();
function getAllChilds(refs: any) {
refs.c1.name = '張三三';
refs.c2.name = '李四四';
}
</script>
子元件:必須向外暴露資料,父元件才可以修改和獲取
子元件1
<template>
<hr />
<h2>子元件-Child1-{{ name }}</h2>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
let name = ref('張三');
// 將name向外暴露出去
defineExpose({ name });
</script>
子元件2
<template>
<hr />
<h2>子元件-Child2-{{ name }}</h2>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
let name = ref('李四');
// 將name向外暴露出去
defineExpose({ name });
</script>
$parent
子傳父
$parent
值為物件,當前元件的父元件例項物件
父元件
<template>
<h2>父元件</h2>
數值num為=={{ num }}
<Child1 />
</template>
<script lang="ts" setup>
import Child1 from '@/components/Child1.vue';
import { ref } from 'vue';
let num = ref(6);
// 必須將父元件的值暴露給子元件,子元件才可以修改父元件的值
defineExpose({ num });
</script>
子元件
<template>
<hr />
<h2>子元件-Child1</h2>
<button @click="minus($parent)">將父元件num值減1</button>
</template>
<script lang="ts" setup>
function minus(p: any) {
console.log(p.num);
p.num -= 1;
}
</script>
provide、inject 祖孫間傳遞資料
Pinia
參考:
Pinia狀態管理
插槽
參考: