Vue 提供了一种通用的方式来观察和响应 Vue 实例上的数据变动:监听属性 watch。

虽然watch的滥用会导致性能不佳,但在一些情况下我们还是需要watch,使得代码更加简洁、逻辑更加清晰(其实就是嫌麻烦...)。

接下来我将逐步讲解微信小程序中如何实现一个监听器 watch,若想直接看最终代码,可直接滑动至底部。

监听器的原理,将data中需监听的属性写在watch对象中,并给其提供一个方法,当被监听属性的值改变时,调用该方法。​​

所以很显然,我们需要用到Javascript中的Object.defineProperty()方法,来手动劫持对象的getter/setter,从而实现给对象赋值时(调用setter),执行watch对象中相对应的函数,达到监听效果。Object.defineProperty()不在这里详细介绍,还不会使用的童鞋速戳这里-> Object.defineProperty()介绍。

首先,既然是微信小程序自定义watch属性,我建议直接将代码写在app.js内,需要使用的页面直接在onLoad()内调用getApp().setWatch(...)即可

// app.js

/**
* 小程序初始化
*/
onLaunch(){...} /**
* 设置监听器
*/
setWatcher(data, watch) { // 接收index.js传过来的data对象和watch对象
Object.keys(watch).forEach(v => { // 将watch对象内的key遍历
this.observe(data, v,watch[v]); // 监听data内的v属性,传入watch内对应函数以调用
})
}, /**
* 监听属性 并执行监听函数
*/
observe(obj, key,watchFun) {
var val = obj[key]; // 给该属性设默认值
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
set: function(value) {
val = value;
watchFun(value,val); // 赋值(set)时,调用对应函数
},
get: function() {
return val;
}
})
}
此时在index.js中已经可以实现简单的属性监听: //index.js
Page({
data: {
name:"xuyang"
},
onLoad(){
getApp().setWatcher(this.data, this.watch); // 设置监听器
this.setData({
name:'lxm'
})
},
watch:{
name:function(newValue){
console.log(newValue); // name改变时,调用该方法输出新值。
}
}
})

我们可以看到,index页面启动后,name被赋值成'lxm',同时触发其setter,调用watch内的name()方法,控制台打印出 lxm。

是不是挺简单的呢?掌握了这一点,接下来的事情就好办多了。

我们知道,Vue中 watch对象内不光是能写name、age这类属性,还能用引号写出'my.name'、'my.age'这类属性,即对my对象下的name、age进行监听。 我们只需要改进一下setWatcher方法:

/**
* 设置监听器
*/
setWatcher(data, watch) {
Object.keys(watch).forEach(v => {
let key = v.split('.'); // 将watch中的属性以'.'切分成数组
let nowData = data; // 将data赋值给nowData
for (let i = 0; i < key.length - 1; i++) { // 遍历key数组的元素,除了最后一个!
nowData = nowData[key[i]]; // 将nowData指向它的key属性对象
}
let lastKey = key[key.length-1];
// 假设key==='my.name',此时nowData===data['my']===data.my,lastKey==='name'
this.observe(nowData, lastKey,watch[v]); // 监听nowData对象的lastKey
})
}
此时在index.js中可以这样写: //index.js Page({
data: {
my:{
name:"xuyang",
age:21
}
},
onLoad(){
getApp().setWatcher(this.data, this.watch);
this.data.my.name = 'lxm';
this.data.my.age = 2;
this.setData({
my: this.data.my
})
},
watch:{
'my.name':function(newValue){
console.log(newValue);
},
'my.age':function(newValue){
console.log(newValue);
}
}
})

控制台会打印出 lxm 和 2

现在我们又离成功更进了一步~ 接下来我们要实现Vue中的深度监听,即监听my对象即可监听它的内部所有属性以及属性的属性以及属性的属性的属性......并且监听函数的this要指向这个page里的this。

需要深度监听,Vue中的写法是

