https://gitee.com/jch1011/vue3_communication.git
通信方式
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() // listen to an event emitter.on('foo', e => console.log('foo', e) ) // listen to all events emitter.on('*', (type, e) => console.log(type, e) ) // fire an event emitter.emit('foo', { a: 'b' }) // clearing all events emitter.all.clear() // working with handler references: function onFoo() {} emitter.on('foo', onFoo) // listen emitter.off('foo', onFoo) // unlisten
- 安装mitt
- 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() // vue3将$attrs和$linterners合并了 <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' // 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`) // 第一个参数是你的应用中 Store 的唯一 ID。 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
//定义组合式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;
- vuex:集中式管理状态容器,可以实现任意组件中间通信
- slot 插槽(默认插槽、具名插槽、作用域插槽)
- 默认插槽
<slot></slot>
- 具名插槽(v-slot可以简写成#)
<slot name="a"></slot> <h1>具名插槽填充数据</h1> <h1>具名插槽填充数据</h1> <!--使用--> <!-- 具名插槽填充a --> <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>