iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >如何实现微信小程序中的数据侦听
  • 776
分享到

如何实现微信小程序中的数据侦听

2024-04-02 19:04:59 776人浏览 八月长安
摘要

这篇文章将为大家详细讲解有关如何实现微信小程序中的数据侦听,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在小程序项目中, 我们的通常会使用到使用到一个全局对象作为各个页面

这篇文章将为大家详细讲解有关如何实现微信小程序中的数据侦听,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

小程序项目中, 我们的通常会使用到使用到一个全局对象作为各个页面通用的数据存储容器, 将它绑定到app对象后, 就能在每一个页面都自由的操纵这个对象. 然而在实践中, 由于这个对象及其属性不具备响应式条件, 它不能直接参与业务逻辑的编写, 能力仅仅局限于数据储存. 若是在Vuejs项目中, 我们可能经常使用到 Vue.$watch 去侦听某个数据是否发生变化, 小程序却缺乏这种能力.

在这篇文章中, 我将用150行代码, 手把手带你打造一个小程序也可以使用的侦听器(下简称VX):

// 一个快速赋值的语法糖函数, 可以创建结构为 { value: a { b: { val: ''} } } 的对象
vx.set('value.a.d', { val: '' })
// 对某个属性进行侦听, 如果发生改变, 则执行相应函数(可多次watch以执行多个函数)
vx.watch('value.a.d.val', newVal => {
 console.log(`val改变为 : `, newVal)
})
value.a.d.val = 3 // val改编为 : 3

使用VX侦听器, 我们可以更加方便的管理各个页面的状态. 同时, 我们凭借 watch 语法, 可以更优雅地编写业务逻辑.

坐稳了, 三轮车准备启动了~ 各位评论见~ :yum:

稍微理一理思路

在全局对象中, 我们不一定要对每一个属性都进行侦听, 所以VX主要的功能就是通过set去设置某个具体属性的setter/getter, 同时通过watch向添加该属性添加需要订阅的回调函数.

依赖对象的实现

首先我们需要造一个通用的 依赖对象 , 依赖对象携带一个订阅数组用于存放一组回调函数, 同时它还应该包括一些操作订阅数组能力(如添加订阅, 清空订阅)的函数

class Dep {
 constructor () {
 this.subs = []
 }
 // 将回调添加到数组中
 addSub (fn) {  }
 delSub (fn) {  }
 // 执行数组中每一项函数
 notify (newVal, oldVal) {
 this.subs.forEach(func => func(newVal, oldVal))
 }
}

全局对象中每一个响应式属性(及其每一个子属性), 都应该和一个新的Dep实例保持一一对应的关系, 这样我们进行侦听变化, 执行订阅的回调函数时, 只需要找到对应的实例执行 notify 通知更新即可.

设置响应式属性

defineProperty

可能是因为接触DefineProperty要比接触Proxy早一些的缘故, 代码使用了前者进行响应式的实现, Object.defineProperty方法会直接在一个对象上定义一个新属性, 这里快速过一遍 defineProperty 具体配置:

// @param obj 要在其上定义属性的对象
// @param key 要定义或修改的属性的名称
Object.defineProperty(obj, key, {
 // 该属性是否能被枚举
 enumerable: true,
 // 该属性能否被删除
 configurable: true,
 // 访问该属性则会执行此方法
 get: () => {
 return val
 },
 // 修改该属性时会执行此方法
 set: newVal => {
 val = newVal
 },
 // value & writeble 不能和 getter/setter 同时出现
})

通过对defineProperty进行上层封装, 我们可以轻松的实现在全局对象上设置响应式属性功能, 在此, 我们结合刚才定义的Dep对象, 将一个新的dep实例绑定到新增属性的setter中:

set (key, val, options = {}, obj = this.store) {
 const dep = new Dep()
 Object.defineProperty(obj, key, {
 enumerable: true,
 configurable: true,
 get: () => {
  return val
 },
 set: newVal => {
  if (newVal === val) {
  return
  }
  dep.notify(newVal, val)
  val = newVal
 }
 })
}

每当对应属性被赋值, 就会执行依赖数组中的回调函数.

不过这样还不够, 我们还得想办法获取到这个dep实例, 才能给它的依赖数组填充函数.

这边提供一个很简单的思路, 并不推荐实践中这么做:

