이 기사는 Vue를 배우고 Vue가 반응형 데이터를 처리하는 방법에 대해 이야기하는 데 도움이 될 것입니다. 모두에게 도움이 되기를 바랍니다!
vue의 반응형 데이터에 대해 많은 의구심을 가지실 수 있습니다.
예를 들어 프록시로 전환해야 하는 이유는 무엇입니까?
예를 들어, 반응성과 참조라는 두 가지 API가 있는 이유는 무엇입니까?
예를 들어 Vue는 응답성을 어떻게 구현합니까?
사실 이에 대한 답은 소스코드에서 찾을 수 있습니다.
첫 번째 글에서 vue3의 업그레이드는 무엇인지, 프록시 사용의 이점과 Object.defineProperty의 단점에 대해서도 언급했습니다. 하지만 오늘은 각도를 바꿔 chatGPT가 이 질문에 답하도록 하고자 합니다. [관련 추천 : vuejs 영상 튜토리얼, 웹 프론트엔드 개발]
글쎄요, 제가 요약한 것보다 더 좋은 것 같아요.
그런 다음 다음 질문으로 넘어가세요.
이 답변은 좀 더 공식적인 느낌이 들지만 제가 원하는 답변은 아닙니다.
그 이유는 실제로 매우 간단합니다. 왜냐하면 프록시 메소드는 값 유형을 프록시할 수 없고 객체만 프록시할 수 있기 때문입니다. 따라서 값 유형 데이터를 처리하려면 추가적인 방법이 필요합니다.
물론 하나의 참조로 전 세계를 장악할 수도 있습니다. vue 소스 코드가 호환되면 자동으로 변환됩니다.
이런 이해를 바탕으로 vue3 소스 코드를 모방하여 살펴보겠습니다. 반응형 시스템을 구현합니다.
클릭하면 관련 코드를 볼 수 있습니다. 기사 제목에 따라 다른 지점을 선택하면 됩니다.
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 등이 있는 경우 리디렉션될 수 있습니다.
예:
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를 사용하면 이름과 나이에 액세스할 때 이를 트리거할 수 있습니다.
대상으로 변경한 후에는 정보에서 한 번만 실행됩니다.
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) } } }复制代码
소스 코드에서는 get/set이 value를 통해 설정되기 때문에 왜 ref를 .value에서 사용해야 하는지 매우 직관적으로 설명합니다.
데이터 프록시를 완료했지만 데이터가 변경된 후 구성 요소에 양방향 바인딩을 구현하도록 어떻게 알릴 수 있나요?
답은 수집과 트리거링에 의존하는 것입니다. 즉, get이 트리거되면 get을 트리거하는 조건[함수]을 저장합니다. set이 트리거되면 다시 실행하여 조건[함수]을 트리거하면 됩니다. .
코드를 다시 살펴보겠습니다. 트랙 수집 방법과 대상 트리거 방법을 추가하겠습니다. (즉, 위 코드에서 주석 처리된 두 줄의 코드입니다.)
또한 트리거 기능을 관리하려면 효과 기능이 필요합니다.
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 세계가 출력됩니다
데이터의 프록시는 복잡하지 않습니다. 프록시를 기반으로 일부 경계 처리가 추가됩니다. 응답성을 얻으려면 종속성 수집 및 종속성 트리거 구현을 추가해야 합니다.
effect는 매우 중요한 기능입니다. useEffect 및 watch와 같은 많은 API가 이 기능을 기반으로 개발되었습니다. 컴포넌트 업데이트 역시 이펙트 기능에 기반을 두고 있는데, 이에 대해서는 나중에 설명하겠습니다.
효과가 이해되지 않으면 실행 순서를 정리할 수 있습니다.
을 클릭하면 됩니다. 관련 코드를 확인하고 Lesson3 브랜치를 선택하세요.
글과 관련된 코드는 vue/examples에서 보실 수 있으며, 모방 vue 구현 버전은 packages/reactivity에서 보실 수 있습니다. (학습 영상 공유:위 내용은 Vue의 반응형 데이터 처리 방법에 대한 심층 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!