1.目标:
假设我现在有一个对象obj
let obj = {text:"hello 小明"}
还有一个函数effect
1. function effect (){ 2. //document.body.innerHTMl看作是页面上的值 3. document.body.innerHTMl = obj.text 4. }
我们希望修改值obj.text = "hello vue3"时,effect函数页执行,使得页面数据发生变化
想要的效果是修改obj.text的值,页面也跟着变化,这就是响应式。
再明确一下目标:修改对象的值,使页面自动更新我们修改的最新值(数据)
2.原理
vue2使用的是obj.defineproperty函数实现,而vue3使用的proxy代理对象实现,这里使用proxy。
完成上述的目的有两个重要的信息:
第一个时当执行effect的时候会触发obj.text的读取操作
第二个是当修改obj.text的时候会触发obj.text的设置操作
当然,重头戏就是代理对象proxy来实现了
3.实现代码
const bucket = new Set() //类数组结构,用来存储effect函数 const data = {text:"hello xiaoming"} //原始数据 const obj = new Proxy(data,{ //拦截读取操作 get(target,key){ //将effect添加到bucket bucket.add(effect) //返回属性值 return targrt[key] }, //拦截设置操作 set(target,key,newval){ //设置属性值 target[key] = newval //把effect从bucket中取出来并执行 bucket.forEach(fn => fn()) //返回true代表设置成功 return true } })
接下来执行以下看效果
function effect(){ document.body.innerHTML = obj.text } effect()//执行 //过1秒后执行修改,看看页面是否变化 setTimeout(()=>{ obj.text = "看看修改成功了没有" },1000)
ps:肯定有人疑问为啥bucket是干嘛的,其实就是为了实现页面变化,细品一下。
这个bucket的作用就是修改dom元素使页面及时变化
要理解proxy”代理“这两个字,举个简单的例子,本来有一个苹果,但是想要的是苹果汁,那么在苹果变成苹果汁的过程可以看作被代理了,这么理解的话,proxy就很容易理解了。
4.小结:
到目前为止,响应式数据完成了,但是不够完善,但这就是响应式原理的最简单实现代码。后续我会写一个完善版的响应式文章。
在上面,我们创建了一个存储副作用的桶bucket,他是set类型。接着定义原始数据类型data,obj是原始数据的代理对象,我们分别设置了get 和 set拦截函数,用于拦截和读取操作。当读取属性时将副作用effect函数放到桶里,即bucket.add(effect),然后返回属性值。当设置属性值时先更新原始数据,然后将副作用函数从桶里取出来并且执行,这样我们就实现了响应式数据。