Vue3笔记_<进阶篇—上篇>

本文探讨Vue3的高级特性,包括自定义修饰符与v-model的工作原理,深入讲解Attributes的继承与透传机制,介绍自上而下的依赖注入方式,通过mitt实现组件间通信,以及组合式函数hooks的应用。
摘要由CSDN通过智能技术生成

自定义修饰符与v-model原理

对于又有参数又有修饰符的v-model绑定,生成的prop名将是arg+"Modifiers"

父组件源码

<template>
    <div>
        <h2>vue3的父组件</h2>
        <!-- <child :msgstr="msg" @update:msg="changeMsg" ></child> -->
        <!-- 简写 -->
        <child v-model:msgstr="msg"></child>
        <hr>
        <meny
         v-model:fiest-Name="user.fiestName"
         v-model:last-Name="user.lastName"
         />
         <p>{{ user.fiestName }}---{{ user.lastName }}</p>
         <hr>
         <!-- 自定义修饰符 -->
         <defineFn v-model:titlestr.absolute="tather"></defineFn>
         <!-- 修饰符  .number:将数据转换为数值类型,.lazy:元素失去焦点的时候触发, .trim:将字符串两边的空白(空格)去除 -->
    </div>
</template>

<script setup>
import defineFn from './defineFn.vue'
import  child  from './childer.vue'
import meny from './menyModel.vue'
import  { reactive, ref } from 'vue'
let msg = ref('true')

let user = reactive({
    fiestName:'欧阳',
    lastName:'娜娜'
})
let tather= ref('我是父组件hahaha')
// const changeMsg=(data)=>{
//     msg.value=data 
// }
</script>

三个子组件源码

menyModel文件
<template>
    <div>
        <h2>vue3 多个v-model 绑定</h2>
        姓:<input :value="fiestName" @input="$emit('update:fiestName',$event.target.value)"/><br>
        名:<input :value="lastName" @input="$emit('update:lastName',$event.target.value)"/>
    </div>
</template>
<script setup>
defineProps(['fiestName','lastName'])
defineEmits(['update:fiestName','update:lastName'])
</script>

defineFn文件
<template>
    <div>
        <h2>vue3自定义修饰符</h2>
        <p>title:{{ titlestr }}</p>
        <!-- <input type="text" :value="titlestr" @input="$emit('update:titlestr',$event.target.value)"/> -->
        <input type="text" :value="titlestr" @input="toUppcase"/>
    </div>
</template>
<script setup>
// <defineFn v-model:titlestr.absolute="tather"></defineFn>
// 除了可以接收到titlestr参数外还可以拿到 prop: arg+Modifiers
const props = defineProps(['titlestr','titlestrModifiers'])
const emit = defineEmits(['update:titlestr'])
const toUppcase = (e)=>{
    let val = e.target.value
    if(props.titlestrModifiers.absolute){  
 再判断absolute是否成立,成立则表示使用了absolute了这个修饰符,在absolute处理函数内写入业务代码
        val =  (val.charAt(0).toUpperCase()+val.slice(1))
        emit('update:titlestr',val)
    }
}
</script>

chiled文件
<template>
    <div>
        <h5>vue3的子组件{{ msgstr }}</h5>
        {{ mmg }}
        <input type="text" :value="msgstr" @input="$emit('update:msgstr',$event.target.value)">
    </div>
</template>
<script setup>
// let { msgstr } = defineProps(['msgstr']) //传过来的数据是对象(传递数据的名称:数据)
// console.log(msgstr)  //通过解构赋值,直接输出数据
let hh =  defineProps({ //设置返回值,方便函数内也可以调用传过来的参数
    msgstr:{
        type:String, //定义数据类型
        default:true
    },
    mmg:{
        type:Number,
        default:100 //默认值
    }
})
defineEmits(['update:msgstr'])  //vue3组合式API的接收自定义事件的方式
</script>

Attributes深层继承与透传

"透传attribute"指的是传递给一个组件,却没有被该组件声明为props或emits的attribute或者v-on事件监听器。最常见的例子接收class,style,id

$attrs可以看作是一个安全网,它可以捕获如何我们没有在组件中声明的东西

Attributes继承