set (key, val, options = {}, obj = this.store) {
 const dep = new Dep()
 Object.defineProperty(obj, key, {})
+ return dep
}
const valueDep = set('value', b, {})
valueDep.addSub(() => { console.log('value changed!') })

虽然代码能使用了, 就是是看起来怪怪的~ :yum: 我们的三轮车开进了岔路~

通过watch添加订阅

喝口水我们继续

<黑客与画家>一书中曾经提到这样一个观点, 我深有体会:

如果你觉得你的代码奇怪, 那么往往它是错的

上面的那一串代码仅仅是能跑通的水平, 我们需要加入更多的细节和思考, 有时候只需要坐下来稍微看一下代码, 就会有各种想法蹦出来:

构思这种东西有一个特点,那就是它会导致更多的构思。你有没有注意过,坐下来写东西的时候,一半的构思是写作时产生的?

隐藏Dep

这些内容应和外部是解耦的. 首先一点, 我们创建一个侦听器类, 用于封装我们侦听所用到的所有方法, 它包含了我们想要的全局对象以及操作它的方法(如watch,set):

class VX {
 constructor () {
 this.store = Object.create(null)
 }
 watch (key, fn, obj = this.store) {}
 set (key, val, options = {}, obj = this.store) {}
}
const vx = new VX()

我们可以在watch中给对象某个属性添加回调, 就不用去直接操作Dep依赖数组了. 只是, 我们在业务代码中调用watch, 要怎么去获取obj.key对应的dep呢?

我们设置一个全局的depHandler, 在obj.key的getter中主动将depHandler设置为当前obj.key的dep实例, 那么我们在watch函数里, 只要用任意操作触发obj.key的getter, 就能通过depHandler得到它的dep实例了, 代码形如:

+ // 一开始没有持有dep实例
+ let handleDep = null
 class VX {
 watch (key, fn, obj = this.store) {
+  console.log(obj.key) // 使用任意操作触发obj.key的getter, 那么handleDep将自动引用obj.key的dep实例
+  handleDep.addSub(fn)
 }
 set (key, val, options = {}, obj = this.store) {
  const dep = new Dep()
  Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: () => {
+   handleDep = dep
   return val
  },
  set: newVal => {}
  })
 }
 }

主动收集依赖

我们增加 handleDep.addSub(fn) 添加回调函数的逻辑, 其实可以直接放到getter中, 首先在Dep类中封装一个'主动'收集依赖的 collect 方法, 他会将全局 handleFn 存放到订阅数组中, 这样一来, 在watch函数中, 我们只要触发obj.key的getter, 就可以主动收集依赖了:

let handleFn = null
class Dep {
 addSub (fn) {}
 delSub (fn) {}
 clear () {}
 collect (fn = handleFn) {
 if (fn && !this.subs.find(x => x === fn)) {
  this.addSub(fn)
 }
 }
 notify (newVal, oldVal) {}
}

let handleDep = null
class VX {
 watch (key, fn, obj = this.store) {
 handleFn = fn
 console.log(obj.key)
 }
 set (key, val, options = {}, obj = this.store) {
 const dep = new Dep()
 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: () => {
  handleDep = dep
  handleDep.collect()
  return val
  },
  set: newVal => {}
 })
 }
}

处理key值为对象链的情况

在先前的watch函数中, 我们使用console.log(obj.key)去触发对应属性的getter, 如果我们调用方式是 watch('a.b.c') 就无能为力了. 这里我们封装一个通用方法, 用于处理对象链字符串的形式:

// 通过将字符串'a.b'分割为['a', 'b'], 再使用一个while循环就可以走完这个对象链
function walkChains (key, obj) {
 const segments = key.split('.')
 let deepObj = obj
 while (segments.length) {
 deepObj = deepObj[segments.shift()]
 }
}
class VX {
 watch (key, fn, obj = this.store) {
 handleFn = fn
 walkChains(key, obj)
 }
}

在set方法中处理对象链字符串稍微有些不同, 因为如果 set('a.b') 时, 没有在我们全局对象中找到a属性, 这里应该抛错.

实际的处理中, 需要推断'obj.a'以及'obj.a.b'是否存在. 假设没有'obj.a', 那么我们应该创建一个新的对象, 并且给新的对象添加属性'b', 所以代码类似 walkChains 函数, 只是稍作一层判断:

