Vue 감청 데이터 개체 변화 소스 코드
Vue 에서 데이터 대상 의 감 시 는 ES5 의 새로운 특성(ES7 이 곧 나 오 는데 ES5 의 것 은 정말 새 롭 지 않다)Object.defineProperty()의 set,get 을 설정 하여 이 루어 집 니 다.
목표.
공식 문서 의 첫 번 째 예 와 비슷 하지만 간략화 도 있 습 니 다.이 편 은 데이터 대상 의 감청 만 소개 하고 텍스트 분석 과 관련 되 지 않 기 때문에 텍스트 분석 과 관련 된 것 은 직접 버 렸 습 니 다.
<div id="app"></div>
var app = new Vue({
el: 'app',
data: {
message: 'Hello Vue!'
}
});
브 라 우 저 디 스 플레이:Hello Vue!
콘 솔 에 다음 과 같이 입력 하 십시오:
app.message = 'Changed!'
이러한 명령 은 브 라 우 저 표시 내용 에 따라 수 정 됩 니 다.
Object.defineProperty
MDN 의 정의 참조:
Object.defineProperty()방법 은 한 대상 에 새 속성 을 직접 정의 하거나 이미 존재 하 는 속성 을 수정 하여 이 대상 을 되 돌려 줍 니 다.
이 상생 과 함께 Object.getOWn Property Descriptor()가 있 습 니 다.
Object.getOwnProperty Descriptor()지정 한 대상 의 이전 속성 에 대응 하 는 속성 설명 자 를 되 돌려 줍 니 다.(자체 속성 이란 이 대상 에 게 직접 부여 하 는 속성 으로 원형 체인 에서 찾 을 필요 가 없 는 속성 을 말한다)
다음 의 예 는 비교적 간단 하고 직관 적 인 방식 으로 setter,getter 를 설정 합 니 다.
var dep = [];
function defineReactive(obj, key, val) {
// property, property
var property = Object.getOwnPropertyDescriptor(obj, key);
if(property && property.configurable === false) {
return;
}
var getter = property && property.get;
var setter = property && property.set;
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
var value = getter ? getter.call(obj) : val;
dep.push(value);
return value;
},
set: function(newVal) {
var value = getter ? getter.call(obj) : val;
// set ,
if(newVal === value) {
return;
}
if(setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
console.log(dep);
}
});
}
var a = {};
defineReactive(a, 'a', 12);
// getter,12 dep, dep [12]
a.a;
// setter, dep ([12])
a.a = 24;
// getter,24 dep, dep [12, 24]
a.a;
Observer간단하게 Object.defineProperty 라 고 하면 Observer 라 고 할 수 있 습 니 다.관찰자대상 속성 값 의 변 화 를 관찰 하 다.따라서 observer 란 대상 의 모든 속성 에 getter,setter 를 추가 하 는 것 입 니 다.대상 의 속성 에 속성 이 있다 면{a:{a:{a'a}}은 재 귀 를 통 해 그 속성 에 대한 속성 에 getter,setter 를 추가 합 니 다.
function Observer(value) {
this.value = value;
this.walk(value);
}
Observer.prototype.walk = function(obj) {
var keys = Object.keys(obj);
for(var i = 0; i < keys.length; i++) {
// getter、setter
defineReactive(obj, keys[i], obj[keys[i]]);
}
};
var dep = [];
function defineReactive(obj, key, val) {
// property, property
var property = Object.getOwnPropertyDescriptor(obj, key);
if(property && property.configurable === false) {
return;
}
var getter = property && property.get;
var setter = property && property.set;
// getter、setter
var childOb = observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
var value = getter ? getter.call(obj) : val;
dep.push(value);
return value;
},
set: function(newVal) {
var value = getter ? getter.call(obj) : val;
// set ,
if(newVal === value) {
return;
}
if(setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
// getter、setter
childOb = observe(newVal);
console.log(dep);
}
});
}
function observe(value) {
if(!value || typeof value !== 'object') {
return;
}
return new Observer(value);
}
WatcherObserver 는 데이터 대상 의 getter,setter 를 설정 하여 데이터 변 화 를 감청 하 는 목적 을 달성 합 니 다.데 이 터 를 가 져 오고 설정 되 고 수정 되 며 모두 들 을 수 있 으 며 해당 하 는 동작 을 할 수 있 습 니 다.
지금 또 하나의 문 제 는 누가 당신 에 게 감청 하 라 고 했 습 니까?
이 명령 을 내 린 것 은 Watcher 입 니 다.Watcher 가 데 이 터 를 가 져 와 야 해당 하 는 조작 을 할 수 있 습 니 다.마찬가지 로 데 이 터 를 수정 할 때 도 왓 처 관련 조작 만 수행한다.
그럼 Observer,Watcher 두 가 지 를 어떻게 말 합 니까?전역 변수!이 전역 변 수 는 Watcher 만 수정 할 수 있 습 니 다.Observer 는 읽 기 판단 일 뿐 이 전역 변수의 값 에 따라 Watcher 가 데 이 터 를 읽 을 지 여 부 를 판단 합 니 다.이 전역 변 수 는 dep 에 추가 할 수 있 습 니 다.
dep.target = null;
상기 에서 말 한 바 와 같이 간단하게 정리 하면 코드 는 다음 과 같다.
function Watcher(data, exp, cb) {
this.data = data;
this.exp = exp;
this.cb = cb;
this.value = this.get();
}
Watcher.prototype.get = function() {
// dep.target , Observer Watcher getter
dep.target = this;
// getter,
var value = this.data[this.exp];
// dep.target
dep.target = null;
return value;
};
Watcher.prototype.update = function() {
this.cb();
};
function Observer(value) {
this.value = value;
this.walk(value);
}
Observer.prototype.walk = function(obj) {
var keys = Object.keys(obj);
for(var i = 0; i < keys.length; i++) {
// getter、setter
defineReactive(obj, keys[i], obj[keys[i]]);
}
};
var dep = [];
dep.target = null;
function defineReactive(obj, key, val) {
// property, property
var property = Object.getOwnPropertyDescriptor(obj, key);
if(property && property.configurable === false) {
return;
}
var getter = property && property.get;
var setter = property && property.set;
// getter、setter
var childOb = observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
var value = getter ? getter.call(obj) : val;
// Watcher , Watcher dep
if(dep.target) {
dep.push(dep.target);
}
return value;
},
set: function(newVal) {
var value = getter ? getter.call(obj) : val;
// set ,
if(newVal === value) {
return;
}
if(setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
// getter、setter
childOb = observe(newVal);
// dep update
for(var i = 0; i < dep.length; i++) {
dep[i].update();
}
}
});
}
function observe(value) {
if(!value || typeof value !== 'object') {
return;
}
return new Observer(value);
}
var data = {a: 1};
new Observer(data);
new Watcher(data, 'a', function(){console.log('it works')});
data.a =12;
data.a =14;
위 에서 기본적으로 데이터 감청 을 실 현 했 습 니 다.bug 가 적지 않 을 것 입 니 다.다만 거 친 demo 일 뿐 대략적인 절 차 를 보 여 주 려 고 했 을 뿐 매우 세밀 하 게 잠 그 지 않 았 습 니 다.Dep
위의 몇 가지 예 를 들 어 dep 는 전체적인 배열 입 니 다.new 하나의 Watcher,dep 에 Watcher 인 스 턴 스 가 하나 더 있어 야 합 니 다.이 럴 때 어느 data 가 업데이트 되 든 모든 Watcher 인 스 턴 스 의 update 가 실 행 됩 니 다.이것 은 받 아들 일 수 없습니다.
Dep 추상 화 되 어 하나의 구조 함 수 를 만 들 고 전체 국면 에 두 지 않 으 면 해결 할 수 있 습 니 다.
function Dep() {
this.subs = [];
}
Dep.prototype.addSub = function(sub) {
this.subs.push(sub);
};
Dep.prototype.notify = function() {
var subs = this.subs.slice();
for(var i = 0; i < subs.length; i++) {
subs[i].update();
}
}
Dep 를 이용 하여 위의 코드 를 고 쳐 쓰 면 됩 니 다.Vue 인 스 턴 스 에이전트 data 개체
공식 문서 에는 다음 과 같은 말 이 있다.
모든 Vue 인 스 턴 스 는 data 대상 의 모든 속성 을 대리 합 니 다.
var data = { a: 1 };
var vm = new Vue({data: data});
vm.a === data.a // -> true
//
vm.a = 2
data.a // -> 2
// ...
data.a = 3
vm.a // -> 3
이런 대 리 는 보기에 매우 번 거 로 워 보이 지만 사실은 Object.defineProperty 를 통 해 이 루어 질 수 있다.
function Vue(options) {
var data = this.data = options.data;
var keys = Object.keys(data);
var i = keys.length;
while(i--) {
proxy(this, keys[i];
}
}
function proxy(vm, key) {
Object.defineProperty(vm, key, {
configurable: true,
enumerable: true,
// vm.data[key]
get: function() {
return vm.data[key];
},
// vm.data[key]
set: function(val) {
vm.data[key] = val;
}
};
}
Vue 를 만들어 서 최초의 목 표를 실현 하 다.
var Vue = (function() {
var Watcher = function Watcher(vm, exp, cb) {
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.value = this.get();
};
Watcher.prototype.get = function get() {
Dep.target = this;
var value = this.vm._data[this.exp];
Dep.target = null;
return value;
};
Watcher.prototype.addDep = function addDep(dep) {
dep.addSub(this);
};
Watcher.prototype.update = function update() {
this.run();
};
Watcher.prototype.run = function run() {
this.cb.call(this.vm);
}
var Dep = function Dep() {
this.subs = [];
};
Dep.prototype.addSub = function addSub(sub) {
this.subs.push(sub);
};
Dep.prototype.depend = function depend() {
if(Dep.target) {
Dep.target.addDep(this);
}
};
Dep.prototype.notify = function notify() {
var subs = this.subs.slice();
for(var i = 0; i < subs.length; i++) {
subs[i].update();
}
};
Dep.target = null;
var Observer = function Observer(value) {
this.value = value;
this.dep = new Dep();
this.walk(value);
};
Observer.prototype.walk = function walk(obj) {
var keys = Object.keys(obj);
for(var i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]]);
}
};
function defineReactive(obj, key, val) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if(property && property.configurable === false) {
return;
}
var getter = property && property.get;
var setter = property && property.set;
var childOb = observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
var value = getter ? getter.call(obj) : val;
if(Dep.target) {
dep.depend();
if(childOb) {
childOb.dep.depend();
}
}
return value;
},
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val;
if(newVal === value) {
return;
}
if(setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = observe(newVal);
dep.notify();
}
});
}
function observe(value) {
if(!value || typeof value !== 'object') {
return;
}
return new Observer(value);
}
function Vue(options) {
var vm = this;
this._el = options.el;
var data = this._data = options.data;
var keys = Object.keys(data);
var i = keys.length;
while(i--) {
proxy(this, keys[i]);
}
observe(data);
var elem = document.getElementById(this._el);
elem.innerHTML = vm.message;
new Watcher(this, 'message', function() {
elem.innerHTML = vm.message;
});
}
function proxy(vm, key) {
Object.defineProperty(vm, key, {
configurable: true,
enumerable: true,
get: function proxyGetter() {
return vm._data[key];
},
set: function proxySetter(val) {
vm._data[key] = val;
}
});
}
return Vue;
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="vue.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
var app = new Vue({
el: 'app',
data: {
message: 'aaaaaaaaaaaaa'
}
});
</script>
</body>
</html>
참고 자료:vue 소스 코드 분석 은 observer 와 watcher 를 어떻게 실현 합 니까?
vue 초기 소스 학습 시리즈 중 하나:대상 의 변 화 를 어떻게 감청 합 니까?
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Vue Render 함수로 DOM 노드 코드 인스턴스 만들기render에서createElement 함수를 사용하여 DOM 노드를 만드는 것은 직관적이지 않지만 일부 독립 구성 요소의 디자인에서 특수한 수요를 충족시킬 수 있습니다.간단한 렌더링 예는 다음과 같습니다. 또한 v...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.