Vue 2结合@vue/composition-api时,组合式函数内部的ref无响应,置于外部却正常的原因咨询
这其实是作用域和实例唯一性的问题,咱们一步步拆解清楚:
1. 内部定义ref:每次调用都生成全新实例
当你把files = ref([])写在useRfp函数内部时,每个组件调用useRfp时,都会创建一个独立的ref实例。也就是说,组件A和组件B拿到的files是完全不相关的两个响应式对象——你在A里更新的是A自己的私有状态,B的files自然不会同步变化,这是符合封装原则的设计,专门用来维护组件私有状态。
2. 外部定义ref:所有组件共享同一个单例
如果把files定义在函数外部,它就变成了一个模块级的单例变量。不管多少组件调用useRfp,拿到的都是同一个ref实例。所以一个组件修改files.value后,所有引用它的组件都会感知到变化,这就实现了同步,但缺点是破坏了封装——外部代码如果直接import这个变量,也能随意修改它,很容易引发不可控的状态混乱。
3. 官方鼠标示例为什么用内部ref?
官方的useMouse设计意图是给每个组件提供独立的鼠标位置状态——比如页面上有两个组件,各自需要追踪鼠标在自己区域内的位置,那它们就应该拥有独立的x、y。这种场景下,内部定义ref正好满足“每个组件私有状态”的需求,和你需要的多组件同步场景完全不同。
推荐的解决方案:封装共享状态同时保持封装性
如果你不想把ref暴露在函数外部,又要实现多组件同步,可以用闭包封装单例状态,既保证状态唯一,又限制外部直接修改:
import { ref } from '@vue/composition-api' // 立即执行函数创建闭包,隐藏内部状态 const useRfp = (() => { // 单例ref,仅在闭包内创建一次 const files = ref([]) // 封装修改状态的方法,避免外部直接操作ref const addFile = (file) => { files.value.push(file) } const clearFiles = () => { files.value = [] } // 返回给外部的函数,每次调用都返回同一个状态和操作方法 return () => { return { files, addFile, clearFiles } } })() export { useRfp }
这样所有组件调用useRfp时,拿到的都是同一个files实例,同时只能通过你暴露的addFile、clearFiles方法修改状态,既保证了多组件同步,又维持了代码的封装性和可维护性。
另外,Vue2中也可以用Vuex做全局状态管理,或者用provide/inject配合ref实现跨层级组件的状态共享,你可以根据业务场景灵活选择。
内容的提问来源于stack exchange,提问作者Victor