set (key, val, obj) {
 const segments = key.split('.')
 // 这里需要注意, 我们只处理到倒数第二个属性
 while (segments.length > 1) {
 const handleKey = segments.shift()
 const handleVal = obj[handleKey]
 // 存在'obj.a'的情况
 if (typeof handleVal === 'object') {
  obj = handleVal
 // 不存在'obj.a'则给a属性赋一个非响应式的对象
 } else if (!handleVal) {
  obj = (
  key = handleKey,
  obj[handleKey] = {},
  obj[handleKey]
  )
 } else {
  console.trace('already has val')
 }
 }
 // 最后一个属性要手动赋值
 key = segments[0]
}

业务场景应用

小程序跨页面刷新数据

我们经常碰到在小程序中由A页面跳转到B页面, 如果B页面进行了一些操作, 希望A页面自动刷新数据的情况. 但是由于A页面跳转到B页面不同(也许是redirect,也许是navigate), 处理方法也不尽相同.

使用navigate方式跳转后, A页面不会被注销, 所以我们一般会通过全局对象去贮存A页面实例(也就是A页面的this对象), 然后在B页面直接调用相应的方法(如A.refreshList())进行刷新操作.

引入VX后, 我们可以在 onload 生命周期直接调用watch方法添加订阅:

// app.js
import VX from '@/utils/suites/vx'
const vx = new VX()
app.vx = vx
app.store = vx.store
app.vx.set('userType', '商户')

// page a
onLoad () {
 app.vx.watch('userType', userType => {
 if (userType === '商户') {
  // ...
 } else if (userType === '管理员') {
  // ...
 }
 }, {
 immediate: true
 })
}

// page b
switchUserType () {
 app.store.userType = '管理员'
}

可能遇到的问题

给watch方法添加的函数设置立即执行

有的时候我们希望通过watch添加函数的同时还立即执行该函数一次, 这个时候我们需要再定义额外的参数传递到watch中. 问题是这个函数不一定是同步函数.

简单处理如下:

class VX {
 async watch (key, fn, options = { immediately: false }, obj = this.store) {
 handleDep = fn
 walkChains(key, obj)
 options.immediately && await fn(options.defaultParams)
 }
}

this绑定丢失问题

在我在对VX进行删除属性方法的扩展时, 我往walkChain函数中添加了一个执行回调函数的机制, 并且在删除属性这个方法直接调用了walkChain:

+ function walkChains (key, obj, fn) {
 const segments = key.split('.')
 let deepObj = obj
 while (segments.length) {
  deepObj = deepObj[segments.shift()]
+  fn && fn()
 }
 }
del (key, obj = this.store) {
 walkChains(key, obj, handleDep.clear)
 delete obj[key]
}

因为handleDep.clear当成参数传递进walkChains中会 丢失this绑定 , 所以上面那段代码其实是有问题的, 不过稍作修改就好了:

del (key, obj = this.store) {
+ walkChains(key, obj, () => handleDep.clear())
 delete obj[key]
 }

关于“如何实现微信小程序中的数据侦听”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

--结束END--

本文标题: 如何实现微信小程序中的数据侦听