当一个组件以单个元素为根作渲染时,透传的attribute会自动被添加到根元素上:

---Child的模板---
注意:Child里只有一个button根元素
<button>click me</button>
---Child的父层---
<Child class='large'></Child>

最后渲染出的DOM结构
<button class='large'>click me</button>

---Child的模板2---
这个模板下有多个元素,所有没有自动透传的行为,如果没有被$attrs显示绑定则会报出警告
<template>
    <button>根节点一</button>
    <button>根节点二</button>
    <!-- 为了进行透传,我们使用显示透传。进行了绑定$attrs的数据全部给到了第三个按钮 -->
    <button :="$attrs">根节点三</button>
    <button>根节点四</button>
    <button>根节点五</button>
</template>
  • 这里的Child并没有将class声明为一个它所接收的props,所有class被视作透传的attribute,自动透传给了Child的根元素上。
  • 事件也会进行透传
  • 当组件有多个根元素时组件没有自动透传行为,如果$attrs没有被显示绑定则会报出一个警告!

深度组件继承

封装组件时,使用attrs充当桥梁,将所有的属性(属性和事件)复制到一个或多个元素上。

禁用attributes继承:如果不想要一个组件自动继承attribute,可以在组件选项中设置inheritattrs:false。如果使用了<script setup>,则需要一个额外的<script>块来写这个选项声明

案例和源码:

最外层
<template>
    <div>
        <h2>vue3深层组件继承</h2>
        <MyButton class="btn-yellowgreen" @click="print(' this is yellogreen ')"></MyButton>
        <MyButton class="btn-red" @click="print(' this is red ')"></MyButton>
        <MyButton class="btn-skyblue" @click="print(' this is skyblue ')"></MyButton>
    </div>
</template>
<script setup>
import MyButton from './MyButton.vue';
const print= (data)=>{
    console.log(data);
}
</script>

MyButton模板
<template>
    <!-- 模板中获取继承 $attrs -->
    <ElButton></ElButton>
</template>
<script setup>
import ElButton from './ElButton.vue';
//在JS中获取父向子传递的数据
import { useAttrs } from 'vue'
const attrs = useAttrs()  //获取到继承的数据
console.log(attrs);  
// defineProps(['class']) //props把class给接收了,所有后面的组件就没有class这个参数
</script>
禁止继承---禁止继承---禁止继承
<script>
    export default{  //重新定义export default可能会提示报错,可以置之不理
        // inheritAttrs:false //禁止自动继承  如果使用了setup组合API就要单独写一个script标签来写 inheritAttrs:false
}
</script>

ElButton模板
<template>
    <button class="btn">按钮</button>
</template>
<script setup></script>
<style scoped>
.btn{
    border:0;
    padding: 10px 20px;
    color: black;
    font-weight: bold;
}
.btn-yellowgreen{
    background:yellowgreen ;
}
.btn-red{
    background:red ;
}
.btn-skyblue{
    background:skyblue ;
}
</style>

自上而下依赖注入

使用场景:一个父组件,里头有子组件,有孙组件,有很多后代组件,都共享父组件的数据

这里主要使用Provide和Inject。在2版本中也可以给后代传递数据和函数,但失去了响应式。他们在vue3中得到了增强

  • provide()和inject()可以实现嵌套组件之间的数据传递
  • 这两个函数只能在setup中使用
  • 父组件中使用provide函数向下传递数据
  • 子组件中使用inject函数获取深层传递过来的数据
  • 不限层级 父组件
祖宗组件
<template>
    <div>
        <h2>祖宗组件</h2>
        <button @click="msg-=20">修改</button>
        {{ msg }}
        <hr>
        <father></father>
    </div>
</template>
<script setup>
import father from './father.vue';
import { provide,ref } from 'vue';
let msg = ref(825102030)
provide('moeny',msg)
const changeMsg = (data)=>{msg.value=msg.value-data}
provide('changeFn',changeMsg)
</script>

父组件
<template>
    <div>
        <h3>父组件</h3>
        <hr>
        <son></son>
    </div>
</template>
<script setup>
import son from './son.vue';
</script>

