优雅的处理 ts 类型问题
app.vue
<MyButton type="primary" @click="onclick" :msg="msg"></MyButton>
-MyButton.vue-
<script>
import {ButtonProps} from 'element-plus'
// element-plus 导出了prop类型!!!
step1:const props = defineProps<ButtonProps>(),
//定义了props 就可以替换 v-bind="$attrs" 为 props, 这样
</script>
<div>
<el-button :loading="loading" @click="onclick" v-bind="props"></el-button>
</div>
step2: step1后,:loading="loading" 会有类型提示错误, 这是因为在 element-plus 中,buttonprops中存在 loading这个属性
所以需要通过Omit,剔除某些属性!!! const props = defineProps<Omit<ButtonProps, 'loading'>>(), 剔除掉这个loading类
step3: element-plus 中 的props 类型中存在很多声明成必传的,但是其实不是必须的
借助 Partial 这个工具类型,将属性变为可选!!! const props = defineProps<Partial<Omit<ButtonProps, 'loading'>>>()
step4: 需要注意属性继承的问题, 这里在 <MyButton type="primary" @click="onclick" :msg="msg"></MyButton> 定义的click ,由于vue的属性继承默认是TRUE, 所以el-button 外的div 为root 节点是会继承到这个click 事件的,所以会触发两次click div一次 el-button 一次。 这里需要在
defineOptions({
inheritAttrs:false // 关闭属性继承
})
vue3 中 $attrs
import {useAttr} from 'vue'
$attr === const attr = useAttr()
表示 获取除了 props 之外的所有的属性。
比如:
<MyButton type="primary" @click="onclick" :msg="msg"></MyButton>
-MyButton.vue-
《script》
import {useAttr} from 'vue'
const attrs = useAttr()
const onClick = async() => {
const data = await attrs?.onClick()
}
</script>
<el-button @click="onclick" v-bind="$attrs"></el-button>
//这样 三个 属性就都自动绑到组件内部了。
但是如果 const props = defineProps<{type:string}>()
定义了一个props,里面有type:string, 那么 type="primary" 就不会被$attr 所捕获,而是到props里了
二次封装组件,如何统一一次性暴露子组件所有的方法.
在子组件defineExpose中返回一个代理对象,get对应key的时候,返回对应的方法。
-myInput.vue-
<js
const inputRef = ref()
defineExpose(new Proxy(
{},
{
get(target,key) {
return inputRef.value?[key]
}
//但是注意,这里必须要判断一下是否key在ref对象上
has(target,key) {
return key in inputRef.value
}
}
))
>
<template>
<div>hello world</div>
<el-input ref="elInput" v-bind="$attrs">
<template v-for="(_, slot) in $slots" :key="slot" #[slot]="slotProps">
<slot :name="slot" v-bind="slotProps"></slot>
// 对于插槽的处理!!!
</template>
</el-input>
</template>
-app.vue-
<js
const myInputRef = ref()
const onClick = function() {
myInputRef.value.focus()
}
>
<template>
<button @click="onClick">点击获取input焦点</button>
<myInput v-model="msg" ref="myInput">
<template #prepend>
<el-button></el-button>
</template>
<template #append>
<el-button></el-button>
</template>
</myInput>
</template>
vue动画
@vueuse/motion
vue 动画库 也是vueuse的一部分!! npm i @vueuse/motion !!! 不是在vueuse/core 里的
ref 和 reactive 的区别
官方建议直接ref一把梭哈。
reactive 只能声明 数组或者对象 reactive([]|{}) ref 可以处理基础类型 reactive(0) 会报错
而且 对于 const list = reative([1,2,3])
当赋值 list = await getData() // 返回 [4,5,6] 视图不会变化,因为reactive声明的变量地址的改变【reactive([1,2,3]) 与 [3,4,5] 地址不同 】是无法被vue 监听到。
但是ref ,赋值变化是可以被vue监听到的
vue2 vue3 中如何监听子组件的生命周期!!
[【对于一些需要等到子组件加载完毕后才发起的操作都可以在这个方式里面调用!!!】]
-app.vue-
const childMounted = () => {}
<Child @childMounted="childMounted">
// 关于这种暴露自定义方法去监听子组件声明周期对于自己写的组件是可以的,但是对于比如来自第三方组件库的组件就是不行的。
</child>
vue2中监听第三方组件库的方法!!!
《comp1 @hook:mounted="childMounted"》</comp1>
vue3中监听第三方组件库的方法!!!
《comp1 @vue:mounted="childMounted"》</comp1>
-child.vue-
const emit = defineEmits(['childMounted'])
onMounted(() => {
emit('childMounted')
})
#vue 异步更新以及nextTick原理
都知道vue 视图更新是异步的,这是因为可能会存在多个视图更新任务同时触发,
h 函数的使用
h 函数返回一个vnode
cons comp = h('div',{}, 'hello world')
<template>
<component :is="comp"></component>
</template>
改为: const comp = () => h('div',{style:{},
onClick() { // 这样传递点击事件!! }}}, msg.value)
setTimeout(() => {msg.value = 'changed!!!'},2000) 这时候两秒钟后 视图就会改变, 这是因为comp 不是直接返回一个vnode ,而是返回一个函数, 函数执行的时候才返回一个vnode, 此时函数是在组件渲染的时候执行的,这时候调用函数的环境就是一个effect, 所以就会收集到依赖并且通知更新!!!
这种用法就是函数式组件的雏形,还可以传参数!!!
也可以直接渲染一个组件 import helloWorld from '@/compoments/helloWorld.vue'
const comp = () => { return h(helloWrold) }
监听自定义事件:
如何传递props?
接收props
接收嵌套的作用域插槽?
h 函数的使用场景
场景1: ant design vue 中 a-table的 列的自定义
更好的方式!!!
场景2, 点击一个按钮,弹框操作
常规的方式都是: <a-button @click="handleClick" />
handleClick() { open.value = true }
另一种的方式,借助于h函数:
场景3 组件的二次封装!
app.vue
child.vue 里面渲染helloworld
这路需要将传递给child的插槽传递给helloworld。