在做项目的过程中,发现 VueUse 一个很鸡肋的 hook
点击关注公众号,“技术干货”及时达!
背景最近刚入职一家公司,在项目中发现了一个非常巧妙的展示弹窗的方式:
首先我们想想,不管一个弹窗长什么样子,它都会有一个控制显示的 「visible」,父组件传进来的 「props」,还有就是 「弹窗组件本身」,基于这一点我的这位同事把它们封装到一个 hook 当中,并且暴露一个 onToggleComponent 方法切换组件。然后在一个公共组件内调用这个 hook 拿到这些公共状态,再用一个动态组件把这个弹窗显示出来,并且绑定对应的状态与事件。
我还真没这样用过,可能是之前接触到的项目太小,一个页面内也就那几个弹窗,不像现在有十几个弹窗,如果不封装 hook,光这些弹窗的显示逻辑都要占几十行代码了,还都是重复的。不得不说,这种方式确实是妙!??
细心的 「jym」 已经发现这其中涉及到一个问题:「公共状态」
那么在 「vue」 项目中,当我们有很多组件需要共享状态时,你能想到几种方式?
共享状态的几种常用方式1、props、emit(父与子共享状态)
2、provide,inject(多组件)
3、使用状态库,比如:vuex、pinia
4、发布订阅
5、......
以上是常用的几种方式,但是这里需要共享状态的是一堆同级弹窗组件,并且同一时间只有一个弹窗显示,也就是同一时间只有一个弹窗组件会用到这些状态,我的这位同事使用的是 vueuse 中一个创建全局状态的 hook:「createGlobalState」
vueuse官方文档中对它的介绍:
然后我先了解了一下它的使用:
createGlobalState 的使用详细请查看:https://vueuse.nodejs.cn/shared/createGlobalState/
import{createGlobalState}from'@vueuse/core'
//store.js
import{computed,ref}from'vue'
exportconstuseGlobalState=createGlobalState(
()={
//state
constcount=ref(0)
//getters
constdoubleCount=computed(()=count.value*2)
//actions
functionincrement(){
count.value++
}
return{count,doubleCount,increment}
}
)
这样其他不同组件就可以通过调用 useGlobalState 获取 count, doubleCount, increment 这三个状态,实现了状态共享。
非常方便是不是,然后我就想去了解一下 vueuse 是怎么实现的。下面是源码:
从官网进入源码发现实现并不复杂,函数体只有十几行代码,他是在一个闭包内保存了这些状态并返回一个 hook 去获取它们。但是这里又引出了一个新的 api,就是 vue 的 「effectScope」
effectScope的介绍与使用//类型
functioneffectScope(detached?:boolean):EffectScope
interfaceEffectScope{
run(fn:()=T):T|undefined//如果作用域不活跃就为undefined
stop():void
}
//示例
constscope=effectScope()
scope.run(()={
constdoubled=computed(()=counter.value*2)
watch(doubled,()=console.log(doubled.value))
watchEffect(()=console.log('Count:',doubled.value))
})
//处理掉当前作用域内的所有effect
scope.stop()
effectScope 函数会返回一个作用域对象,对象上有一个 run 方法和一个 stop
方法,run 方法接受一个状态工厂函数,它会统一收集内部产生的依赖到一个作用域内。调用 stop 函数处理掉当前作用域内的所有 effect,停止追踪状态变化。
官方文档:https://cn.vuejs.org/api/reactivity-advanced.html#effectscope
更详细的讲解:https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md
思考effectScope 为我们提供了「手动统一收集依赖」,并且「统一停止追踪依赖」变化的语法糖。
vueuse 利用 vue 的 effectScope 统一收集了依赖,但是通过 createGlobalState 的源码来看他并「没有调用stop」函数去停止追踪依赖变化。
那既然你不需要统一停止追踪,那统一收集到一个作用域又有什么意义?直接使用一个简单闭包不是也能实现相同的效果,以下是闭包与effectScope两种实现方式的对比:
createGlobalState(vueuse源码)typeAnyFn=(...args:any[])=any
exportfunctioncreateGlobalStateFnextendsAnyFn(stateFactory:Fn):Fn{
letinitialized=false
letstate:any
constscope=effectScope(true)
return((...args:any[])={
if(!initialized){
state=scope.run(()=stateFactory(...args))!
initialized=true
}
returnstate
})asFn
}
myCreateGlobalState(闭包实现)functionmyCreateGlobalStateFnextendsAnyFn(stateFactory:Fn):Fn{
conststate=stateFactory()
return(()=state)asFn
}
两种实现的使用示例//vueusecreateGlobalState
exportconstuseGlobalState=createGlobalState(
()={
//state
constcount=ref(0)
//getters
constdoubleCount=computed(()=count.value*2)
//actions
functionincrement(){
count.value++
}
return{count,doubleCount,increment}
}
)
//闭包方式
exportconstuseGlobalState=myCreateGlobalState(
()={
//state
constcount=ref(0)
//getters
constdoubleCount=computed(()=count.value*2)
//actions
functionincrement(){
count.value++
}
return{count,doubleCount,increment}
}
)
并且我在公司项目中把 vueuse 的 createGlobalState 函数更换为我的 myCreateGlobalState,依然能正常使用,那我是不是可以认为 createGlobalState 就是个很鸡肋的 hook??。
总结vueuse 通过 vue 的 「effectScope」 实现了 「createGlobalState」,用来创建全局状态,方便多组件取用。
但是我觉得 effectScope 的意义在于统一停止追踪依赖变化。createGlobalState 是为了创建全局状态,并不需要停止追踪依赖。那就没必要使用 effectScope 去实现,简单闭包实现即可。
这是我的理解,也是我的疑惑,如果有大佬知道使用 effectScope 实现的好处或意义,请在评论区指教???♂?(抱拳)
参考文章https://vueuse.nodejs.cn/shared/createGlobalState/
https://github.com/vueuse/vueuse/blob/main/packages/shared/createGlobalState/index.ts
https://cn.vuejs.org/api/reactivity-advanced.html#effectscope
https://juejin.cn/post/7412922729517203465
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线