Vue3组件通信方式
Vue3组件通信方式
vue2: props、自定义事件、全局事件总线$bus、pubsub、vuex、ref、slot
props
可以实现父子组件通信,在vue3中我们可以通过defineProps获取父组件传递的数据,且在组件内部不需要引入defineProps方法可直接使用。【只读】
父组件给子组件传值
<Child info="中国" :money="money"></Child>
父组件向子组件传值,如果不加:冒号,传过去的不是数值,而是一个字符串;加上:冒号,传过去的就是一个数值,不是字符串,因为加了:冒号以后引号""里面的内容就是一个js表达式
子组件接收方式一
let props = defineProps({
info:{
type:String,
default:"默认参数"
},
money:{
type:Number,
default:0
}
})
子组件接收方式二
let props = defineProps(["info","money"])
在模板中使用可省略props
自定义事件
vue2 如果直接在自定义的组件上写@click事件是不管用的,但是在事件的后面加上.native修饰符就可以直接在父组件中使用这个点击事件了。
<Child @xxx="handler1"></Child>
<template>
<button @click="handler"></button>
</template>
<script setup lang="ts">
let $emit = defineEmits(["xxx"])
const handler = () => {
$emit("xxx","吃饭","喝水");
}
</script>
defineEmits,不需要引入直接使用。defineEmits方法执行,传递一个数组,数组元素即为将来组件需要触发的自定义事件类型,此方执行会返回一个$emit方法用于触发自定义事件。
当点击按钮的时候,事件回调内部调用$emit方法去触发自定义事件,第一个参数为触发事件类型,第二个、三个、N个参数即为传递给父组件的数据。
<Child @xxx="handler" @click="handler1"></Child>
正常说组件标签书写@click应该为原生DOM事件,但是如果子组件内部通过defineEmits定义就变为自定义事件了。
let $emit = defineEmits(["xxx","click"])
全局事件总线
mitt 全局事件总线可以实现任意组件通信,在vue2中可以根据VM与VC关系推出全局事件总线。 但是在vue3中没有Vue构造函数,也就没有Vue.prototype.以及组合式API写法没有this, 可以使用插件mitt实现。
npm install --save mitt
import mitt from 'mitt';
const $bus = mitt();
export default $bus;
import $bus from "../../bus";
//组合式API函数
import { onMounted ] from "vue";//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() => {//第一个参数:即为事件类型 第二个参数:即为事件回调
$bus.on("car",(car) => {
console.log(car);
}
}
v-model
v-model指令可以收集表单数据(数据双向绑定),除此之外它也可以实现父子组件数据同步。而v-model是指利用props[modelValue]与自定义事件[update:modelValue]实现的。
useAttrs
在Vue3中可以利用useAttrs方法获取组件的属性与事件(包含:原生DOM事件或者自定义事件),次函数功能类似于Vue2框架中attrs属性与listeners方法。
在父组件内部使用一个子组件my-button
<Child size="small" title="标题" @click="handler"></Child>
子组件内部可以通过useAttrs方法获取组件属性与事件.因此你也发现了,它类似于props,可以接受父组件传递过来的属性与属性值。需要注意如果defineProps接受了某一个属性,useAttrs方法返回的对象身上就没有相应属性与属性值。
import { useAttrs } from 'vue';
let $attrs = useAttrs();
ref与$parent
ref 可以在父组件内部通过ref获取子组件实例VC,子组件内部的方法与响应式数据父组件可以使用。
<template>
<Son ref="son"></Son>
</template>
<script lang="ts">
import Vue from 'vue'
import Son from 'Son'
import { onMounted, ref } from 'vue'
const son = ref()
onMounted(()=>{
console.log(son.value);
})
</script>
如果想让父组件获取子组件的数据或者方法需要通过defineExpose对外暴露,因为vue3中组件内部的数据对外“关闭的”,外部不能访问.
<script lang="ts">
import { ref } from 'vue'
let money = ref(1000)
const handler = ()=>{
console.log(1);
}
defineExpose({
money,
handler
})
</script>
$parent可以获取某一个组件的父组件实例VC,因此可以使用父组件内部的数据与方法。必须子组件内部拥有一个按钮点击时候获取父组件实例,当然父组件的数据与方法需要通过defineExpose方法对外暴露。
<button @click="handler($parent)">点击</button>
provide与inject【提供和注入】
vue3提供两个方法provide与inject,可以实现隔辈组件传递参数。
provide方法用于提供数据,此方法执需要传递两个参数,分别提供数据的key与提供数据value。
注意:后代组件修改的时候,源数据也会修改。
import { provide } from 'vue'
provide('token','token')
后代组件可以通过inject方法获取数据,通过key获取存储的数值
import {infect} from 'vue'
let token = infect('token')
pinia
pinia的特点
- 支持
options api
和composition api
- 去除
mutations
比vuex更加精简 - 在页面或者组件直接引用后使用, 数据来源更加清晰调试更加方便
- 全面的
TypeScript
支持 - 服务器端渲染支持
- 轻量 压缩后的体积只有1kb左右
下载
npm install pinia --save
使用
// main.js
import { createPinia } from 'pinia'
app.use(createPinia())
或
//store/index.ts
import { createPinia } from 'pinia'
const store = createPinia()
export default store;
// main.js
import store from './store'
const app = create(App)
app.use(store)
核心概念与基本使用
创建Store
import { defineStore } from 'pinia';
// defineStore 第一个参数是id,必需且值唯一
export const useUserStore = defineStore('user', {
//state返回一个函数,防止作用域污染
state: () => {
return {
userInfo: {
name: 'zhangsan',
age: 23,
},
token: 'S1',
};
},
getters: {
getNewName: (state) => state.userInfo.name 'vip',
},
actions: {
//更新整个对象
updateUserInfo(userInfo: { name: string; age: number }) {
this.userInfo = userInfo;
},
//更新对象中某个属性
updateAge(age: number) {
this.userInfo.age = age;
},
//更新基础数据类型
updateToken(token: string) {
this.token = token;
},
},
});
使用store
store 是一个用 reactive 包装的对象,直接解构读取state会失去响应式,因此需要storeToRefs,它将为每一个响应式属性创建引用;需通过 .value 读取解构出来的值
<template>
<div>
<div>姓名:{{ userInfo.name }} 年龄:{{ userInfo.age }}</div>
<div>token:{{ token }}</div>
<div>getter值:{{ newName }}</div>
<button @click="handleUser">更新用户</button>
<button @click="handleAge">更新年龄</button>
<button @click="handleToken">更新token</button>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/store/user'; //路径别名,引入store
const userStore = useUserStore();
//storeToRefs 会跳过所有的 action 属性
const { userInfo, token, newName } = storeToRefs(userStore);
//action 属性直接解构
const { updateUserInfo, updateAge, updateToken } = userStore;
const handleUser = () => {
updateUserInfo({ name: 'lisi', age: 24 });
};
const handleAge = () => {
//userInfo是一个ref响应式引用,需通过.value取值
updateAge(userInfo.value.age 1);
};
const handleToken = () => {
updateToken('23234');
};
</script>
异步 actions
除了上面展示的同步action,action也可以是异步的
新建store/list.ts
import { defineStore } from 'pinia';
const getData = () => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(Math.random() * 100);
}, 200);
});
};
export const useListStore = defineStore('list', {
state: () => {
return {
list: [] as number[],
};
},
actions: {
async updateList() {
try {
const data = await getData();
this.list.push(data);
} catch {
/* empty */
}
},
},
});
store 的相互引用
每个store可看做一个hook。相互引用即调用不同hook
新建store/userSex.ts
import { defineStore } from 'pinia';
import { useUserStore } from './user';
enum Sex {
man = '男人',
woman = '女人',
}
export const userSexStore = defineStore('user2', {
state: () => {
return {
sex: Sex.man,
};
},
actions: {
updateSex() {
const userStore = useUserStore(); // 引用其他store
if (userStore.userInfo.name !== 'zhangsan') this.sex = Sex.woman;
},
},
});
路由钩子中使用store
使用store的前提是保证pinia已被注册
import { useUserStore } from '@/store/user';
router.beforeEach((to, from) => {
// 这样做是可行的,因为路由器是在其被安装之后开始导航的,
// 而此时 Pinia 也已经被安装。
const userStore = useUserStore();
if (!userStore.token && to.path !== "/login") {
return "/login";
}
});
slot
插槽:默认插槽、具名插槽、作用域插槽可以实现父子组件通信.
默认插槽
在子组件内部的模板中书写slot全局组件标签
<template>
<div>
<slot></slot>
</div>
</template>
<script lang="ts">
</script>
在父组件内部提供结构:Todo即为子组件,在父组件内部使用的时候,在双标签内部书写结构传递给子组件
<Todo>
<h1>填充默认插槽</h1>
</Todo>
具名插槽
<template>
<div>
<h1>todo</h1>
<slot name="a"></slot>
<slot name="b"></slot>
</div>
</template>
<script lang="ts">
</script>
父组件内部向指定的具名插槽传递结构。需要注意v-slot:可以替换为#
<template>
<div>
<Todo>
<template #a>
<div>填入组件a</div>
</template>
<template v-slot:b>
<div>填入组件b</div>
</template>
</Todo>
</div>
</template>
作用域插槽
子组件数据由父组件提供。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfhefef
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
excel打印预览压线压字怎么办
PHP中文网 06-22