Vue散记
阻止冒泡
<button @click.stop="clickTap">123</button>
阻止默认提交行为
<form action="/">
<button @click.prevent="clickTap" type="submit">123</button>
</form>
ref配合ts使用
import { ref, Ref } from "vue"
let message: Ref<string> = ref('波宁')
changeMsg = () => {
message.value = '宁波'
console.log(message)
}
shallowRef的使用
import { shallowRef, triggerRef } from 'vue'
let message = shallowRef({
name: 'tian'
})
const changeMsg = () => {
message.value = { name: 'qiu' }
// 更改message.value.name无响应式
}
const changeMsgForce = () => {
message.value.name = 'qiu'
triggerRef(message)
}
这种响应式用于提升性能
setup语法糖中使用defineProps
type Props = {
title: string,
data: number[]
}
defineProps<Props>()
当然也可以将属性设置为可选的,并且给予初始值
type Props = {
title?: string,
data?: number[]
}
withDefaults(defineProps<Props>(),{
title: "默认值",
data: () => [1,2,3]
})
注意这里复杂的数据类型需要函数将所需要赋的值return出来
setup语法糖中使用defineEmits
const emit = defineEmits(['on-click'])
const clickTap = () => {
emit('on-click',list)
}
父组件中
<template>
<Menu @on-click="functionName">
</template>
<script setup lang="ts">
const functionName = (list: number[]) => {
console.log(list)
}
</script>
defineExpose的使用
defineExpose({
list,
flag
})
这么做父组件就可以通过实例去访问子组件中暴露的内容,更加安全。
先判断对象存在再读取属性防止报错的替代方案
console.log(list) // undefined
console.log(list.length) // error
console.log(list && list.length) // undefined
console.log(list?.length) // undefined
拓展
当前边的表达式使用了?后可以使用??来返回当前者表达式为undefined或者null时一个指定的值。
console.log(list?.length ?? []) // []
TypeScript中Pick的使用
type Tabs = {
name: string,
comName: any
}
type Com = Pick<Tabs, 'comName'>
markRaw跳过代理
const data = reactive<Tabs[]>([
{
name: '组件A',
comName: markRaw(A)
},
{
name: '组件B',
comName: markRaw(B)
},
{
name: '组件C',
comName: markRaw(C)
}
])
注册的组件本身会拥有代理,reactive也会添加一层代理,这是多余的。
动态插槽
<Dialog>
<template #[name]>
<div>
something
</div>
</template>
</Dialog>
let name = ref('header')
关于白屏
朋友面试的时候面试官问白屏怎么解决,在三个月的答案求索过程中,终于知道原来问的是异步问题,不是怎么处理bug。
异步组件
利用defineAsyncComponent来引入一个异步组件
const A = defineAsyncComponent(() => import('./boning.vue'))
需要配合<Suspense>组件使用。
<Suspense>
<template #default>
<A></A>
</template>
<template #fallback>
<div>loading...</div>
</template>
</Suspense>
new Array(n).fill(0)初始化数组的替代方案
Array.apply(null,{length: 81} as number[])
数字过渡效果
const num = reactive({
current: 0,
tweenedNumber: 0
})
watch(()=> num.current,(newVal,oldVal) => {
gasp.to(num,{
duration: 1,
tweenedNumber: newVal
})
})
<input v-model="num.current" step="20" type="number">
{{ num.tweenedNumber.toFixed(0) }}
手撸实现eventBus
type BusClass = {
emit: (name: string) => void
on:(name: string,callBack: Function) => void
}
type Pramskey = string | number | symbol
type List = {
[key: Pramskey]: Array<Function>
}
class Bus implements BusClass {
list: List
constructor() {
this.list = {}
}
emit(name: string, ...args:Array<any>) {
let eventName: Array<Function> = this.list[name]
eventName.forEach(fn => {
fn.apply(this, args)
})
}
on(name: string, callBack: Function) {
let fn:Array<Function> = this.list[name] || []
fn.push(callBack)
this.list[name] = fn
}
}
export default new Bus()
mitt的使用
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt'
const Mit = mitt()
//TypeScript注册
// 由于必须要拓展ComponentCustomProperties类型才能获得类型提示
declare module "vue" {
export interface ComponentCustomProperties {
$Bus: typeof Mit
}
}
const app = createApp(App)
//Vue3挂载全局API
app.config.globalProperties.$Bus = Mit
app.mount('#app')
// A组件派发
<template>
<div>
<h1>我是A</h1>
<button @click="emit1">emit1</button>
<button @click="emit2">emit2</button>
</div>
</template>
<script setup lang='ts'>
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance();
const emit1 = () => {
instance?.proxy?.$Bus.emit('on-num', 100)
}
const emit2 = () => {
instance?.proxy?.$Bus.emit('*****', 500)
}
</script>
<style>
</style>
// B组件监听
<template>
<div>
<h1>我是B</h1>
</div>
</template>
<script setup lang='ts'>
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
instance?.proxy?.$Bus.on('on-num', (num) => {
console.log(num,'===========>B')
})
</script>
<style>
</style>
// 监听所有事件
instance?.proxy?.$Bus.on('*',(type,num)=>{
console.log(type,num,'===========>B')
})
// 移除监听事件
const Fn = (num: any) => {
console.log(num, '===========>B')
}
instance?.proxy?.$Bus.on('on-num',Fn)//listen
instance?.proxy?.$Bus.off('on-num',Fn)//unListen
// 清空所有监听
instance?.proxy?.$Bus.all.clear()
样式穿透
:deep(.el-input__inner) {
background: red;
}
插槽选择器
:slotted(.a){
color: red
}
全局选择器
:global(div) {
background-color: pink;
}
动态css
<template>
<div class="div">
动态css
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// const style = ref('pink')
const style = ref({
color: 'red'
})
</script>
<style scoped>
/* .div {
color: v-bind(style);
} */
.div {
color: v-bind('style.color');
}
</style>
Router传参
利用query
传出,此时参数会显示在url上
import { useRouter } from 'vue-router'
const router = useRouter()
router.push({
path: '/login',
query: {
something
}
})
传入,注意导入的模块不同
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.something)
利用params传参
传出,不会显示在url中
import { useRouter } from 'vue-router'
const router = useRouter()
router.push({
name: 'Login', // 必须使用name
params: {
something
}
})
传入,params存储在内存当中,刷新会消失
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.something)
router定位先前滚动距离
const router = createRouter({
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {
top:0
}
}
// 延迟
// return new Promise((r)=> {
// setTimeout(() => {
// r({
// top:15
// })
// },2000)
// })
}
})