Observe 数据监听
简介
为了减少手动更新的频率,系统框架都需要使用代理监听数组或对象的变化而修改DOM,ES6推出了Proxy方法打开了数据监听思路,目前Proxy是当前主流的监听方法。AXUI中的axObserve
监听函数可实现数据监听,支持劫持和深度监听。
基本用法
该函数完整写法为axObserve(obj, options),参数说明如下
obj
:Array数组或Object对象options
:监听参数,支持多个参数:- deep:是否为深度监听,也就是每个数组和对象都监听,默认为false只监听第一层数组或对象
- accept:监听到数据变化是否直接给原数组或对象赋值,默认true,可选择false,为false则需要在回调里手动赋值,例如Reflect.set(target, key, value)
- once:是否为只监听一次complete回调,默认为false每次数据操作都执行一次complete回调,可使用true,以此避免反复操作回调
- methods:代理使用的监听方法,是数组,一共有三个值get/set/deleteProperty,默认['set','deleteProperty']即只监听设置和删除两个行为不监听get行为,如果是深度监听而且数据较大建议取消get监听,以提高运行效率
- callback:回调函数,支持一个参数,该参数是一个对象包含的key如下:(注意不同监听类型数据结构可能会不一样)
- target:代理的原数组或对象
- key:监听到的属性
- value:将赋予的新值
- raw:原旧值
- type:监听类型,包含add、edit、delete、get和complete五种
- proxy:当前代理对象
插件实例最终可返回{proxy:'',instance:''}:
- new axObserve(...).proxy:返回原数据的代理对象,也就是对该proxy操作才会触发监听
- new axObserve(...).instance:返回监听实例,可以使用实例方法on
add类型监听
对数组增加新项或者对对象增加新的键值会触发add
类型监听。
例如obj.x = 1,会触发一次add类型监听和一次complete类型监听。
-
<a href="###" class="ax-btn" id="btnAdd">试一试</a>
-
let dataAdd = { a: 1, b: 2, c: 3 }, proxyAdd = new axObserve(dataAdd, { callback: (data) => { console.log(data.type + '监听:', data); //complete类型中没有data.key和data.value } }), btnAdd = document.querySelector('#btnAdd'); btnAdd.onclick = () => { proxyAdd.proxy.d = 100; }
edit类型监听
对数组和对象的已有key进行改写会触发edit
类型监听,但是改写的value与原value一致的话不会触发edit类型监听。
例如arr[2] = 1,会触发一次edit类型监听和一次complete类型监听。
-
<a href="###" class="ax-btn" id="btnEdit">试一试</a>
-
let dataEdit = [9, 6, 8, 1, 3, 5], proxyEdit = new axObserve(dataEdit, { callback: (data) => { console.log(data.type + '监听:', data); //complete类型中没有data.key和data.value } }), btnEdit = document.querySelector('#btnEdit'); btnEdit.onclick = () => { proxyEdit.proxy[2] = 100; }
数组length监听
有一类特别的edit类型监听即对数组length
变化监听。对数组的pop()、shift()、unshift()、push()、splice()操作相当于对数组进行剪切,这几个数组方法都会改变数组的length。
比如pop()方法会触发三次get监听,一次delete监听,一次edit监听和一次complete监听。
-
<a href="###" class="ax-btn" id="btnLength">试一试</a>
-
let dataLength = [9, 6, 8, 1, 3, 5], proxyLength = new axObserve(dataLength, { callback: (data) => { console.log(data.type + '监听:', data); //get和delete类型中没有data.value //complete类型中没有data.key和data.value } }), btnLength = document.querySelector('#btnLength'); btnLength.onclick = () => { proxyLength.proxy.pop(); }
delete类型监听
删除数组项或删除对象的key会触发delete
类型监听。
例如delete obj.a,会触发一次delete类型监听和一次complete类型监听。
-
<a href="###" class="ax-btn" id="btnDelete">试一试</a>
-
let dataDelete = { a: 1, b: 2, c: 3 }, proxyDelete = new axObserve(dataDelete, { callback: (data) => { console.log(data.type + '监听:',data); //delete类型中没有data.proxy } }), btnDelete = document.querySelector('#btnDelete'); btnDelete.onclick = () => { delete proxyDelete.proxy.a; }
get类型监听
读取数组下标或者对象key时会触发get
类型监听。
-
<a href="###" class="ax-btn" id="btnGet">试一试</a>
-
let dataGet = { a: 1, b: 2, c: 3 }, proxyGet = new axObserve(dataGet, { methods:['get'], callback: (data) => { console.log(data.type + '监听:', data); //get类型中没有data.value } }), btnGet = document.querySelector('#btnGet'); btnGet.onclick = () => { proxyGet.proxy.a; }
complete类型监听
add、edit和delete之后最终都会触发complete
类型监听,complete类型监听在频繁修改数据的时候很有用。频繁修改数据当中会产生很多的监听,但是只有数据全部处理完毕才有必要进行下一步业务。多次数据同步操作最终只会产生一次complete类型监听。
-
<a href="###" class="ax-btn" id="btnComplete">试一试</a>
-
let dataComplete = [9, 6, 8, 1, 3, 5], proxyComplete = new axObserve(dataComplete, { callback: (data) => { console.log(data.type + '监听:', data); } }), btnComplete = document.querySelector('#btnComplete'); btnComplete.onclick = () => { proxyComplete.proxy.unshift(100); proxyComplete.proxy.push(200); proxyComplete.proxy[2] = 300; proxyComplete.proxy.pop(); }
深度监听
本插件默认只监听数组或对象的第一层,如果需要监听子数组或子对象,需要对options开启deep:true
。如果子数组length未知或者子对象的keys未知,会带来性能开销,不建议开启。
-
<a href="###" class="ax-btn" id="btnComplete">试一试</a>
-
let dataDeep = { fruit: {a: 1, b: 2, c: 3}, food: { x: 1, y: 2, z: 3 } }, proxyDeep = new axObserve(dataDeep, { deep: true, callback: (data) => { console.log(data.type + '监听:', data); } }), btnDeep = document.querySelector('#btnDeep'); btnDeep.onclick = () => { proxyDeep.proxy.food.x = 100; proxyDeep.proxy.fruit.d = { apple: 1, banana: 2 }; proxyDeep.proxy.fruit.d.apple = 200; }
once监听
创建一个Proxy对象,每次同步或异步任务下(如反复点击按钮)都会产生一次complete监听;但是有时候我们需要在Proxy在产生一次complete监听之后就不需要在异步任务中(如反复点击按钮)产生新的complete监听,此时需要设置onece:true
。
-
<a href="###" class="ax-btn" id="btnOnce">试一试</a>
-
let dataOnce = [9, 6, 8, 1, 3, 5], proxyOnce = new axObserve(dataOnce, { once: true, callback: (data) => { console.log(data.type + '监听:', data); } }), btnOnce = document.querySelector('#btnOnce'); btnOnce.onclick = () => { proxyOnce.proxy.push(200); }
数据劫持和手动赋值
当修改值时会比对旧值,如果不一致将自动用新值替代旧值,如果需要劫持数据并手动赋值,需要对options设置accept:false
,如果需要赋值需要在回调里使用Reflect.set方法
。
-
<a href="###" class="ax-btn" id="btnAccept">试一试</a>
-
let dataAccept = { a: 1, b: 2, c: 3 }, proxyAccept = new axObserve(dataAccept, { accept: false, callback: (data) => { if (key === 'a') { Reflect.set(data.target, key, data.value); } if (key === 'c') { Reflect.deleteProperty(data.target, key); } if (key === 'complete') { console.log('最终值:', data.target); } } }), btnAccept = document.querySelector('#btnAccept'); btnAccept.onclick = () => { proxyAccept.proxy.a = 100; proxyAccept.proxy.b = 200; delete proxyAccept.proxy.c; }
on语法
callback语法需要与new实例时同时使用,而on
语法可与new实例分步进行。其基本语法为let ob=new axObserve(...);ob.instance.on(key,callback)。key为监听到的属性,callback的参数与options.callback参数相同。
-
<a href="###" class="ax-btn" id="btnAccept">试一试</a>
-
let dataOn = { a: 1, b: 2, c: 3 }, proxyOn = new axObserve(dataOn), btnOn = document.querySelector('#btnOn'); proxyOn.instance.on('a',(data)=>{ console.log(data); }); proxyOn.instance.on('complete',(data)=>{ console.log(data); }); btnOn.onclick = () => { proxyOn.proxy.a = 100; proxyOn.proxy.b = 200; delete proxyOn.proxy.a; }