vue中监听数据变化,vue监测数组变化
这就涉及到Vue的一个重要特征:响应式系统。数据模型只是一个普通的JavaScript对象。当我们修改它时,视图将被更新,而变化检测是响应式系统的核心。
目录
一、对象的变化检测二。关于对象三的问题。阵列3.1的变化检测。背景3.2。实施四。关于阵列的问题
一、Object的变化侦测
让我们模拟一下检测数据变化的逻辑。
强调我们要做的事情:当数据发生变化时通知外界(外界会自己做一些逻辑处理,比如重新渲染视图)。
在我们开始编码之前,我们必须回答以下问题:
1.如何检测物体的变化?
请使用Object.defineProperty()。读取数据时会触发Getter,修改数据时会触发setter。
只有当你能察觉到对象的变化时,你才能在数据发生变化时发出通知。
2.当数据改变时,我们通知谁?
通知数据的使用位置。并且数据可以在模板或vm中使用。$watch(),而且在不同的地方行为是不一样的。比如模板要在这里渲染,其他逻辑要在那里执行。所以简单抽象一个类。数据变化时通知它,然后它会通知其他地方。
这个类被命名为Watcher。是中介。
3.你依靠谁?
谁通知,取决于谁,取决于观察者。
4.什么时候通知?
修改数据时。即setter中的通知。
5.你什么时候收集依赖关系?
因为要告知数据使用的地方。你用的时候要读取数据,所以我们可以在你读取的时候收集,也就是在getter里面收集。
6.在哪里收集的?
您可以在每个属性中定义一个数组,与该属性相关的所有依赖项都放在该数组中。
编码如下(可以直接运行):
//全局变量,用于存储依赖关系
设globalData=undefined
//将数据转化为有响应的数据
函数defineReactive (obj,key,val) {
//依赖列表
let dependList=[]
Object.defineProperty(obj,key,{
可枚举:真,
可配置:真,
get: function () {
//收集依赖项(观察器)
global data dependlist . push(global data)
返回值
},
set:函数reactiveSetter (newVal) {
if(val===newVal){
返回
}
//通知依赖项(观察器)
dependList.forEach(w={
w.update(新瓦尔,瓦尔)
})
val=newVal
}
});
}
//依赖性
类监视器{
构造函数(数据、键、回调){
this.data=data
this.key=key
this.callback=回调;
this . val=this . get();
}
//这段代码可以将自己添加到依赖项列表中
get(){
//将依赖关系保存在globalData中
globalData=this
//读取数据时收集依赖项
设value=this.data[this.key]
globalData=未定义
返回值;
}
//数据发生变化时接收通知,然后通知外界。
更新(新值,旧值){
this.callback(newVal,oldVal)
}
}
/*下面是测试代码*/
let data={ };
//将name属性转换为响应。
defineReactive(数据,年龄, 88 )
//当数据年龄发生变化时,会通知观察器,然后观察器再通知外界。
新观察器(数据,年龄,(新值,旧值)={
console . log(` outside:new val=$ { new val };oldVal=${oldVal} `)
})
Data.age -=1 //控制台输出:outside:new val=87;旧值=88
继续在控制台下执行data.age -=1,输出外部世界:newVal=86旧瓦尔=87 .
附上数据、defineReactive、dependList、Watcher与外界的关系图。
首先,通过defineReactive()方法将数据转换为响应(defineReactive (data, age , 88 )。
当观察器读取数据时(let value=this.data[this.key]),会触发数据的getter,所以观察器被globalData收集。
当数据被修改时(data.age -=1),会触发setter,通知依赖者列表,通知依赖者给Watcher(w.update(newVal,Val)),最后Watcher通知外界。
二、关于 Object 的问题
想一想:在上面的例子中,delete data.age的继续执行是否会告知外界?
不会。因为设置器不会被触发。请继续阅读:
!声明文档类型
html lang=en
头
meta charset=UTF-8
meta name= viewport content= width=device-width,initial-scale=1.0
标题文档/标题
script src= https://cdn . jsdelivr . net/NPM/vue/dist/vue . js /script
/头
身体
div id=应用程序
部分
{{ p1.name }}
{{ p1.age }}
/部分
/div
脚本
const app=new Vue({
埃尔: #app ,
数据:{
p1: {
名称: ph ,
年龄:18岁
}
}
})
/脚本
/body
/html
运行后,页面会显示ph 18。我们知道当我们更改数据时,视图会被重新渲染,所以我们在控制台上执行delete app.p1.name,发现页面并没有改变。就像上面例子中执行delete data.age一样,不会触发setter,所以不会通知外界。
为了解决这个问题,Vue提供了两个API(后面会介绍):vm。$set和vm。$删除。
如果继续执行app。$delete(app.p1, age ),你会发现页面上没有任何信息(name属性已经用delete删除了,只是当时没有重新渲染)。
注意:如果在这里执行app.p1.sex=man ,使用数据p1的地方不会被通知。这个问题可以通过vm来解决。$set。
三、Array 的变化侦测
3.1、背景
如果数据是let data={a:1,b:[11,22]},它被Object.defineProperty转换成响应后,我们修改数据data.a=2,会通知外界,很好理解;同样的,data.b=[11,22,33]也会通知到外界,但是如果data b用另外一种方式修改,像这个data.b.push(33),就不会通知到外界,因为setter没有取。请参见示例:
函数defineReactive(obj,key,val) {
Object.defineProperty(obj,key,{
可枚举:真,
可配置:真,
get: function () {
console.log(`get val=${val} `)
返回值
},
set:函数reactiveSetter (newVal) {
if(val===newVal){
返回
}
console . log(` set val=$ { new val };oldVal=${val} `)
val=newVal
}
});
}
//下面是测试代码{1}
让数据={}
defineReactive(数据, a ,[11,22])
Data.a.push(33) //get val=11,22(不触发setter){ 2 }
data.a //get val=11,22,33
data . a=1//set val=1;OldVal=11,22,33(触发器设置器)
通过push()方法改变数组的值不会触发setter(第{2}行),所以无法通知外界。这里似乎有一个问题:通过Object.definePropery()方法,只能使对象具有响应性,而不能使数组具有响应性。
实际上,Object.definePropery()可以将数组变成响应。请参见示例:
//继续上面的示例,并将测试代码(第{1}行)更改为:
let data=[]
defineReactive(数据, 0 ,11)
data[0]=22//set val=22;旧值=11
Data.push(33) //不会触发{10}
虽然Object.definePropery()可以把数组变成响应式的,但是通过data.push(33) (line {10})修改数组仍然不会通知到外界。
所以在Vue中,数据转化为响应有两种方式:对象使用object . define property();该数组使用另一组。
3.2、实现
在es6中,可以使用代理来检测数组的变化。请参见示例:
让数据=[11,22]
设p=新代理(数据,{
set:函数(目标、属性、值、接收者){
target[prop]=值;
console.log(属性集: prop =值);
返回true
}
})
控制台. log(p)
p.push(33)
/*
输出:
[ 11, 22 ]
属性集:2=33
属性集:长度=3
*/
之前Es6有点麻烦,可以用拦截器。原理是:当我们执行[]。push(),我们将调用数组原型(Array.prototype)中的方法。我们在[]之间添加一个拦截器。push()和Array.prototype. When []。未来调用push(),首先执行拦截器中的push()方法,拦截器中的push()正在调用Array.prototype中的push()方法,请看示例:
//数组原型
让arrayPrototype=Array.prototype
//创建一个拦截器
let interceptor=object . create(array prototype)
//将拦截器与原始数组的方法相关联
;(“推入、弹出、取消移位、移位、拼接、排序、反转”)。拆分(,)。forEach(方法={
let origin=array prototype[method];
Object.defineProperty(侦听器,方法,{
值:函数(.args){
console . log(` interceptor:args=$ { args } } `)
return origin.apply(this,args);
},
可枚举:false,
可写:真,
可配置:真
})
});
//测试
设arr1=[a]
让arr2=[10]
arr1.push(b )
//检测数组arr2的变化
Object.setPrototypeOf(arr2,interceptor) //{20}
Arr2.push(11) //拦截器:args=11
Arr2.unshift(22) //拦截器:args=22
这个例子向拦截器添加了七个可以改变数组内容的方法。如果需要检测哪个数组的变化,将数组的原型指向拦截器(第{20}行)。当我们通过push等七种方法修改数组时,会在拦截器中触发,这样就可以通知外界了。
到目前为止,我们只完成了检测数组变化的任务。
更改数据并通知外界。上面的编码实现只针对对象数据,这里需要针对数组数据。
让我们思考同一个问题:
1.如何检测数组的变化?
interseptor
2.当数据改变时,我们通知谁?
看守人
3.你依靠谁?
看守人
4.什么时候通知?
修改数据时。在拦截器中通知。
5.你什么时候收集依赖关系?
因为要告知数据使用的地方。如果你使用数据,你必须读取数据。读取数据时收集。这与对象集合依赖项相同。
{a: [11,22]}比如我们要用数组A,就必须访问对象的属性A。
6.在哪里收集的?
对象在每个属性中收集依赖关系,但是这里我们要考虑数组会触发拦截器中的依赖关系,位置可能会被调整。
就是这样。不会继续了。在下一篇文章中,我会挑出vue中与数据检测相关的源代码,用这篇文章做一个简要的分析。
四、关于 Array 的问题
//需要自己介绍vue.js。后续也尽可能只列出核心代码。
div id=应用程序
部分
{{ p1[0] }}
{{ p1[1] }}
/部分
/div
脚本
const app=new Vue({
埃尔: #app ,
数据:{
p1: [ph , 18]
}
})
/脚本
运行后页面显示ph 18,控制台执行app.p1[0]=lj 。页面没有响应,因为数组只能通过拦截器调用指定的7个方法来通知外界。如果你执行app。$set(app.p1,0, pm ),页面内容就会变成pm 18。
以上是vue检测数据变化的基本实现的详细内容。更多关于vue检测数据变化的信息,请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。