关于 vue3 中 hooks 在页面中使用的一点注意事项

45 阅读2分钟

关于 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
  }
}
​

这里可以看到,我定义了一个简单的 refcomputed 数据,并且在一秒钟和两秒钟后改变 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>
​

答案可能和我们预想的有一些差别。

首先,页面刷新后内容如下:

hooks-1.png

一秒钟后,内容如下:

hooks-2.png

两秒钟后内容如下:

hooks-3.png

从图中可以发现, 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(),解构使用,使 infoflag 成为顶级属性,表达式才会按预期工作,也就是原本 !state.flag!!state.flag 这里才会是正确的值。而 state.flag 正确是因为,在文本插值时会被正常解包,相当于 state.flag.value

所以将 infoflag 解构使用即可。

<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>
​

现在内容如下:

hooks-4.png

一秒钟后,内容如下:

hooks-5.png

两秒钟后内容如下:

hooks-6.png

参考文章: