整理自gitee.com/jch1011/vue…
通信方式
vue2
- props:父子组件、子父组件、甚至兄弟组件
- 自定义事件:可以实现子父组件
- $bus:任意组件通信
- pubsub:发布订阅,任意组件通信
- vuex:集中式状态管理容器,实现任意组件通信
- ref:获取子组件的响应式数据及方法
- slot:插槽(默认、具名、作用域)实现父子组件通信
- ......
vue3
- props 使用defineProps
- 自定义事件
- 原生dom事件
- vue2中 @click是自定义事件,原生要加.navite
- vue3中@click为原生DOM事件
- vue3:原生的DOM事件不管是放在标签身上还是组件标签身上,都是原生DOM事件
- @click其实是给子组件的根节点绑定
- 子组件给父组件传递 @xxx='()=>{}'
- 声明:
let $emit = defineEmits(['xxx'])
- 使用:
$emit('xxx','东风')
- 第一个参数:事件类型,接下来的参数为注入数据
- 如果在defineEmits声明'click',@click就会变成自定义事件
- 这个
emits 选项还支持对象语法,它允许我们对触发事件的参数进行验证:
<script setup>
const emit = defineEmits({
submit(payload) {
// 通过返回值为 `true` 还是为 `false` 来判断
// 验证是否通过
}
})
</script>
- 如果你正在搭配 TypeScript 使用
<script setup>,也可以使用纯类型标注来声明触发的事件:
<script setup lang="ts">
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
</script>
- 全局事件总线(vue3使用mitt 代替$bus,因为没有this了)
- 安装mitt
npm i -D mitt
// 使用 ES6 模块 import mitt from 'mitt'
- 使用方法
const emitter = mitt()
emitter.on('foo', e => console.log('foo', e) )
emitter.on('*', (type, e) => console.log(type, e) )
emitter.emit('foo', { a: 'b' })
emitter.all.clear()
function onFoo() {}
emitter.on('foo', onFoo)
emitter.off('foo', onFoo)
- v-model 父子组件数据同步
- 父组件中使用
<Child v-model:money="money">
- 子组件中使用
let props = defineProps(["money"])
let $emit = defineEmits(["update:money"])
<Button>@click="$emit('update:pageSize', pageSize + 4)"<Button/>
- attrs vue3提供了一个方法,获取组件身上的属性和事件
- 使用
let $attrs = useAttrs()
<el-button :='$attrs'>按钮</el-button>
- 注意!如果使用props接收了,useAttrs方法就获取不到了
- 使用ref和$parent
- 子组件内部的数据默认关闭的,需要使用
defineExpose({money})暴露
son.value.money-=10;
子组件中使用$parent
- 父孙之间通信:Provide与Inject
- vue3提供provide(提供)和inject(注入),可以实现隔辈传递数据
- 例如
let car = ref('车');
provide('key',car);
let car = inject('key')
- pinia
- vuex:集中式管理状态容器,可以实现任意组件中间通信
- 核心概念:state、mutations、actions、getters、modules
- pinia:集中式状态管理容器,可以实现任意组件中间通信
- 核心概念:states、actions、getters
- 创建大仓库
let store = createPinia()
export default store
- 安装
import store from './store'
app.use(store)
- 定义小仓库
import { defineStore } from 'pinia'
export const useAlertsStore = defineStore('alerts', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
- 使用
import useInfoStore from "../../store/modules/info";
let infoStore = useInfoStore();
console.log(infoStore);
const updateCount = () => {
infoStore.updateNum(66, 77);
};
- 组合式api
import { defineStore } from "pinia";
import { ref, computed, watch } from "vue";
const useTodoStore = defineStore("todo", () => {
const todos = ref([
{ id: 1, title: "吃饭" },
{ id: 2, title: "睡觉" },
{ id: 3, title: "打豆豆" },
]);
const 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;
- slot 插槽(默认插槽、具名插槽、作用域插槽)
- 默认插槽
<slot></slot>
- 具名插槽(v-slot可以简写成#)
<slot name="a"></slot>
<h1>具名插槽填充数据</h1>
<h1>具名插槽填充数据</h1>
<template #a>
<div>我是填充具名插槽a位置结构</div>
</template>
- 作用域插槽
- 就是可以传递数据的插槽,子组件可以将数据回传给父组件,父组件可以决定这些回传的数据是以何种结构或者外观在子组件内部展示的!
- 例如
<!--父-->
<Test1>
<template v-slot='{$row,$index}'>
<h1>{{$row.title}}</h1>
</template>
</Test1>
<!--子-->
<ul>
<li v-for='(item,index) in todos' :key='item.id'>
<!--回传给父组件-->
<slot :$row='item' $index='index'></slot>
</li>
</ul>