组件通信
- Vue2组件通信方式
- 全局事件总线bus,可以实现组件通信
- Vue3组件通信方式
- props
- provide与inject依赖注入
- 全局APi
- Vue3其他改变
- 组件通信之自定义事件
- 组件通信之事件总线
- 组件通信之v-model
- 多个v-model传值
- TSX风格
- 使用风格一
- 使用tsx风格,使用optionsApi模式
- 使用tsx风格,使用setup函数模式
- 使用tsx风格,使用数组比较麻烦
- 使用tsx风格,来父子组件传值
- 使用tsx风格,插槽
Vue2组件通信方式
- props可以实现父子组件,子父组件,甚至是兄弟组件
- 自定义事件,可以实现子父组件通信
全局事件总线bus,可以实现组件通信
- pubsub,发布订阅模式实现任意组件通信
- Vuex,集中式状态管理容量,可以实现任意组件通信
- ref,父组件获取子组件实例vc
- slot, 插槽,实现父子组件通信
Vue3组件通信方式
props
- 需要使用到defineProps方法去接受父组件传递过来的数据
- 页面中props可以省略前面的名字
- props的数据是只读的
- 父组件
<Child info="我是祖先" :money="money"></Child>
- 子组件
<template>
<div class="son">
<h1>我是子组件</h1>
<p>{{props.info}}</p>
<p>{{props.money}}</p>
<!--props可以省略前面的名字--->
<p>{{info}}</p>
<p>{{money}}</p>
<button @click="updateProps">修改props数据</button>
</div>
</template>
<script setup lang="ts">
//需要使用到defineProps方法去接受父组件传递过来的数据
//defineProps是Vue3提供方法,不需要引入直接使用
let props = defineProps(['info','money']); //数组|对象写法都可以
//按钮点击的回调
const updateProps = ()=>{
// props.money+=10; props:只读的
console.log(props.info)
}
</script>
provide与inject依赖注入
- 实现祖孙组件之间的通信
- 当在深度嵌套的组件中,深层的子组件想要需要父组件的部分内容
- 沿着prop方法传递,很麻烦
- 采用依赖注入:
- 祖先组件有一个provide选项来指定我们想要提供给后代组件的数据或方法
- 子组件有一个inject来接收provide提供的数据或方法
- 实现的效果图片
- 祖先存
- 后代取
全局APi
- 在Vue3中,不支持Vue.xxx,将它调整到应用实例app上
Vue3其他改变
- 移除keyCode作为v-on的修饰符,同时不再支持config.keycodes
- 移除v-on.native修饰符,这个的作用就是当父组件点击子组件上的@click,不当原生click,当成了自定义事件,加了@click.native,就会变成原生click点击事件
- 移除过滤器(filter)
组件通信之自定义事件
-
vue框架中事件分为两种,一种是原生的Dom事件,另外一种自定义事件
-
原生dom事件可以让用户与网页进行交互,比如click这些
-
自定义事件可以实现子组件给父组件传递数据
-
子组件
-
方式一
<button @click="handler">点击我触发自定义事件xxx</button>
//按钮点击回调
<script setup lang="ts">
const handler = () => {
//第一个参数:事件类型 第二个|三个|N参数即为注入数据
$emit('xxx','乞力马扎罗','18');
};
</script>
- 方式二
<button @click="$emit('xxx','乞力马扎罗','18')">点击我触发自定义事件xxx</button>
<script setup lang="ts">
//利用defineEmits方法返回函数触发多个自定义事件,defineEmits方法不需要引入直接使用
let $emit = defineEmits(['xxx','click']);
</script>
- 父组件
<!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
<Event2 @xxx="handler3" @click="handler4"></Event2>
//事件回调---4
const handler3 = (param1,param2)=>{
console.log(param1,param2);
}
//事件回调--5
const handler4 = (param1,param2)=>{
console.log(param1,param2);
}
组件通信之事件总线
- 引入mitt插件:mitt一个方法,方法执行会返回bus对象
- mitt方法身上带着一on方法和emit方法
- 导入npm install – save mitt
- impirt mitt from ‘ mitt’
var mitt =require(‘mitt’)
- 你也可以新建一个bus文件,创建index.ts
//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from 'mitt';
const $bus = mitt();
export default $bus;
- 子组件1
<button @click="handler">点击我给兄弟传值</button>
<script setup lang="ts">
//引入$bus对象
import $bus from '../../bus';
//点击按钮回调
const handler = ()=>{
$bus.emit('xxx',{car:"我给你送"});
}
</script>
- 子组件2
<script setup lang="ts">
import $bus from "../../bus";
//组合式API函数
import { onMounted } from "vue";
//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() => {
//第一个参数:即为事件类型 第二个参数:即为事件回调
$bus.on("xxx", (car) => {
console.log(car);
});
});
</script>
组件通信之v-model
父传子
- 父组件
<Child v-model="money"></Child>
- 子组件
- 接收的值modelValue是固定的
<h3>钱数:{{ modelValue }}</h3>
let props = defineProps(["modelValue"]);
子传父
- 子组件
- 同步更新父组件的@update事件
<button @click="handler">父子组件数据同步</button>
//方法一
//<button @click="handler" @update:modelValue=""handler>父子组件数据同步</button>
//方法2
//子组件内部按钮的点击回调
const handler = ()=>{
//触发自定义事件
$emit('update:modelValue',props.modelValue+1000);
}
- 父组件
- 自定义事件接收
let money = ref(10000);
//自定义事件的回调
const handler = (num) => {
//将来接受子组件传递过来的数据
money.value = num;
};
– 这样就形成闭环实现父子组件同步更新
多个v-model传值
- 父组件
<Child1 v-model:pageNo="pageNo" v-model:pageSize="pageSize"></Child1>
- 子组件
<script setup lang="ts">
let props = defineProps(["name", "age"]);
let $emit = defineEmits(["update:name", "update:age"]);
//第一个按钮的事件回调
const handler = () => {
$emit("update:name", props.name + 3);
};
</script>
TSX风格
- 原先使用的是Template风格,现在可以扩展另一种风格TSX风格
- 安装: npm install @vitejs/plugin-vue-jsx -D
- 在vite.config.ts注册使用
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
//导入jsx
import vuejsx from "@vitejs/plugin-vue-jsx";
//在plugins下注册vuejsx
export default defineConfig({
plugins: [vue(),vuejsx()],
})
使用风格一
- 新建app.tsx
- 页面使用(接下来父级页面都是这个)
使用tsx风格,使用optionsApi模式
- defineComponent是Vue 3中高阶组件工厂函数,主要用于类型推导和提供一个统一的API,以创建Vue组件
- 当你使用defineComponent时,它会接受一个对象或者一个包含setup函数和其他可选配置的函数,然后返回一个组件对象。这个组件对象可以直接被Vue实例化,并且会包含所有的生命周期钩子、响应式数据、计算属性、侦听器等
- 使用变量是单括号
使用tsx风格,使用setup函数模式
使用tsx风格,使用数组比较麻烦
- v-if 也不支持,需要用三元表达式
使用tsx风格,来父子组件传值
- 子组件,的tsx风格
import { defineComponent } from 'vue';
//ref的值,在template中自动解包,在tsx不会,得使用.value
export default defineComponent({
props: {
name: String,
},
emits: ['on-getfdetail'],
setup(props, { emit }) {
const list = [{ name: '花花1' }, { name: '花花2' }, { name: '花花3' }];
const fun = (item) => {
console.log('回传', item);
emit('on-getfdetail', item);
};
return () => (
<>
<div>props:{props?.name}</div>
<hr />
{list.map((v) => {
//有问题,一上来就直接调用了
// return <div onClick={fun()}>{v.name}</div>;
// 函数柯里化解决
return <div onClick={() => fun(v)}>{v.name}</div>;
})}
</>
);
},
});
- App.vue页面父组件
<template>
<div>
<tsx @on-getfdetail="getname" name="乞力马扎罗"></tsx>
</div>
</template>
<script setup lang="ts">
import tsx from './App';
const getname = (item) => {
console.log(item, '子组件传的值');
};
</script>
<style scoped></style>
使用tsx风格,插槽
import { defineComponent } from 'vue';
//ref的值,在template中自动解包,在tsx不会,得使用.value
//留下插槽位置
const A = (_, { slots }) => (
<>
<div>{slots.default ? slots.default() : '默认值'}</div>
<div>{slots.foo?.()}</div>
</>
);
export default defineComponent({
setup() {
//定义插槽要展示的内容
const slotName = {
default: () => (<div>展示默认插槽</div>),
foo: () => (<div>插槽</div>)
};
return () => (
<>
{/* 父组件展示内容 */}
<A v-slots={slotName}></A>
</>
);
},
});
- 效果展示