关于 vue3 中 hooks 在页面中使用的一点注意事项
最近在使用 hooks
的时候发现了一个很奇怪的问题,代码如下:
// useHooks.js 文件
import {ref,computed} from 'vue'
export const useHooks = () => {
/**
*
* @type {Ref<UnwrapRef<{name:string,age:number}>>}
*/
const info=ref({})
const flag=computed(()=>{
return info.value.name && info.value.name.startsWith('T')
})
setTimeout(()=>{
info.value={
name:'Tom',
age:18
}
},1000)
setTimeout(()=>{
info.value={
name:'James',
age:20
}
},2000)
return {
info,
flag
}
}
这里可以看到,我定义了一个简单的 ref
和 computed
数据,并且在一秒钟和两秒钟后改变 info
对象。
在demo.vue
文件中将 hooks
引入使用,这里各位可以猜一下 v-if='state.flag'
是否生效?template
中使用 state.flag.value
会有值吗? 以及 state.flag
的值是否是 Boolean
值。
<script setup>
import {useHooks} from './useHooks.js'
const state=useHooks()
</script>
<template>
<div class='content'>
<div>不使用 .value </div>
<div v-if='state.flag'>姓名:{{state.info.name}}</div>
<div v-if='state.flag'>年龄:{{state.info.age}}</div>
<div>'state.flag':{{state.flag}}</div>
<div>'!state.flag':{{!state.flag}}</div>
<div>'!!state.flag':{{!!state.flag}}</div>
<div>'state.info':{{state.info}}</div>
</div>
<div class='content'>
<div>使用 .value </div>
<div v-if='state.flag.value'>姓名:{{state.info.value.name}}</div>
<div v-if='state.flag.value'>年龄:{{state.info.value.age}}</div>
<div>'state.flag.value':{{state.flag.value}}</div>
<div>'!state.flag.value':{{!state.flag.value}}</div>
<div>'!!state.flag.value':{{!!state.flag.value}}</div>
<div>'state.info.value':{{state.info.value}}</div>
</div>
</template>
<style scoped>
.content{
font-size:20px;
}
</style>
答案可能和我们预想的有一些差别。
首先,页面刷新后内容如下:
一秒钟后,内容如下:
两秒钟后内容如下:
从图中可以发现, v-if='state.flag'
并未按照我们预期的生效,反而是 v-if='state.flag.value'
生效了,其次可以看到不使用 .value
时,只有 state.flag
正常生效了,而 !state.flag
和 !!state.flag
都没有正常工作,反而是使用了 .value
的情况下所有都正常生效了,这是为什么呢,不是说在 template
中使用的时候不需要 .value
嘛?好了,不卖关子了,实际上,所有的问题都来自于这句 const state=useHooks()
,在官方文档 中说了,在模板渲染上下文中,只有顶级的 ref 属性才会被解包。也就是说,只有 const {flag,info}=useHooks()
,解构使用,使 info
和 flag
成为顶级属性,表达式才会按预期工作,也就是原本 !state.flag
和 !!state.flag
这里才会是正确的值。而 state.flag
正确是因为,在文本插值时会被正常解包,相当于 state.flag.value
。
所以将 info
和 flag
解构使用即可。
<script setup>
import {useHooks} from './useHooks.js'
const {flag,info}=useHooks()
</script>
<template>
<div class='content'>
<div>不使用 .value </div>
<div v-if='flag'>姓名:{{info.name}}</div>
<div v-if='flag'>年龄:{{info.age}}</div>
<div>'flag':{{flag}}</div>
<div>'!flag':{{!flag}}</div>
<div>'!!flag':{{!!flag}}</div>
<div>'info':{{info}}</div>
</div>
<!-- <div class='content'>
<div>使用 .value </div>
<div v-if='flag.value'>姓名:{{info.value.name}}</div>
<div v-if='flag.value'>年龄:{{info.value.age}}</div>
<div>'flag.value':{{flag.value}}</div>
<div>'!flag.value':{{!flag.value}}</div>
<div>'!!flag.value':{{!!flag.value}}</div>
<div>'info.value':{{info.value}}</div>
</div>-->
</template>
<style scoped>
.content{
font-size:20px;
}
</style>
现在内容如下:
一秒钟后,内容如下:
两秒钟后内容如下:
参考文章: