微信小程序实现watch监听,无需页面引入!!!
目录1监听函数实现2.页面Page重写(实现无需引入)3完整代码实现4微信小程序代码片段大家都知道小程序其实和Vue的写法以及原理都存在很大的相同,但是里有watch监听可以监听data定义的数据,而小程序里并没有(其实小程序并不需要哈哈,大家可以想想。但是作为程序员总想凭什么没有!)。**其实监听器的原理,就是将data中需监听的属性写在watch对象中,并给其提供一个方法,当被监听属性的值..
·
大家都知道小程序其实和Vue的写法以及原理都存在很大的相同,但是里有watch监听可以监听data定义的数据,而小程序里并没有(其实小程序并不需要哈哈,大家可以想想。但是作为程序员总想凭什么没有!)。
**其实监听器的原理,就是将data中需监听的属性写在watch对象中,并给其提供一个方法,当被监听属性的值改变时,调用该方法。
所以很显然,我们需要用到Javascript中的Object.defineProperty()方法,来手动劫持对象的getter/setter 从而实现给对象赋值时(调用setter),执行watch对象中相对应的函数,达到监听效果。Object.defineProperty()不在这里详细介绍,还不会使用的童鞋速戳这里-> Object.defineProperty()介绍。Object.defineProperty()
1监听函数实现
function 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
observe(nowData, lastKey, watchFun, deep, page); // 监听nowData对象的lastKey
})
};
function 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
observe(val, childKey, watchFun, deep, page); // 递归调用监听函数
})
}
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;
},
get: function () {
return val;
}
})
};
2.页面Page重写(实现无需引入)
// 保存原生构造函数
const OldPage = Page;
Page = function (conf) {
// 这里的conf就是Page函数的内容{data, onLoad, onShow, ...}
// 重写onLoad方法,用一个变量保存旧的onLoad函数
let oldonLoad = conf.onLoad; // 同理
conf.onLoad = function() {
//执行onLoad的默认事件 do something
// *********************************************
// 此处不能写成oldonLoad(),否则没有this,this.setData等方法为undefined。这里的this在Page构造函数实例化的时候才会指定
// 在Page构造函数实例化的时候,小程序会将当前的Page对象的原型链(__proto__)增加很多方法,例如setData。当前的obj没有setData
oldonLoad.call(this)
}
return OldPage(conf); // 将原生的构造函数return出去保持页面函数正常执行
};
3完整代码实现
1.在utils文件夹创建Page.js
// Page.js
// 保存原生构造函数
const OldPage = Page;
function 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
observe(nowData, lastKey, watchFun, deep, page); // 监听nowData对象的lastKey
})
};
function 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
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;
},
get: function () {
return val;
}
})
};
// 还原上个页面的参数到 options, 并删除 options.params
const extractParams = function (query = {}) {
const { params } = query
let options = { ...query }
if (params !== undefined) {
options = {
...options,
...JSON.parse(decodeURIComponent(params)),
}
delete options.params
}
return options
}
Page = function (conf) {
if (conf.watch) {
// 设置了watch监听
let oldonLoad = conf.onLoad;
conf.onLoad = function(options = {}) {
setWatcher(this)
oldonLoad && oldonLoad.call(this, extractParams(options))
}
} else {
}
return OldPage(conf);
};
module.exports = {
Page
}
// 在app.js头部引入即可
let Page = require('./utils/page.js').Page
// 在index.js使用
Page({
data: {
watchData: "",
my: {
name: 'yeshen',
age: 18,
hobby: ['girls', 'games'],
info: {
tag: "年轻的少年",
like: {
food: "鱼"
}
}
},
},
watch: {
'my': {
handler(newValue, oldValue) {
console.log(newValue, oldValue, '-----监听到数据变化')
},
deep: true
}
},
onLoad: function () {
this.setData({
'my.age': "20"
})
setTimeout(() => {
this.data.my.info.like.food = "肉肉"
}, 1000)
},
})
4微信小程序代码片段
更多推荐
已为社区贡献1条内容
所有评论(0)