当前位置:网站首页>微信小程序实现数据侦听器watch,包含销毁watch和子属性的watch
微信小程序实现数据侦听器watch,包含销毁watch和子属性的watch
2022-06-29 08:55:00 【milugloomy】
原因
在上一篇《微信小程序实现数据侦听器,类似vue的watch,替代Observer》中,我们实现的watch功能,但使用起来还是有点麻烦。能不能像Vue那样直接写个watch属性呢?当然可以,这就是写这一片博客的目的。我们要实现以下几个目标:
- 支持侦听对象的变化,侦听对象的子属性的变化,支持立即执行
- 少量几行代码即可引入,不修改之前代码结构,不需要每个页面都引入
- 使用方式和vue的watch一样
- 页面销毁时可以注销watch,减少开销
在组件和Page中实现
微信小程序实现watch功能,有如下四个关键点:
- 侦听子属性变化时,watch的key类似于'a.b.c.d',需要解析这个key,在下面的parseKey函数中实现。
- watch功能要在组件的attached生命周期和page的onLoad生命周期中实现。一个原因是要获取this,另一个原因是在Component初始化时,有data和properties两个可以定义数据的地方。而在created生命周期中,会将properties中的属性复制到data中。所以我们在created的下一阶段,attached只用侦听data中的属性变化,而不用侦听data和properties两个对象的属性变化,减少实现复杂度。
- watch的原理是使用Object.defineProperty的set方法来侦听值的变化。
- 销毁watch的方案,可以用临时变量先保存这个属性的值,删除这个属性,再重新赋值。
新建一个watch.js文件,内容如下:
function initComponent() {
let oldComponent = Component
Component = function(obj) {
let oldAttached = obj.attached || function() {}
let oldDetached = obj.detached || function() {}
// 在attached中初始化watch
obj.attached = function() {
obj.watch && initWatch.call(this, obj.watch)
oldAttached.call(this, ...arguments)
}
// 在detached中注销watch
obj.detached = function() {
obj.watch && unWatch.call(this, obj.watch)
oldDetached.call(this, ...arguments)
}
return oldComponent(obj)
}
}
function initPage() {
let oldPage = Page
Page = function(obj) {
let oldOnLoad = obj.onLoad || function() {}
let oldOnUnload = obj.onUnload || function() {}
// 在onLoad中初始化watch
obj.onLoad = function() {
obj.watch && initWatch.call(this, obj.watch)
oldOnLoad.call(this, ...arguments)
}
// 在onUnload中注销watch
obj.onUnload = function() {
obj.watch && unWatch.call(this, obj.watch)
oldOnUnload.call(this, ...arguments)
}
return oldPage(obj)
}
}
function initWatch(watch) {
let that = this
// 如果obj有watch这个属性,遍历watch对象
for (let key in watch) {
//临时对象,tmpObj由三部分组成,obj,key,value;obj={key:value}
let tmpObj = parsePath.call(this, key)
// 如果watch的key在data或者properties中有定义,就监控它的变化
if (tmpObj && tmpObj.key) {
// 暂存数据,用于get
let tmpData = tmpObj.value
// watch的回调
let cb = watch[key]
let immediate = false
if (typeof cb === 'object') { // watch: {xxx: {immediate: true, handler: function(){}}}
immediate = cb.immediate
cb = cb.handler
}
// 如果immediate为true,先执行一次侦听变化的方法
if (immediate) {
cb.call(that, tmpData)
}
// 配置watch
Object.defineProperty(tmpObj.obj, tmpObj.key, {
set: function(newVal) {
//保存旧值,供watch侦听器用
let oldVal = tmpData
//修改当前值,也就是this.data.xxx的值改变,会调用下面的get方法即时取到
tmpData = newVal
// 数据变化时,再执行一次方法
if (oldVal !== newVal) {
cb.call(that, newVal, oldVal)
}
},
get: function() {
return tmpData
}
})
}
}
}
function unWatch(watch) {
for (let key in watch) {
let tmpObj = parsePath.call(this, key)
// 删除这个属性,再重新赋值,则可以注销watch
if (tmpObj && tmpObj.key) {
let tmp = tmpObj.obj[tmpObj.key]
delete tmpObj.obj[tmpObj.key]
tmpObj.obj[tmpObj.key] = tmp
}
}
}
// 解析watch的key,可以解析对象的子属性'user.address.city'这样
function parsePath(key) {
let parent = that.data
let obj = that.data
let segments = key.split('.')
for (var i = 0; i < segments.length; i++) {
if (obj.hasOwnProperty(segments[i]) === false) {
return null
}
parent = obj
obj = obj[segments[i]]
}
return {
obj: parent, //defineProperty的第一个参数
key: segments[i - 1], //defineProperty的第二个参数
value: obj //值
}
}以上,主要关键代码都已经添加注释。
引入
一句话引入,在app.js中引入watch.js
import './utils/watch.js'使用方式
使用方式和Vue一样,三种watch使用方式的示例代码如下:
Page({
data: {
patient: null,
patientList: null
},
onLoad: function(options) {
app.formRequest('/chronic/selectPatientList').then(res => {
this.setData({
patientList: res.body.list
})
})
},
watch:{
//watch对象
patientList: function(val) {
console.log('patientList变化了')
console.log(val)
},
//watch对象,立即执行
patient: {
immediate: true,
handler: function(val){
console.log('patient变化了')
console.log(val)
}
},
//watch对象的子属性
'patient.patientId': function(val) {
console.log('patientId变化了')
console.log(val)
},
}
})总结
这篇,我们在小程序的Page和Component中实现了watch功能,且实现了开头的四个目标。
边栏推荐
- Student增删gaih
- UE4 plug-in reports an error cannot open include file: 'modulemanager H 'resolve
- How to do unit test well
- Data visualization: the significance of data visualization
- The difference between cokkie and session
- 基于keil5自动配置stm32f103标准库的官网freertos移植
- Five heart charity matchmaker team
- Data governance: data standard management (Part III)
- 数据可视化:数据可视化的意义
- 【NOI模拟赛】为NOI加点料(重链剖分,线段树)
猜你喜欢

UE4 compile a single file (VS and editor start respectively)

Print service IP setting scheme

基于stm32标准库独立按键的多按键状态机的实现

Easyexcl export 1million lines of EXECL report font error solution

GD32F4xx 以太網芯片(enc28j60)驅動移植

SPI drive of lsm6dsl

Find the most repeated element in the string

CROSSFORMER: A VERSATILE VISION TRANSFORMER BASED ON CROSS-SCALE ATTENTION

Pytorch summary learning series - operation

1424. 对角线遍历 II
随机推荐
Basic operations of MAC MySQL database
Automatic 3D Detection and Segmentation of Head and Neck Cancer from MRI Data.
watch监听和computed计算属性的使用和区别
Iso16000-9 testing of volatile organic compounds in building products and furniture
Factory mode
用户级线程和内核级线程
Print service IP setting scheme
Pytorch Summary - Automatic gradient
1424. 对角线遍历 II
UE4 display 3D editable points in Viewport
LeetCode刷题——泰波那契数列
数据治理:数据治理在数据中台下的解决方案
Chapter 12 signals (II) - examples of producers and consumers
CROSSFORMER: A VERSATILE VISION TRANSFORMER BASED ON CROSS-SCALE ATTENTION
Data warehouse: layered architecture of Finance / banking
你知道BFD是什么吗?一文详解BFD协议原理及使用场景
linux下centos7中mysql5.7安装教程
Mysql配置主从数据库
A 2.5D Cancer Segmentation for MRI Images Based on U-Net
Find the most repeated element in the string