모바일 이해의 Vue3 Reactivity의 구조적 후편
전편에서는 이미 targetMap을 통해 여러 객체를 관리할 수 있게 됐다.
후편에서 수동 호출
track
,trigger
함수를 자동으로 호출합니다.track(product, "quantity"); // ←自動で呼ばれて欲しい
product.quantity = 5;
trigger(product, "quantity"); // ←自動で呼ばれて欲しい
하고 싶은 일로즉, 속성의 get/set을 차단할 수 있다면 좋겠다.
Reflect
이른바 Reflect
JavaScript 작업에 개입할 수 있는 내장 객체를 제공합니다.
MDN에서
에서 target에서 지정한 대상의 함수를 호출하거나 속성을 가져올 수 있습니다.
그중 이번에는 Reflect.get을 사용합니다.
Reflect.get을 사용하면 대상에서 속성을 얻을 수 있습니다.
(receiver는 뒤에 설명합니다.)
Reflect.get(product, 'quantity', receiver) // => 3
Proxy
Proxy 다음과 같이 대상을 싸서 Proxy(대리인)가 대상에 접근할 수 있도록 합니다.
예를 들어 Proxy의 두 번째 매개 변수에 처리 프로그램을 전달하면 속성에 접근할 때 get 함수를 차단할 수 있습니다.
추가 Reflectget과 조합해서 사용하면 다음과 같습니다.
let product = { price: 5, quantity: 2 };
let proxiedProduct = new Proxy(product, {
get(target, key) {
console.log("Get key", key);
return target[key];
}
});
// Get key quantity
// 2
console.log(proxiedProduct.quantity);
receiver를 사용하면 target의 대상이 다른 대상에서 계승된 대상 자체임을 보증할 수 있습니다.(자세한 내용은 여기 기사.get과 마찬가지로 set도 다음과 같이 정의할 수 있습니다.
let product = { price: 5, quantity: 2 };
let proxiedProduct = new Proxy(product, {
get(target, key, receiver) {
console.log("Get key", key);
return Reflect.get(target, key, receiver);
}
});
console.log(proxiedProduct.quantity);
reactive 정의
리플렉스와 프록시를 파악한 끝에 드디어 실장에 들어갔다.
매개변수 "reactive"로 전달된 데이터가 Proxy화되어 반환되는 함수를 정의합니다.
let product = { price: 5, quantity: 2 };
let proxiedProduct = new Proxy(product, {
get(target, key, receiver) {
console.log("Get key", key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log("Set called", key, value);
return Reflect.set(target, key, value, receiver);
}
});
proxiedProduct.quantity = 10;
console.log(proxiedProduct.quantity);
지금 시작하는 get/set을 끊을 수 있습니다.즉, 속성의 get/set을 차단할 수 있다면 좋겠다.
그리고 트랙과 trigger를 각각 실행합니다.
속성이 get에 호출되었을 때 트랙 사용하기
속성이 설정되었을 때 trigger를 호출합니다
function reactive(target) {
const handler = {
get(target, key, receiver) {
console.log("Get key", key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log("Set called", key, value);
return Reflect.set(target, key, value, receiver);
}
};
return new Proxy(target, handler);
}
let product = { price: 5, quantity: 2 };
let proxiedProduct = reactive(product);
이렇게 하면 속성의 변경에 따라track,trigger를 호출할 수 있다.나는 당신이 직감적인 견해에 주의를 기울였다고 생각한다. 이것은 Composition API의 reactive에 해당한다.
드디어 완성됐습니다.🎉
그러면 마지막으로 정의한reactive 함수가 제대로 실행되었는지 확인하십시오.
앞부분을 포함하는 코드는 전체적으로 다음과 같다.
코드 확장
function reactive(target) {
const handler = {
get(target, key, receiver) {
let result = Reflect.get(target, key, receiver);
// getされたらtrackを呼び出す
track(target, key);
return result;
},
set(target, key, value, receiver) {
let oldValue = target[key];
let result = Reflect.set(target, key, value, receiver);
// 値が変更されたらtriggerを呼び出す
if (result && oldValue !== value) {
trigger(target, key);
}
return result;
}
};
return new Proxy(target, handler);
}
codesandbox 요점
먼저 totalEffect()를 초기화할 때 호출합니다.
프로젝트, Quantity의 변경 사항을 추적하기 위해 내부에서 트랙을 호출합니다.
// targetMapを定義
const targetMap = new WeakMap();
function track(target, key, effect) {
// targetMapからdepsMapを取得
let depsMap = targetMap.get(target);
// depsMapがなければ作成する
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
// depを取得
let dep = depsMap.get(key);
// depが存在しなければ作成する
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(effect);
}
function trigger(target, key) {
// targetMapからdepsMapを取得
const depsMap = targetMap.get(target);
// なければリターン
if (!depsMap) {
return;
}
let dep = depsMap.get(key);
if (dep) {
dep.forEach((effect) => effect());
}
}
function reactive(target) {
const handler = {
get(target, key, receiver) {
let result = Reflect.get(target, key, receiver);
track(target, key, totalEffect);
return result;
},
set(target, key, value, receiver) {
let oldValue = Reflect.get(target, key, receiver);
let result = Reflect.set(target, key, value, receiver);
if (result && oldValue !== value) {
trigger(target, key);
}
return result;
}
};
return new Proxy(target, handler);
}
let product = reactive({ price: 200, quantity: 3 });
let total = 0;
const totalEffect = () => {
total = product.price * product.quantity;
};
// 初期化
// effect内でpriceとquantityがgetされることで
// track(product, 'price')
// track(procut, 'quantity')
// がコールされる
totalEffect()
// quantityにsetされることで
// trigger(product, 'quantity')がコールされ
// effectが再計算されるがコールされ、totalが更新される。
product.quantity = 4;
console.log(total); // => 800
수치를 Quantity로 설정한 후 Proxy 내부에서 trigger를 호출하고 total의 값을 업데이트합니다.// 初期化
// effect内でpriceとquantityがgetされることで
// track(product, 'price')
// track(procut, 'quantity')
// がコールされる
totalEffect()
이상, 예쁘게 track
,trigger
자동 호출, 설정값만 있으면 토탈을 업데이트합니다🎉어때요?
프레임의 인터페이스를 이용하는 것 뿐만 아니라 이번처럼 내부 구조를 이해함으로써 디버깅을 하고 디자인을 배우며 프로그램 디자인을 깊이 있게 이해하면 프로그래밍의 즐거움을 누릴 수 있지 않겠는가.
[추기] 레프에 대해서.
reactive가 있다면 많은 사람들이 ref라고 생각할 것 같아서 가볍게 건드려보세요.
예를 들어 다음과 같은 total이saleProice의 다른 effect의 결과로 대입된 값에 의존하는 상황을 고려한다.
// quantityにsetされることで
// trigger(product, 'quantity')がコールされ
// effectが再計算されるがコールされ、totalが更新される。
product.quantity = 4;
console.log(total); // => 800
여기는 제품입니다.price에 대한 변경을 통해 직접적으로 의존하는aleProice가 업데이트되었지만,aleProice에 의존하는total이 업데이트되지 않았음을 알 수 있다.let product = reactive({ price: 5, quantity: 2 })
let salePrice = 0
let total = 0
effect(() => {
// totalはsalePriceに依存する
total = salePrice * product.quantity
})
effect(() => {
salePrice = product.price * 0.9
})
// salePriceには変更されるが、salePriceに依存するtotalの値は変更されない
product.price = 10
console.log(
`total (should be 18) = ${total} salePrice (should be 9) = ${salePrice}`
// total (should be 18) = 0 salePrice (should be 9) = 9
)
거기에서ref를 사용할 수 있습니다.이른바
property만value의reactive 대상으로
따라서 단일 값을 reactive로 직접 사용할 수 있습니다.
이번 예에서는 다음과 같은 방법을 통해 반영해야 한다.
// salePriceには変更されるが、salePriceに依存するtotalの値は変更されない
product.price = 10
console.log(
`total (should be 18) = ${total} salePrice (should be 9) = ${salePrice}`
// totalが0更新されず0のままになっている。
// total (should be 18) = 0 salePrice (should be 9) = 9
)
ref 설치
ref를 실현하는 두 가지 방법이 있어요.
첫 번째 방법은 간단하게 리얼리티로 싸는 것이다.
let product = reactive({ price: 5, quantity: 2 })
let salePrice = ref(0) // refの使用
let total = 0
// effectの中では.valueを使うようにする
effect(() => {
total = salePrice.value * product.quantity
})
effect(() => {
salePrice.value = product.price * 0.9
})
그러나 이것은 사실상 Vue3의ref 실시 방식과 다르다.ObjectAccess 정보
그 구현을 보기 전에 먼저 ObjectAccess를 실제로 이해해야 합니다.
이것은 별명
JavaScript computed properties
(Vue의 컴퓨터property와 다른 것)으로 다음과 같은 정의get/set
로 컴퓨터property를 정의할 수 있습니다.function ref(initialValue){
return reactive({value: initialValue})
}
이걸 사용하면ref는reactive를 사용하지 않는 간단한 형식으로 정의할 수 있습니다.let user = {
firstName: 'Gregg',
lastName: 'Pollack',
get fullName() {
return `${this.firstName} ${this.lastName}`
},
set fullName(value) {
[this.firstName, this.lastName] = value.split(' ')
},
}
console.log(`Name is ${user.fullName}`)
user.fullName = 'Adam Jahr'
console.log(`Name is ${user.fullName}`)
Reference
이 문제에 관하여(모바일 이해의 Vue3 Reactivity의 구조적 후편), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/kazuwombat/articles/3cc9b504dd5999텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)