这篇“vue处理响应式数据的方法是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue处理响应式数据的方法是什么”文章吧。
那继续下一个问题。
这个回答感觉就比较官方了,没有回答到我想要的答案。
理由其实很简单,因为proxy方法不能代理值类型,只能代理对象。所以需要一个额外的方法,来处理值类型的数据。
当然你也可以一个ref走天下,vue源码做了兼容会自动进行转换
有了这些理解之后,我们来看今天的重头戏,自己仿照vue3源码,实现一个响应式系统。
相关代码可以点击查看,根据文章的标题选择不同的分支即可。
在前文我们也介绍了,reactive其实用的是proxy代理对象。
我们可以使用proxy实现一个简单的代理函数reactive。
function reactive(target) { const isObject = (val) => val !== null && typeof val === 'object'
if (!isObject(target)) { console.warn(`数据必须是对象: ${String(target)}`) return target
} const proxy = new Proxy(target, { get: (target, key, receiver) => { console.log('get', key) const res = Reflect.get(target, key, receiver) // track(target, key)
// 这句很关键,不然嵌套数据,里面的不会响应
if (isObject(res)) { return reactive(res)
} return res
}, set: (target, key, value, receiver) => { console.log('set', key, value) const result = Reflect.set(target, key, value, receiver) // trigger(target, key, value)
return result
}, deleteProperty: () => { const result = Reflect.deleteProperty(target, key) return result
}
}) return proxy
} const person = reactive({ name: '', age: 18, like: ['apple']
})
person.name = 'vue test'复制代码
注意:Reflect
Reflect.get(target, key)跟直接访问target[key]是有一点差别的。
当代理对象中,有get,set等this指向的时候,可以重定向this。
举个例子:
const person = new Proxy({ name: "vue test", age: 18, get info() { return 'name is :' + this.name + ' age is:' + this.age
}
}, { get: (target, key, receiver) => { console.log('get', key) // return target[key]
return Reflect.get(target, key, receiver)
}
}) console.log(person.info)复制代码
使用Reflect,我们在访问name, age的时候均可以触发。
改成target之后,则只会在info触发一次。
return target[key]复制代码
在vue3中,ref是通过get和set来实现的。
跟上面类似,也还是先声明一个函数,然后通过get,set来访问数据。
function ref(value) { return new RefImpl(value)
} class RefImpl { constructor(value) { // 如果值是对象,则用reactive处理
this._value = isObject(value) ? reactive(value) : value // 记录一下初始值
this._rawValue = value
} get value() { // trackRefValue(this)
return this._value
} set value(newVal) { if (!Object.is(newVal, this._rawValue)) { // 更新原始数据
this._rawValue = newVal // 更新 .value 的值
this._value = isObject(newVal) ? reactive(newVal) : value // triggerRefValue(this)
}
}
}复制代码
源码也很直观的解释了,为什么ref的使用一定要用.value的方式使用,因为get/set就是通过value来设置的。
数据代理我们已经完成了,但是数据更改之后,怎么通知组件实现双向绑定呢?
答案就是依赖收集与触发,也就是当触发get的时候,我把触发get的这个条件【函数】给保存起来,当触发set的时候,重新执行一下触发一下这个条件【函数】不就行了吗。
还是来看代码,我加一个track收集的方法,加一个target触发的方法。(也就是上面代码片段注释掉的两行代码)
除此之外,还需要一个effect函数来管理触发函数。
let activeEffect = null
const targetMap = new WeakMap() // 依赖收集/触发
function track(target, key) { let depsMap = targetMap.get(target) if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
} let dep = depsMap.get(key) if (!dep) {
dep = new Set()
}
dep.add(activeEffect)
depsMap.set(key, dep)
} function trigger(target, key, value) { const depsMap = targetMap.get(target) if (!depsMap) { return
} const deps = depsMap.get(key) if (!deps) { return
}
deps.forEach(effectFn => { if (effectFn.scheduler) {
effectFn.scheduler()
} else { effectFn()
}
})
} function effect(fn,options = {}) { const effectFn = () => { try {
activeEffect = effectFn return fn()
} catch (error) {
activeEffect = null
}
} if (!options.lazy) { effectFn()
}
effectFn.scheduler = options.scheduler
return effectFn
} const person = reactive({ name: "hello world"
}) effect(() => { console.log('effect person', person.name)
}) setTimeout(() => {
person.name = 'setTimeout world'
}, 2000)复制代码
activeEffect用来存触发的条件函数。
targetMap用来存放依赖字典,格式如下
{
target: {
key: []
}
}复制代码
输出结果为hello world,2秒之后,重新执行依赖性函数,输出setTimeout world
数据的代理并不复杂,就是在proxy的基础上,加上一些边界处理。而要实现响应式则需要加上依赖收集,依赖触发的实现。
effect是一个很重要的函数,很多api是基于这个函数开发的,比如useEffect, watch。组件的更新也是基于effect函数,这个后续还会提到。
如果不太理解effect,可以梳理一下执行顺序。
1.调用effect函数,并传入参数fn
2.申明一个effectFn函数,并执行,将函数保存为activeEffect
3.执行fn,读取person.name
4.走proxy的get代理
5.收集依赖,将activeEffect保存的函数存到全局map中
6.此时,effect函数执行完毕,等待数据更新
7.2s后,走proxy的set代理
8.执行全局map中保存的函数,重新执行effect函数,再次回到步骤1
相关代码可以点击查看,选择lesson3分支即可。
文章相关代码,可在vue/examples中查看,仿vue实现版本可在packages/reactivity中查看。
以上就是关于“vue处理响应式数据的方法是什么”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。