vue使用pinia实现全局无缝通信
前言
细数 vue 中的数据通信方式,足足有十几种,其中包括了父子互传、兄弟互传、隔代互传、无关互传,这十几种方式每个都有其不同的语法,也就是说我们需要根据这些组件的亲戚关系选择最合适的通信方式,我们无时无刻不在使用各种形式的参数传递。MD,像极了我们混乱的前端框架圈。
mqtt 协议
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅
(publish/subscribe
)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布,适用于 低性能 的物联网设备。
为什么要提mqtt呢,因为我接下来要说的和mqtt的通信方式息息相关。
首先简单介绍下 mqtt 这个东西,简单来说,它由三个部分组成:服务器(mqtt broker)、发布者(publisher)、订阅者(subscriber)。其中发布者和订阅者的身份通常是混合的,即一个用户可以既是发布者又是订阅者,当发布消息时,就是发布者,当订阅时,就是订阅者。
在发布消息时,需要指定一个主题(topic),当然,订阅时也要指定一个主题。向某个主题发布消息时,服务器会向所有订阅这个主题的客户端分发消息,当订阅者接收到时,再根据消息内容进行它的下一步操作。
举个例子,大概是:你(订阅者)跟你妈(服务器)说,你弟弟(发布者)打游戏就告诉你,你好打他,然后你妈说好的(订阅成功),然后你弟弟打游戏(发布)的一瞬间,消息被你妈捕获到了,然后告你了,你就成功打了你弟弟。
在上面的例子中,你通过订阅消息,而后执行了某些操作
发布订阅思想
我们都知道,vue的双向绑定原理是基于发布订阅范式的(面试必备,擦,哥们)。想必你和我一样,即使看了几遍 vue 双向绑定原理,还是对这个字眼不太理解,什么叫发布订阅范式,发布订阅在生活和编程中无处不在,是一种通俗意义,并不是编程专有术语。
其实,vue 中的 watch / watchEffect 就是一个订阅的表现。
watch(someValue,()=>{ // ... })
上面的代码,订阅了 someValue
的值变化这一事件
。但是好像这个例子中缺失了发布者,其实发布者是隐式存在的。
someValue = otherValue
此时,这个js 语句
就成了 发布者。
vue3 中的 watch 方法十分强大,它支持 深度监听、监听对象、对象下的单个属性、多变量监听等。
使用 watch 和 pinia 实现全局事件发布订阅
通过上面的描述,不难发现用 watch 来监听 pinia 中的某些属性,即可达到订阅的效果,从而实现无障碍 传参/通信,那么我们把所有涉及传参和通信的场景都使用 pinia ,可行吗?
我认为可行,以这种方式传参,完全不用考虑值从哪里来,将要到哪里去。哪里用,就在哪里 watch。
适用场景
通过监听全局变量变化从而执行下一步操作的这种写法,听起来好像对性能影响很大,而实际上呢,pinia 对此已经做了比较合适的机制,首先 Pinia store 依靠 pinia
实例在所有调用中共享同一个 store 实例,你可以定义任意多的 store,而这些 store 不会单独占用内存,所以放心大胆的在多文件中创建 store 即可。
比如,现在有一个 全局的地图组件mapbox
,又有若干个小组件控制着地图上的图层的增删改查,我们无需在每个组件中都向mapbox
组件传值、或者用ref
获取其实例,我们只需把需要的数据放到pinia中,然后在mapbox
组件内对其监听。
// Mapbox.vue watch(() => store.currGridData, (newValue) => { renderGrid(newValue) })
上面的代码中监听了store.currGridData
数据,当其数据有变化时,则重新渲染此数据。当然,如果需要清除,就直接把此数据置空就好了,可以在监听中新增空数据处理,或者在renderGrid
函数中处理。
这么一来,所有的组件,无论在什么位置,什么路径,只要监听某值的变化,即可做到类似传值的效果。
潜在的问题
内存泄露
当然世界上没有十全十美的事情,这么干正常情况下对性能几乎是没有影响的,但一旦出现问题,那将是致命的。虽然 pinia 的实例是单例的,但是引用却不是,每新增一个 对store值的watch,就会新增一个对此值的引用,正常情况下,vue会主动在页面销毁时移除这些 watch,但是如果 watch 放在了异步体内,则需要注意手动移除它们,否则就会使这些内存常驻,也就是内存泄露。
死循环
完全依靠 watch 来执行操作,会在不经意间造成死循环或者栈溢出的问题。举个例子
watch(()=>store.someValue,()=>{ store.someValue = otherValue })
如果otherValue的值是一个固定值,那么在下一次将不再进入了,因为值没有再变化,但是如果值不是固定值,很容易看出来,这个 watch 实际上是一个死循环,当操作了store.someValue
的值后,又会重新回到这个watch。要避免这个问题也很简单,像通常的递归函数一样,给他一个跳出的条件即可
watch(()=>store.someValue,(nv)=>{ if(nv == someValue) return store.someValue = otherValue })
不触发问题
当监听的某值不变化时,watch 不会触发,这样就会导致另一个问题,如果依赖某个值的变化来做一些操作,而这个操作和这个值的变化又不是双向耦合的,此时就会造成给某值赋值时,不会触发 watch。
例如通过值goNewArea
来使地图跳转到新的地区视角。
watch(()=>store.goNewArea,()=>{ map.flyTo({ // ... }) })
比如有一个按钮,会将其赋值为北京市
,那么地图正常跳转到北京,如果此时我们通过拉动地图视角,使实际区域变成了上海市
,此时再通过按钮将其赋值为北京市
时,地图将不会触发跳转,因为store.goNewArea
没有变化。
要解决这个问题,则应该给这个值一个置空。即:
watch(()=>store.goNewArea,()=>{ map.flyTo({ // ... }) map.once('moveend',()=>{ store.goNewArea = "" }) })
上面提到,这样做会导致watch重复触发死循环的问题,所以还要再加一句:
watch(()=>store.goNewArea,(nv)=>{ if(nv == "") return map.flyTo({ // ... }) map.once('moveend',()=>{ store.goNewArea = "" }) })
总结
使用 watch 配合 pinia 全局变量,来实现全局的事件控制,在一定意义上简化了开发流程,说实话,写起来非常爽,也非常清晰。但是一旦变量多起来,相互影响的概论就会增大,管理庞大的 watch 们也是一项负担,你需要时刻注意不要内存溢出、死循环。
总之这种写法的灵活性拉满了,但是需要非常清晰的思路和对业务深刻的理解,才能游刃有余。
到此这篇关于vue使用pinia实现全局无缝通信的文章就介绍到这了,更多相关vue pinia全局无缝通信内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
vue2+elementUI的el-tree的选中、高亮、定位功能的实现
这篇文章主要介绍了vue2+elementUI的el-tree的选中、高亮、定位功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2022-09-09详解Vue、element-ui、axios实现省市区三级联动
这篇文章主要介绍了Vue、element-ui、axios实现省市区三级联动,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2019-05-05
最新评论