watch:{
my:{
handler(newValue){
// do something...
},
deep:true // 深度监听
}
}
我们需要修改一下app.js中的两个函数: /**
* 设置监听器
*/
setWatcher(page) {
let data = page.data;
let watch = page.watch;
Object.keys(watch).forEach(v => {
let key = v.split('.'); // 将watch中的属性以'.'切分成数组
let nowData = data; // 将data赋值给nowData
for (let i = 0; i < key.length - 1; i++) { // 遍历key数组的元素,除了最后一个!
nowData = nowData[key[i]]; // 将nowData指向它的key属性对象
}
let lastKey = key[key.length - 1];
// 假设key==='my.name',此时nowData===data['my']===data.my,lastKey==='name'
let watchFun = watch[v].handler || watch[v]; // 兼容带handler和不带handler的两种写法
let deep = watch[v].deep; // 若未设置deep,则为undefine
this.observe(nowData, lastKey, watchFun, deep, page); // 监听nowData对象的lastKey
})
},
/**
* 监听属性 并执行监听函数
*/
observe(obj, key, watchFun, deep, page) {
var val = obj[key];
// 判断deep是true 且 val不能为空 且 typeof val==='object'(数组内数值变化也需要深度监听)
if (deep && val != null && typeof val === 'object') {
Object.keys(val).forEach(childKey=>{ // 遍历val对象下的每一个key
this.observe(val,childKey,watchFun,deep,page); // 递归调用监听函数
})
}
var that = this;
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
set: function(value) {
// 用page对象调用,改变函数内this指向,以便this.data访问data内的属性值
watchFun.call(page,value,val); // value是新值,val是旧值
val = value;
if(deep){ // 若是深度监听,重新监听该对象,以便监听其属性。
that.observe(obj, key, watchFun, deep, page);
}
},
get: function() {
return val;
}
})
}
其中需要特别注意,若是data中先声明一个对象userInfo = {},再给userInfo赋值一个对象(比如从服务器获取用户数据),则需要重新遍历监听才能实现深度监听,故需加上这段代码: if(deep){ // 若是深度监听,重新监听该对象,以便监听其属性。
that.observe(obj, key, watchFun, deep);
} 此时index.js中需要深度监听的属性,可以这样写: //index.js Page({
data: {
my: {
name: 'xuyang',
age: 21,
hobby: ['girls', 'games']
},
nameInfo:{}
},
onLoad() {
getApp().setWatcher(this);
this.data.my.hobby[0] = 'study';
this.setData({
nameInfo:{name:'haha',sex:'boy'}
})
console.log(this.data)
},
watch: {
my:{
handler(newValue) {
console.log(newValue);
},
deep:true
}
}
})