孙组件
<template>
    <div>
        <h6>孙组件</h6>
        {{ msg }}
        <!-- 孙组件也可以触发祖组件发过来的函数 -->
        <button @click="FN(500)">emit</button>
    </div>
</template>
<script setup>
import { inject } from 'vue'
//接收变量
const msg = inject('moeny')
//接收函数
const FN = inject('changeFn')
console.log(msg);
</script>

事件总线mitt

组件之间的数据交互有多少种方案

  1. 父传子:props
  2. 子传父:自定义事件emit
  3. 组件共享状态:vuex或pinia
  4. 自上而下:provider inject
  5. 事件总线:eventbus(不太推荐)或mitt

mitt使用方法: 

  1.  安装 npm i mitt -S
  2. 在main里面引入mitt,调用mitt将调用后的mitt赋给app.config.globalProperties.$mitt(名称可自定义)=mitt
  3. 组件内的引入vue中的getCurrentInstance并调用再构造出proxy
  4. 发送: proxy.定义的名称.emit('事件名',参数变量)
  5. 接收: proxy.全局定义的名称.on('事件名',(val)=>{})

 代码演示

main.js中
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import mitt from 'mitt' //npm i mitt -S

const emitt = mitt()  //调用
const app = createApp(App)

app.config.globalProperties.$mitt =emitt  
mitt赋值到全局
在组件中引入vue结构出 getCurrentInstance并调用,再结构出proxy
proxy下就可以拿到全局定义的$mitt,然后emit发送事件,on接收事件
app.mount('#app')//挂载
A组件
<template>
    <div>
        <h2>兄弟A</h2>
        {{ msg }}
        <button @click="emitB">这是A的参数</button>
        <button @click="msg-=20">change</button>
    </div>
</template>
<script setup>
import { ref,getCurrentInstance } from 'vue';
let { proxy } = getCurrentInstance() //调用!里面是对象类型数据,然后结构出proxy方法
const emitB=()=>{
    //第一个值是传递的参数名,第二个值是传递的参数
    proxy.$mitt.emit('AAAAA',msg)
}
let msg = ref(12312312)
</script>

B组件
<template>
    <div>
        <h2>兄弟B</h2>
        {{ value }}
    </div>
</template>
<script setup>
import { ref,getCurrentInstance,onMounted } from 'vue'
let { proxy } = getCurrentInstance()
let value = ref('')
onMounted(()=>{
    //第一个值是接收的参数名(根据参数名接收),第二个值是回调函数
    proxy.$mitt.on('AAAAA',(msg)=>{
        console.log(msg);
        value.value=msg.value
    })
})
</script>

组合式函数hooks(钩子)

Vue应用的概念中,“组合式函数”是一个利用Vue的组合式API来封装和复用有状态逻辑的函数

Hook本质是一个函数,

自定义hook:使用vue3的组合式API封装的可复用的功能函数,类似于vue2的mixin技术

封装复用代码,让组合API逻辑更清楚易懂

页面文件
<template>
    <div>
        <h2>获取鼠标位置</h2>
        <p>X:{{ x }}--Y:{{ y }}</p>
        <hr>
        <h3>点击次数增加</h3>
        {{ count }}<br>
        <button @click="add">count++</button>
    </div>
</template>
<script setup>
import  useMove  from '../../hooks/useMove';
import  useCount  from '../../hooks/useCount';
const {x,y} = useMove() //调用函数并拿到返回值
const { add,count }=useCount()  //调用函数并拿到返回值
</script>

封装的两个hook

userMove文件
import { reactive , onBeforeUnmount,onMounted, toRefs } from 'vue'
export default function(){
    let mount = reactive({
        y:'',
        x:''
    })
    onMounted(()=>{
        window.addEventListener('mousemove',move)
    })
    const move = (e)=>{
        mount.x=e.clientX
        mount.y=e.clientY
    }
    onBeforeUnmount(()=>{  //清楚事件,防止内存泄漏
        window.removeEventListener('mousemove',move)
    })
    将mount变为普通函数属性值变为ref返回出去,方便结构响应数据
    return { ...toRefs(mount) }
}

userCount文件
import { ref } from 'vue'
export default function(){
    let count=ref(0)
    const add = ()=>{
        count.value++
    }
    return { count ,add }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我的白银时代

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值