基本路由组件基础知识了解
props:实现父子组件(数组、字符串)、子父组件(函数)、兄弟组件之间的通信(数据挂载到父组件上)(只读)
自定义事件:可以实现子父组件通信
全局事件总线$BUS:可以实现任意组件通信(vue2很重要)
pubsub:发布订阅模式实现任意组件通信(vue项目很少用,react项目和微信小程序项目用的较多)
vuex:集中式状态管理容器,实现任意组件通信(缺点:存储数据并非持久化)
ref:父组件获取子组件实例VC,获取子组件响应式数据以及方法
slot:插槽(默认插槽,具名插槽,作用域插槽)实现父子组件通信
1props路由组件
基本常识:1.运行项目:在终端中输入npm run dev
2.自动生成vue文件模板方式:文件-》首选项-》用户代码片段-》输入vue.json-》输入代码:
{
"Vue Component Template": {
"prefix": "vue",
"body": [
"<template>",
" <div>",
" <!-- Component HTML goes here -->",
" </div>",
"</template>",
"",
"<script>",
"export default {",
" name: '$1',",
" data() {",
" return {",
" // component data",
" };",
" },",
" methods: {",
" // component methods",
" }",
"};",
"</script>",
"",
"<style scoped>",
"/* component CSS styles */",
"</style>"
],
"description": "Basic Vue component structure"
}
}
props:可以实现父子组件通信,props数据还是只读的!!!
1.1父组件传参给子组件
在父组件中写入引入:import Child from './Child.vue';
在div中写入<child></child>
组件中的通信通过属性的形式的传递
1.父组件给子组件传递字符串:
<child info="我是曹操" ></child>
2.通过引入组合式api函数动态传递数据
import {ref} from 'vue';
let money = ref(10000);
<child info="我是曹操" :money="money"></child>
前者数据传递较为死板,后者较为灵活
1.2 子组件接收数据
1.选择式api写法
// export default {
// props:[‘info’,‘money’]
// };
<p>{{ info }}</p>
2.组合式api写法 setup
需要使用到defineprops方法去接收父组件传递过来的数据
// defineprops是vue3提供的方法,不需要引入直接使用
let props = defineProps(['info','money']);
//数组或者对象写法都可以,与vue2一样的
返回的是一个代理对象,有两个属性,分别为info和money,用于存储父组件给的数组,因此可以在模板中使用这些数组:
<p>{{props.info }}</p>
<p>{{ $props.money }}</p>
<!-- props可以省略前面的名字 -->
<p>{{ info }}</p>
<p>{{ money }}</p>
代理对象数组props不可以修改,如果需要修改,在父组件内修改
Tips:1.<button @click.=“updateProps”>修改props数据中@click="updateProps"是事件监听器,用户点击即可触发updateProps方法
2.
const updateProps = () =>{
// props.money +=10 ; props:只读的
console.log(props.info)
}
这是用箭头函数定义的事件处理器,是一个新的函数表达方式,不会绑定this
3.const 是一种声明变量的关键字,用于定义一个常量。且常量不可重新赋值
2自定义事件
2.1原生DOM事件
用户与网页进行交互,鼠标点击等方式
vue2框架当中:这种写法自定义事件,可以通过.native修饰符变为原生DOM事件
vue3框架下面写法其实即为原生DOM事件
vue3:原生的DOM事件不管是放在标签身上、组件标签身上都是原生DOM事件
<pre @click="handler">
大江东去浪淘尽,千古风流人物
</pre>
<button @click="handler1(1,2,3,$event)">点击我传递多个参数</button>
<Event1 @click="handler2"></Event1>
// 引入子组件
import Event1 from './Event1.vue';
// 事件回调--1
const handler = (event) => {
// event即为事件对象
console.log(event);
}
// 事件回调--2
const handler1 = (a,b,c,$event)=>{
console.log(a,b,c,$event);
}
// 事件回调--3
const handler2 =()=>{
console.log(123);
}
2.2 自定义事件
用于实现组件之间的通信,子组件给父组件传递参数
<!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
<Event2 @xxx="handler3" @click="handler4"></Event2>
// 引入子组件
import Event2 from './Event2.vue';
// 事件回调--4
const handler3 = (param1,param2)=>{
console.log(param1,param2);
}
// 事件回调--5
const handler4 = (param1,param2)=>{
console.log(param1,param2);
}
Event2.vue
<template>
<div class="child">
<p>我是子组件2</p>
<button @click="handler">点击我触发自定义事件</button>
<button @click="$emit('click','AK47','J20')">点击我触发自定义事件click</button>
</div>
</template>
<script setup lang="ts">
// vue3组合式api写法没有this setup没有this
// 利用defineEmits方法返回函数触发自定义事件
// defineEmits方法不需要引入直接使用
let $emit = defineEmits(['xxx']);
const handler = () => {
// 第一个参数:事件类型 第二个|第三个|n个即为注入数据
$emit('xxx','东风导弹','航母');
}
</script>
<style scoped>
.child {
width: 400px;
height: 200px;
background-color: pink;
}
</style>
3.全局事件总线
全局事件总线可以实现任意组件通信,在vue2中可以根据vm与vc关系推出全局事件总线,但是在vue3中没有vue构造函数,也没有vue.prototype以及组合式api写法没有this
所以vue3实现全局事件总线私用插件mitt
例子:兄弟组件实现通信
在npm官网查看mitt插件
1.新建bus/index.ts封装bus对象
// 引入mitt插件:mitt是一个方法,方法执行会返回一个bus对象
import mitt from "mitt";
const $bus = mitt();
export default $bus;
2.给兄弟组件2绑定事件
接收兄弟组件1发过来的事件
<script setup lang="ts">
import $bus from '../../bus';
// 组合式API函数
import { onMounted } from 'vue';
// 组件挂载完毕的时候,当前组件绑定一个事件,接收将来兄弟组件传递的数组
onMounted(() => {
// 第一个参数:即为事件的类型 第二个参数:即为事件的回调
$bus.on('car', (car) => {
console.log(car);
});
})
console.log($bus);
import $bus from ‘…/…/bus’; 向上走两级找到bus文件
3.兄弟组件1触发事件
<template>
<div class="child2">
<h1>我是子组件2:曹丕</h1>
<button @click="handler">点击我给我的兄弟送一台车</button>
</div>
</template>
<script setup lang="ts">
// 引入$bus对象
import $bus from '@/bus';
// 点击按钮回调
const handler=()=>{
$bus.emit('car',{car:"法拉利"});
}
</script>
<style scoped>
.child2{
width: 300px;
height: 300px;
background-color: skyblue;
}
</style>
3.父组件
<template>
<div class="box">
<h1>全局事件总线$bus</h1>
<hr>
<div class="container">
<Child1></Child1>
<Child2></Child2>
</div>
</div>
</template>
<script setup lang="ts">
// 引入子组件
import Child1 from "./Child1.vue";
import Child2 from './Child2.vue';
</script>
<style scoped>
.box {
width: 100vw;
height: 400px;
background-color: yellowgreen;
}
.container{
display: flex;
justify-content: space-between;
}
</style>
样式:1. display: flex;元素的显示方式设置为Flexbox。Flexbox是一个用于在一维空间(行或列)中排列和对齐子元素的布局模型
2 . justify-content: space-between;这是Flexbox的一个属性,用于定义子元素在主轴(默认为水平方向,即行)上的对齐方式。space-between的值意味着子元素之间的空间是平均分配的,并且第一个子元素将靠近容器的开始边界,最后一个子元素将靠近容器的结束边界。
TIPS:为什么有的import引入带花括号,有的引入没有花括号?
没有花括号的是默认导出,有{}是命名导出
// mathOperations.js
export default function multiply(a, b) {
return a * b;
}
export function divide(a, b) {
return a / b;
}
// main.js
import multiply from './mathOperations.js';
import { divide } from './mathOperations.js';
console.log(multiply(2, 3)); // 输出 6
console.log(divide(6, 2)); // 输出 3
4.v-model
实现父子组件数据同步,方法一:父亲给子组件数据 props,子组件给父组件数据 自定义事件
子组件
// vue3组合式api写法没有this setup没有this
// 利用defineEmits方法返回函数触发自定义事件
// defineEmits方法不需要引入直接使用
<template>
<div class="child">
<h3>钱数{{ modelValue }}</h3>
<button @click="handler">父子组件数据同步</button>
</div>
</template>
<script setup lang="ts">
// 接收props
let props = defineProps(['modelValue']);
let $emit = defineEmits(['update:modelValue']);
// 子组件内部按钮点击回调
const handler = ()=>{
// 触发自定义事件
$emit('update:modelValue',props.modelValue+1000);
}
</script>
<style scoped>
.child{
width: 600px;
height: 300px;
background-color: skyblue;
}
</style>
父组件
<template>
<div>
<h1>v-model :钱数{{ money }}</h1>
<input type="text" v-model="info">
<hr>
<!-- props:父亲给儿子数据 -->
<Child :modelValue="money" @update:modelValue="handler"></Child>
</div>
</template>
<script setup lang="ts">
// v-model收集表单数据,实现双向绑定
// v-model也可以实现组件之间的通信,实现父子组件数据同步的业务
// 父亲给子组件数据 props
// 子组件给父组件数据 自定义事件
import { ref } from 'vue';
// 引入子组件
import Child from './Child.vue';
let info = ref('');
// 父组件的数据钱数目
let money = ref(10000);
// 自定义事件的回调
const handler = (num :any) =>{
// 接收子组件传递过来的数据
money.value=num;
}
</script>
<style scoped></style>
v-model实现父子组件事件同步
<!-- <Child :modelValue="money" @update:modelValue="handler"></Child> -->
<!-- v-model组件身上使用
第一:相当于给子组件传递props[modelValue] =10000
第二:相当于给子组件绑定自定义事件update:modelValue-->
<Child v-model = "money"></Child>
<hr>
<Child1 v-model:pageNo="pageNo" v-model:pageSize="pageSize"></Child1>
</div>
v-model可以同步多个事件
1.给父亲的参数
// 父组件的数据钱数目
let money = ref(10000);
// 父亲的数据
let pageNo = ref(1);
let pageSize = ref(3);
2.父亲的参数传递给子组件
let props = defineProps(['pageNo', 'pageSize'])
let $emit = defineEmits(['update:pageNo','update:pageSize'])
3.子组件绑定事件调用,修改参数
<button @click="handler">pageNo{{ pageNo }}</button>
<button @click="$emit('update:pageSize',pageSize+3)">pageSize{{ pageSize }}</button>
const handler = ()=>{
$emit('update:pageNo',props.pageNo+3)
}
4.父组件显示参数
<h1>v-model :钱数{{ money }}{{ pageNo }}{{ pageSize }}</h1>
5.useAttrs
可以获取组件身上的属性与事件
案例:二次封装el-button组件,并且把鼠标放在上面可以显示文字
新建子组件HintButton.vue
<el-button type="primary" size="small" :icon="Edit" ></el-button>
<!-- 自定义组件 -->
<HintButton type="primary" size="small" :icon="Edit"></HintButton>
type=“primary” size=“small” :icon="Edit"相当于props传参,子组件接受参数
// 接受父组件传递过来的数据
defineProps(['type','size','icon'])
也可以使用useAttrs拿到父亲传过来的参数
// 引入useAttrs方法:获取组件标签身上的属性与事件
import { useAttrs } from 'vue';
// 此方法执行会返回一个对象
let $attrs = useAttrs();
console.log($attrs);
界面显示
v-band绑定三个属性 type size icon
<el-button :="$attrs"></el-button>
<!-- <h1 v-bind="{a:1,b:2}">123</h1> -->
<!-- v-bind可以省略为: -->
文字提示 :title=“title”
// 万一用props接收title
let props = defineProps(['title']);
// props和useattrs方法都可以获取父组件传递过来的属性和属性值
// 但是props接受了useattrs方法就获取不到了
界面显示
<div :title="title">
useAttrs可以接受事件 @click=“handler”,原生DOM事件以及自定义事件
6.ref与$parent
ref:获取真实的DOM节点,获取到子组件的实例vc
$parent:可以在子组件的内部获取到父组件的实例
实例1:在父组件设置一个按钮,父亲的钱数+10,子组件的钱数-10
如何在父组件里获取子组件的信息?
<Son ref="son"></Son>
// 获取子组件实例
let son = ref();
但是拿不到子组件的具体数据,要在子组件暴露方法
// 组件内部数据对外关闭的,别人不能访问
// 如果想让外部访问需要通过defineExpose方法对外暴露
defineExpose(
{
money,
fly
})
拿到子组件的实例,可以用子组件的数据和方法
// 父组件内部按钮点击回调
const handler = () => {
money.value += 10;
// 儿子的钱数减去10
son.value.money -= 10;
son.value.fly();
}
实例2:在子组件女儿设置一个按钮,点击按钮女儿钱数+10000,父组件钱数-10000
在子组件内如何拿到父组件的方法?
<button @click="handler($parent)">点击我爸爸给我10000元</button>
const handler = ($parent) => {
money.value += 10000;
$parent.money-=10000;
}
同样,父组件的钱数也要对外暴露
// 对外暴露
defineExpose({
money
});
7.provide与inject
vue3提供provide(提供)与inject(注入),可以实现隔辈组件传递数据
祖先组件给后代组件提供数据,可以使用provide组件提供数据
// 两个参数:第一个参数是提供的数据Key
// 第二个参数:祖先组件提供数据
provide('TOKEN',car);
后代组件(儿子或者孙子都可以使用)
孙子组件Grandchild.vue中
import { inject } from 'vue';
// 注入祖先组件提供数据
// 需要参数:即为祖先提供数据的Key
let car = inject('TOKEN');
const updateCar = ()=>{
car.value = '自行车';
}
孙子组件可以直接修改爷爷组件传过来的数据,因为他们指向的是同一个对象
8.pinia选择式API
vue2一般用到vuex集中式状态管理容器,去实现任意组件的通信,vue3使用大菠萝pinia类似于vuex,不管是vuex还是pinia,他们存储的数据并非持久化
// vuex:集中式管理状态容器,可以实现任意组件的通信!!!
// 核心概念:state(存储数据)、mutation(唯一可以修改数据的地方)、actions(处理业务,异步等)、getters(计算属性)、modules(模块式开发)
// pinia:集中式管理状态容器,可以实现任意组件的通信!!!
// 核心概念:state、actions、getters
// pinia写法:选择式API、组合式API
如何让两个组件水平排列?
1.给两个组件套一个盒子div
<div class="container">
<Child></Child>
<Child1></Child1>
</div>
2.使用flex工具
.container{
display: flex;
}
pinia写法有两种,一种是选择式api写法,一种是组合式api写法
选择式api类似于vuex
新建一个仓库store/index.ts
// 创建大仓库
import { createPinia } from "pinia";
// createPinia方法可以用于创建大仓库
let store = createPinia();
// 对外暴露,安装仓库
export default store;
在入口文件main.ts引入仓库
// 引入仓库
import store from './store'
app.use(store)
pinia大菠萝图标就会亮出来
创建小仓库store/modules/info.ts
// 定义info小仓库
import { defineStore } from "pinia";
// 第一个参数是小仓库的名字,第二个参数是小仓库配置对象
// defineStore方法执行会返回一个函数,函数作用就是让组件可以获取仓库数据
let useInfoStore = defineStore("info", {
// 存储数据:state
state: () => {
return {
count: 99,
arr:[1,2,3,4,5,6,7,8,9,10]
}
},
actions: {
// 注意:函数没有context上下文对象
// 没有commit、没有mutation去修改数据
updateNum(a: number, b: number) {
this.count++;
}
},
getters: {
total(){
let result:any = this.arr.reduce((prev:number,next:number)=>{
return prev+next;
},0);
return result;
}
}
});
// 对外暴露方法
export default useInfoStore;
vvue3的state写法是函数的写法
如何更新仓库数据?
<button @click="updateCount">点击我修改仓库数据</button>
1.方法一:infoStore.count++;
2.方法二: infoStore.$patch({
// count:111
// })用一个新的数据替换原来的数据
const updateCount = ()=>{
// infoStore.count++;
// infoStore.$patch({
// count:111
// })
// 仓库调用自身的方法去修改数据
infoStore.updateNum(66,77);
}
3.类似vuex的方法: infoStore.updateNum(66,77);infostore没有这个方法,要在仓库action里写这个方法
actions: {
// 注意:函数没有context上下文对象
// 没有commit、没有mutation去修改数据
updateNum(a: number, b: number) {
this.count++;
}
},
选择式api不仅可以调用对象也可以传参
const updateCount = ()=>{
infoStore.updateNum(66,77);
}
actions: {
updateNum(a: number, b: number) {
console.log(updateNum);
this.count+=a;
}
},
getters方法可以简化数据,计算数据
state: () => {
return {
arr:[1,2,3,4,5,6,7,8,9,10]
}
},
getters: {
total(){
let result:any = this.arr.reduce((prev:number,next:number)=>{
return prev+next;
},0);
return result;
}
}
js中 reduce方法介绍:https://blog.csdn.net/w17624003437/article/details/125858710
educe()方法为归并类方法,最常用的场景就是,计算数组中的每一项的总和。
reduce() 方法会遍历数组的每一项,他接收两个参数:
第一个参数:每次遍历都会调用的函数,而这个函数有接收四个参数,分别是:前一个值、当前项、项目的索引和数组对象,而这个函数的返回值,回传给下一次遍历时,执行的这个方法的第一个参数。