微信小程序实现watch属性监听数据变化的更多相关文章

  1. 关于微信小程序使用watch监听数据变化的方法

    众所周知,Vue中,可以使用监听属性 watch来观察和响应 Vue 实例上的数据变化,那么小程序能不能实现这一点呢? 监听器的原理,是将data中需监听的数据写在watch对象中,并给其提供一个方法 ...

  2. 微信小程序结合后台数据管理实现商品数据的动态展示、维护

    微信小程序给我们提供了一个很好的开发平台,可以用于展现各种数据和实现丰富的功能,本篇随笔介绍微信小程序结合后台数据管理实现商品数据的动态展示.维护,介绍如何实现商品数据在后台管理系统中的维护管理,并通 ...

  3. 微信小程序通过api接口将json数据展现到小程序示例

    这篇文章主要介绍了微信小程序通过api接口将json数据展现到小程序示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧实现知乎客户端的一个重要知识前提就是,要知道怎么通过 ...

  4. 微信小程序wx.request请求服务器json数据并渲染到页面

    [原文出自]: https://blog.csdn.net/weixin_39927850/article/details/79766259 微信小程序的数据总不能写死吧,肯定是要结合数据库来做数据更 ...

  5. ESP32 BLE蓝牙 微信小程序通信发送大于20字符数据

    由于微信小程序只支持BLE每次发送数据不大于20个字节,ESP32则有经典蓝牙.低功耗蓝牙两种模式. 要解决发送数据大于20个字节的问题,最简单实用的方式就是分包发送.如下图所示: 1.什么起始字符和 ...

  6. vue.js之过滤器,自定义指令,自定义键盘信息以及监听数据变化

    一.监听数据变化 1.监听数据变化有两种,深度和浅度,形式如下: vm.$watch(name,fnCb); //浅度 vm.$watch(name,fnCb,{deep:true}); //深度监视 ...

  7. $scope.$watch()——监听数据变化

    $scope.$watch(watchFn, watchAction, [deepWatch]):监听数据变化,三个参数 --watchFn:监听的对象,一个带有Angular 表达式或者函数的字符串 ...

  8. vue教程2-08 自定义键盘信息、监听数据变化vm.$watch

    vue教程2-08 自定义键盘信息 @keydown.up @keydown.enter @keydown.a/b/c.... 自定义键盘信息: Vue.directive('on').keyCode ...

  9. 微信小程序关于tabbar点击切换数据不刷新问题

    微信小程序中经常遇到的需求就是我提交了一个表单或者进行了一个操作,需要在我的个人中心页面中实时显示出来,但是小程序中的tabbar切换类似于tab切换 并不会进行页面刷新请求 所以总是会造成一些数据更 ...

随机推荐

  1. 微软牛津计划——声纹识别与视频识别API上线啦!

    上个月,我们发布了牛津计划机器学习的情感识别API,能够帮助不同平台的开发者轻松添加智能应用,而无需精通人工智能领域.牛津计划仅仅是微软在人工智能领域探索中的一个实例,而我们的期望是实现更加注重个人使 ...

  2. 如何申请免费域名证书,以及在IIS上绑定

    1.前往https://freessl.cn/ 申请域名 证书 2.输入邮箱后,点击创建.文件验证方式 会自动下载一个压缩包,把该压缩包放到所申请的域名首层下.(不要重复创建,需要3,5分钟.最好手动 ...

  3. QTimer掉坑出坑过程

    最近遇到一个问题,就是关于QTimer设置了10ms,结果不生效,很头疼啊,查了快一天了,终于知道为什么了? 先说下QTimer的使用方法: m_delayHideTimer这是QTimer的对象. ...

  4. Struts2学习-json

    Struts2 实现JSON输出有2种办法1.把Action变成Servlet,使用传统做法2.使用Struts 内置功能完成 二. 1.导入配置,pom.xml,要去struts2的官网下载mvnr ...

  5. 如何使用Adobe Reader复制PDF文档上的文字

    PDF文档大家常用,但是有没有简单的方法能够提取PDF文档上的文字,然后使用呢?除了将PDF转换成Word,这里介绍一种更为简单实用的方法复制PDF文本文字,Adobe Reader是大家都常用的PD ...

  6. 设计模式:Builder模式

    设计模式:Builder模式 一.前言    今天我们讨论一下Builder建造者模式,这个Builder,其实和模板模式非常的像,但是也有区别,那就是在模板模式中父类对子类中的实现进行操作,在父类之 ...

  7. 查看Oracle表中的指定记录在数据文件中的位置

    查看Oracle表中的指定记录位置select rowid,user_id from sshr.xx_user where user_id=3010586 select rowid,       db ...

  8. SAP Cloud for Customer里Sales Order和Sales Quote的建模方式

    SAP Cloud for Customer的Sales工作中心里有Sales Quote和Sales Order两个视图,一个用于销售报价单,另一个用于销售订单. 流程上是先有报价单 ,报价单是一份 ...

  9. c++中左值的含义

    <<cpp primer plus 6th edition>>中的原文(Chapter 8 Adventures in Functions): What is an lvalu ...

  10. VB.NET & DataGridView与数据库的连接

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/huo065000/article/details/37378313      加入删除用户,这就又要 ...