vue3高级技巧(持续补充并且复习版本)
ts标注类型
https://cn.vuejs.org/guide/typescript/composition-api.html#typing-ref
基础部分注意事项
v-if v-show
当需要频繁切换时,用v-show
当不需要频繁切换时,用v-if
ref 模版引用
v-for时的模版引用
<script setup>
import { ref, onMounted } from 'vue'
const list = ref([
/* ... */
])
const itemRefs = ref([])
//////// ref 数组并不保证与源数组相同的顺序
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="itemRefs">
{{ item }}
</li>
</ul>
</template>组件上的ref
默认情况下,组件内部属性是私有的,想访问需要defineExpose出来
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
a,
b
})
</script>reactive 局限
有限的值类型
不能替换整个对象
因为proxy响应式跟踪核心是通过属性访问实现的,不能轻易替换响应式对象
let state = reactive({ count: 0 })
// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })解构操作不友好
shallowRef
const state = shallowRef({ count: 1 })
// 不会触发更改
state.value.count = 2
// 会触发更改
state.value = { count: 2 }computed 对象式(避免修改)
注意:
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// 注意:我们这里使用的是解构赋值语法
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>类和样式绑定
<div :class="[isActive ? activeClass : '', errorClass]"></div>watch watchEffect watchPostEffect
三者使用情况
watch(todoId, async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
}, { immediate: true })watchEffect(async () => { // 自动追踪todoId.value
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
})默认情况下,watch watchEffect在Vue组件更新前调用,即在侦听回调中得到的Dom是更新前的dom,想在侦听回调中获取更新后的DOM,需要指明flush: 'post'
watch(source, callback, {
flush: 'post'
})
watchEffect(callback, {
flush: 'post'
})或者使用****
watchPostEffect(() => {
/* 在 Vue 更新后执行 */
})停止侦听
在setup函数中同步创建的侦听器,会自动绑定在宿主实例上,故不需要考虑停止,但是,异步创建的侦器需要手动停止,防止内存泄漏
<script setup>
import { watchEffect } from 'vue'
// 它会自动停止
watchEffect(() => {})
// ...这个则不会!
setTimeout(() => {
watchEffect(() => {})
}, 100)
</script>手动停止: 调用watchEffect返回的函数
const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()Ref 的监听
const targetRef = ref(null);
useHeightComputed(targetRef);我在封装一个计算高度的 hooks 的时候,当 hooks 执行时,targetRef 并未挂载,这样的话我在内部得到的其实是一个 null
其实是一个很简单的问题,但是最开始的时候钻了进去,只需要执行时传进去targetRef,不要.value,然后在内部 watch
watch(target,.....)高级技巧
props单项数据流
当出现想直接更改父组件传递的数据的想法时,请谨慎,谨记
tips:子组件中用新的ref数据承接props数据时,此时意味着props数据只是新ref的初始数据
事件
事件绑定修饰符
.once .native
emit事件校验:
<script setup>
const emit = defineEmits({
// 没有校验
click: null,
// 校验 submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
})
function submitForm(email, password) {
emit('submit', { email, password })
}
</script>v-model修饰符
内置修饰符
https://cn.vuejs.org/guide/essentials/forms.html#modifiers
自定义修饰符
https://cn.vuejs.org/guide/components/v-model.html#handling-v-model-modifiers
例如场景:v-model
插槽
动态插槽名
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
<!-- 缩写为 -->
<template #[dynamicSlotName]>
...
</template>
</base-layout>作用域插槽
应用:让父组件中的插槽dom中使用子组件数据
父组件:
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>具名:
<MyComponent>
<template #header="headerProps">
{{ headerProps }}
</template>
<template #default="defaultProps">
{{ defaultProps }}
</template>
<template #footer="footerProps">
{{ footerProps }}
</template>
</MyComponent>子组件:
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div><slot name="header" message="hello"></slot>依赖注入
https://cn.vuejs.org/guide/components/provide-inject.html#working-with-reactivity
可以用readonly包裹provide提供的数据,防止注入方修改provide('read-only-count',readonly(count))
大型项目用Symbol作为注入名,保证唯一
异步组件
正常使用:
const AdminPage = defineAsyncComponent(() =>
import('./components/AdminPageComponent.vue')
)高级使用
const AsyncComp = defineAsyncComponent({
// 加载函数
loader: () => import('./Foo.vue'),
// 加载异步组件时使用的组件 很快时可能会造成闪烁影响体验
loadingComponent: LoadingComponent,
// 展示加载组件前的延迟时间,默认为 200ms
delay: 200,
// 加载失败后展示的组件
errorComponent: ErrorComponent,
// 如果提供了一个 timeout 时间限制,并超时了
// 也会显示这里配置的报错组件,默认值是:Infinity
timeout: 3000
})配合Suspense组件使用
组件 v-model defineModle
https://cn.vuejs.org/guide/components/v-model.html
为组件绑定 v-modle,不论是父组件还是子组件的修改,两组件都会响应
子 dialog 组件场景下感觉很好用
vue 内 style 标签中的响应式样式
以前我都是通过动态类名完成动态演示的展示,如果用动态内联样式的话会很繁琐
简单的个别样式可以通过动态绑定完成
<template>
<p class="demo">hello</p>
</template>
<script setup>
import { ref } from 'vue'
const theme = ref({
color: 'red',
})
</script>
<style scoped>
.demo {
color: v-bind('theme.color');
}
</style>复杂的样式通过动态绑定 style 完成变量注入
<template>
<div class="detail" :style="styleVars" @click="changeColor">一个小测试</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const color = ref('red');
let styleVars = computed(() => {
return {
'--color': color.value
};
});
const changeColor = () => {
color.value = 'blue';
};
</script>
<style scoped>
.detail {
color: var(--color)
}
</style>
内置组件
https://cn.vuejs.org/guide/built-ins/keep-alive.html
keepAlive
当渲染成本过高的部分需要频繁切换时,建议使用keepAlive