本文链接: https://www.lsjlt.com/news/76341.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何实现微信小程序中的数据侦听
    这篇文章将为大家详细讲解有关如何实现微信小程序中的数据侦听,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在小程序项目中, 我们的通常会使用到使用到一个全局对象作为各个页面...
    99+
    2024-04-02
  • 微信小程序如何实现数据遍历
    小编给大家分享一下微信小程序如何实现数据遍历,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!制作数据遍历的步骤在index.js中...
    99+
    2024-04-02
  • 微信小程序如何停止监听罗盘数据
    本文将为大家详细介绍“微信小程序如何停止监听罗盘数据”,内容步骤清晰详细,细节处理妥当,而小编每天都会更新不同的知识点,希望这篇“微信小程序如何停止监听罗盘数据”能够给你意想不到的收获,请大家跟着小编的思路慢慢深入,具体内容如下,一起去收获...
    99+
    2023-06-26
  • 微信小程序如何开始监听罗盘数据
    这篇“微信小程序如何开始监听罗盘数据”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“微信小程序如何开始监听罗盘数据”,小编整理了以下知识点,请大家跟着小编的步伐一步一步的慢慢理解,接下来就让我们...
    99+
    2023-06-26
  • Vue中如何实现对Array的数据侦听
    这篇文章给大家分享的是有关Vue中如何实现对Array的数据侦听的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。核心思想通过创建一个拦截器来覆盖数组本身的原型对象Array.pro...
    99+
    2024-04-02
  • 微信小程序中如何实现微信支付
    微信支付是微信公众平台提供的一种在线支付服务,可以为用户提供快速、方便、安全的支付体验。而在微信小程序中实现微信支付,则可以为应用程序提供更多的功能和服务,提高用户体验和商业价值。因此,在本文中,我们将介绍如何在微信小程序中实现微信支付。 ...
    99+
    2023-08-16
    微信小程序 微信 小程序 前端 javascript
  • 微信小程序如何实现用户数据解密
    小编给大家分享一下微信小程序如何实现用户数据解密,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!微信小程序 用户数据解密官方指引图...
    99+
    2024-04-02
  • 微信小程序如何实现本地数据存储
    这篇文章给大家分享的是有关微信小程序如何实现本地数据存储的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。微信小程序 本地数据存储实例详解前言如果您在看此文章之前有过其他程序的开发经...
    99+
    2024-04-02
  • 微信小程序实现监听页面滚动
    本文实例为大家分享了微信小程序实现监听页面滚动的具体代码,供大家参考,具体内容如下 1.正常状态下 2.页面滚动到大于100时显示定位到顶部的view盒子 3.html部分 &l...
    99+
    2024-04-02
  • 微信小程序数据监听器使用实例详解
    目录一、数据监听器1.1 什么是数据监听器1.2 数据监听器的基本用法1.3 监听对象属性的变化二、数据监听器2.1 监听对象中指定属性的变化2.2 监听对象中所有属性的变化三、纯数...
    99+
    2023-05-17
    小程序数据监听器 微信小程序数据监听
  • 微信小程序如何实现tabBar
    这篇文章主要介绍了微信小程序如何实现tabBar,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。具体如下:1、效果展示2、原理:在app.js...
    99+
    2024-04-02
  • 微信小程序如何实现slider
    这篇文章给大家分享的是有关微信小程序如何实现slider的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。实现效果图:滑动选择器属性名类型默认值说明minNumber0最小值maxN...
    99+
    2024-04-02
  • 微信小程序中的数据存储实现方式
    目录全局变量globalData 页面私有变量 datastorage异步存储(根据设备性能的不同,你还真不知道他会存多久) 同步存储(会造成阻塞~) 文件存储 fileSysteM...
    99+
    2024-04-02
  • 微信小程序如何实现数据交互与渲染
    小编给大家分享一下微信小程序如何实现数据交互与渲染,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!微信小程序 数据交互与渲染实现效...
    99+
    2024-04-02
  • PHP开发微信小程序:如何实现数据分析?
    PHP开发微信小程序:如何实现数据分析?随着微信小程序的普及和应用范围的扩大,对于数据分析的需求也越来越大。在开发微信小程序的过程中,数据分析是非常重要的一环。通过数据分析,我们可以了解用户的行为习惯、用户群体特征,从而进行针对性的优化和改...
    99+
    2023-10-26
    数据分析 PHP 微信小程序
  • 微信小程序中如何实现本地数据缓存功能
    小编给大家分享一下微信小程序中如何实现本地数据缓存功能,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!具体如下:前面介绍了微信小程序获取位置信息操作。这里再来介绍一下微信小程序的本地数据缓存...
    99+
    2024-04-02
  • 微信小程序的Socket.io client如何实现
    这篇“微信小程序的Socket.io client如何实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“微信小程序的Sock...
    99+
    2023-06-26
  • 微信小程序如何监听全局变量
    最近工作上遇到一个问题,有个全局变量 red_heart,因为它在很多地方用到,当它发生改变了,用到的地方也要改变。但是原生小程序并没有像Vue这种相关的做法。所以我就想自己实现一个...
    99+
    2024-04-02
  • 微信小程序中如何实现轮播图
    这篇文章主要介绍了微信小程序中如何实现轮播图,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。业务需求:5个图片轮番播放,可以左右滑动,点击指示...
    99+
    2024-04-02
  • 微信小程序中如何实现Redux绑定
    这篇文章给大家分享的是有关微信小程序中如何实现Redux绑定的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。微信小程序Redux绑定实例安装clone或者下载代码库到本地:git&...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作