今天是学习硅谷甄选的第一天,项目总长46个小时,视频144ep,希望能尽快学完早日找工作。
vue3组件通信方式
1,props
props是只读的,用来给父子组件进行通信,vue2,vue3使用模式
vue2:声明props属性,接收参数如info,props:['info']
props:['info']
vue3:运用defineProps
//需要使用到 defineProps方法去接受父组件传递过来的数据
// defineProps是Vue3提供的方法,不需要引入直接使用
let props =defineProps(['info','money'])
console.log(props);//获取到一个代理对象身上有该俩个属性
// 按钮点击的回调
const updateProps=()=>{
// props.money+=10 props数据只读可看不可用
}
2,自定义事件
子给父亲传递参数
父代码:
<Event2 @xxx="handler3" @click="handler4"/>
<script>
import Event2 from './Event2.vue'
const handler3= (param1,param2)=>{
console.log(param1,param2);
}
</script>
子代码:
<button @click="handler">点我触发自定义事件xxx</button>
<button @click="$emit('click','AK47','J20')">点我触发自定义事件click</button>
<script setup lang='ts'>
//利用defineEmits方法返回函数触发自定义事件
//defineEmits方法不需要引入直接使用
let $emit=defineEmits(['xxx'])
//按钮点击回调
const handler=()=>{
//第一个参数:事件类型 第二个|三个|N参数即为注入数据
$emit('xxx','东风导弹','航母军队')
}
</script>
结果,可以顺利传参,但是自定义事件不声明的话,会默认当成原生DOM事件
编辑
3,全局事件总线
1.在Vue3中没有$bus因此需要引入mitt来实现$bus的事件,用来进行子组件间通信
mitt - npm
//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from 'mitt';
const $bus = mitt();
export default $bus;
mitt类似于pubsub发布与订阅
$on
//子组件1
import $bus from '../../bus'
//组合式API函数
import {onMounted} from 'vue'
//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(()=>{
//第一个参数:即为事件类型 第二个参数:即为事件回调
$bus.on('car',(car)=>{
console.log(car);
})
})
$emit
//引入$bus对象
import $bus from '../../bus'
//点击按钮回调
const handler =() =>{
$bus.emit('car',{car:'法拉利'})
}
4,V-model
也可用于父子间数据通信,类似于自定义事件,但是更加简便
父组件
//父组件的数据钱数
let money=ref(10000)
//自定义事件的回调
const handler =(num)=>{
//将来接受子组件传递过来的数据
money.value=num
}
//父亲的数据
let pageNo=ref(1)
let pageSize=ref(3)
子组件
let props = defineProps(["pageNo", "pageSize"]);
let $emit = defineEmits(["update:pageNo", "update:pageSize"]);
//第一个按钮的事件回调
const handler = () => {
$emit('update:pageNo',props.pageNo+2)
};
页面展示
//父
<Child1 v-model:pageNo="pageNo" v-model:pageSize="pageSize"/>
//子
<button @click="$emit('update:pageSize',pageSize+4)">pageSize{{pageSize}}</button>
5,useAttrs
Vue3提供的用来获取组件身上的属性与事件,可以用来封装自定义组件
props和useAttrs俩个一起使用时须小心,因为props优先级更高,因此useAttrs会获取不到想要的数据
props与useAttrs方法都可以获取父组件传递过来的属性与属性值
useAttrs单独使用时,能够拿到title
//引入useAttrs方法:获取组件标签身上属性与事件
import {useAttrs} from 'vue'
//此方法执行会返回一个对象
let $attrs=useAttrs()
console.log($attrs);
编辑
props和useAttrs一起使用时,title被分配给了props
//引入useAttrs方法:获取组件标签身上属性与事件
import {useAttrs} from 'vue'
//此方法执行会返回一个对象
let $attrs=useAttrs()
console.log($attrs);
//万一用props接受title
let props=defineProps(['title'])
console.log(props);
console.log($attrs);
编辑
还可以获取到原生DOM事件与自定义事件
<HintButton type='primary' size='small' :icon='Edit' title="编辑按钮" @click="handler" @xxx='handler'></HintButton>
<script setup lang='ts'>
const handler = ()=>{
alert(12306);
}
</script>
编辑
6,ref与$parent
ref是父给子组件是进行数据更改
$parent是子给父组件进行数据更改
ref
子组件的声明如下
import {ref} from 'vue';
//儿子钱数
let money = ref(666);
const fly = ()=>{
console.log('我可以飞');
}
//组件内部数据对外关闭的,别人不能访问
//如果想让外部访问需要通过defineExpose方法对外暴露
defineExpose({
money,
fly
})
父组件的回调如下
//获取子组件的实例
let son = ref();
//父组件内部按钮点击回调
const handler = ()=>{
money.value+=10;
//儿子钱数减去10
son.value.money-=10;
son.value.fly();
}
$parent
子组件的回调如下
<button @click="handler($parent)">点击我爸爸给我10000元</button>
const handler = ($parent)=>{
money.value+=10000;
$parent.money-=10000;
}
父组件的声明如下
//父组件钱数
let money = ref(100000000);
//对外暴露
defineExpose({
money
})
总结,子给父需要用$parent获取数据以及回调,父给子需要用ref获取数据以及回调,并且都需要对外声明暴露,可以完成组件间的通信。
7,provide和inject
用于隔辈组件传递数据
祖先用provide组件声明:
//vue3提供provide(提供)与inject(注入),可以实现隔辈组件传递数据
import { ref, provide } from "vue";
let car = ref("法拉利");
//祖先组件给后代组件提供数据
//两个参数:第一个参数就是提供的数据key
//第二个参数:祖先组件提供数据
provide("TOKEN", car);
孙组件用inject收数据:
import {inject} from 'vue';
//注入祖先组件提供数据
//需要参数:即为祖先提供数据的key
let car = inject('TOKEN');
const updateCar = ()=>{
car.value = '自行车';
}
孙组件是可以更改祖先组件的数据的
<h1>孙子组件</h1>
<p>{{car}}</p>
<button @click="updateCar">更新数据</button>
8,pinia
类似vuex(集中式管理状态容器),缺点持久性差(state,mutations,actions,getters,modules)
pinia:集中式管理状态容器( state、actions、getters)
与vuex一样在store文件夹下创建一个modules用来储存小仓库
//定义info小仓库
import { defineStore } from "pinia";
state,actions,getters:在info仓库中定义
//第一个仓库:小仓库名字 第二个参数:小仓库配置对象
//defineStore方法执行会返回一个函数,函数作用就是让组件可以获取到仓库数据
let useInfoStore = defineStore("info", {
//存储数据:state
state: () => {
return {
count: 99,
arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
},
actions: {
//注意:函数没有context上下文对象
//没有commit、没有mutations去修改数据
updateNum(a: number, b: number) {
this.count += a;
}
},
getters: {
total() {
let result:any = this.arr.reduce((prev: number, next: number) => {
return prev + next;
}, 0);
return result;
}
}
});
//对外暴露方法
export default useInfoStore;
子页面1代码
<div class="child">
<h1>{{ infoStore.count }}---{{infoStore.total}}</h1>
<button @click="updateCount">点击我修改仓库数据</button>
</div>
<script setup lang="ts">
import useInfoStore from "../../store/modules/info";
//获取小仓库对象
let infoStore = useInfoStore();
console.log(infoStore);
//修改数据方法
const updateCount = () => {
//仓库调用自身的方法去修改仓库的数据
infoStore.updateNum(66,77);
};
</script>
子页面2代码
<div class="child1">
{{ infoStore.count }}
<p @click="updateTodo">{{ todoStore.arr }}{{todoStore.total}}</p>
</div>
<script setup lang='ts'>
import useInfoStore from "../../store/modules/info";
//获取小仓库对象
let infoStore = useInfoStore();
//引入组合式API函数仓库
import useTodoStore from "../../store/modules/todo";
let todoStore = useTodoStore();
//点击p段落去修改仓库的数据
const updateTodo = () => {
todoStore.updateTodo();
};
</script>
实现如图,也能实现组件间通信
编辑
在pinia中,利用了vue中的组合式API函数,ref相当于state,回调相当于actions,computed相当于actions,如图
//定义组合式API仓库
import { defineStore } from "pinia";
import { ref, computed,watch} from 'vue';
//创建小仓库
let useTodoStore = defineStore('todo', () => {
let todos = ref([{ id: 1, title: '吃饭' }, { id: 2, title: '睡觉' }, { id: 3, title: '打豆豆' }]);
let 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;
9,slot插槽
插槽:默认插槽、具名插槽、作用域插槽,也可用于父子组件通信
且具名插槽中的v-slot可以简化为 #
子组件:
<div class="box">
<h1>我是子组件默认插槽</h1>
<!-- 默认插槽 -->
<slot></slot>
<h1>我是子组件默认插槽</h1>
<h1>具名插槽填充数据</h1>
<slot name="a"></slot>
<h1>具名插槽填充数据</h1>
<h1>具名插槽填充数据</h1>
<slot name="b"></slot>
<h1>具名插槽填充数据</h1>
</div>
父组件:
<Test>
<div>
<pre>大江东去浪淘尽,千古分流人物</pre>
</div>
<!-- 具名插槽填充a -->
<template #a>
<div>我是填充具名插槽a位置结构</div>
</template>
<!-- 具名插槽填充b v-slot指令可以简化为# -->
<template #b>
<div>我是填充具名插槽b位置结构</div>
</template>
</Test>
<script setup lang="ts">
import Test from "./Test.vue";
</script>
结果
编辑
作用域插槽:就是可以传递数据的插槽,子组件可以讲数据回传给父组件,父组件可以决定这些回传的数据是以何种结构或者外观在子组件内部去展示!!!
子组件传递数据:
<ul>
<li v-for="(item, index) in todos" :key="item.id">
<!--作用域插槽:可以讲数据回传给父组件-->
<slot :$row="item" :$index="index"></slot>
</li>
</ul>
<script setup lang="ts">
defineProps(["todos"]);
</script>
父组件决定展示模式组件:
<Test1 :todos="todos">
<template v-slot="{ $row, $index }">
<p :style="{ color: $row.done ? 'green' : 'red' }">
{{ $row.title }}--{{ $index }}
</p>
</template>
</Test1>
<script setup lang="ts">
import Test1 from "./Test1.vue";
import { ref } from "vue";
//todos数据
let todos = ref([
{ id: 1, title: "吃饭", done: true },
{ id: 2, title: "睡觉", done: false },
{ id: 3, title: "打豆豆", done: true },
{ id: 4, title: "打游戏", done: false },
]);
</script>
结果
编辑