vue 에서 watch 와 coptute d 는 왜 데이터 의 변화 와 다른 점 을 감청 할 수 있 습 니까?
우선 Vue 애플 리 케 이 션 을 만 들 때:
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
Vue 구조 함수 원본 코드:
// Vue
function Vue (options) {
if (!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}
//_init , data,watch,computed
Vue.prototype._init = function (options) {
var vm = this;
// a uid
vm._uid = uid$3++;
......
// expose real self
vm._self = vm;
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
initState(vm);
......
};
initState 방법 에서 data,watch,coptute 를 초기 화하 고 object 함수 감청 data(Object.defineProperty)를 호출 합 니 다.
function initState (vm) {
vm._watchers = [];
var opts = vm.$options;
if (opts.props) { initProps(vm, opts.props); }
if (opts.methods) { initMethods(vm, opts.methods); }
if (opts.data) {
initData(vm);//initData observe
} else {
observe(vm._data = {}, true /* asRootData */);
}
if (opts.computed) { initComputed(vm, opts.computed); }
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch);
}
}
1、observeobserve 는 initState 에서 호출 되 었 습 니 다.vue 인 스 턴 스 의 data 속성 값 에 getter,setter 함 수 를 만 들 고 setter 에서 dep.depend 는 watcher 인 스 턴 스 를 Dep 인 스 턴 스 의 subs 속성 에 추가 합 니 다.getter 에 서 는 dep.notify 를 호출 하여 watcher 의 update 방법 을 호출 합 니 다.
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
* initState
*/
function observe (value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
var ob;
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value);
}
if (asRootData && ob) {
ob.vmCount++;
}
re * Observer class that is attached to each observed
* object. Once attached, the observer converts the target
* object's property keys into getter/setters that
* collect dependencies and dispatch updates.
*/
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
this.walk(value);
}
};
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]);
}
};
/**
* Define a reactive property on an Object.
*/
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
var getter = property && property.get;
var setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
var childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
//Dep.target Watcher
// dep.addSub, Watcher Dep Watcher
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();// , Dep notify , Watcher
} }); }
2、DepWatcher 의 update 방법 은 new Dep 의 notify 방법 에서 호출 되 었 습 니 다.
/**
* A dep is an observable that can have multiple
* directives subscribing to it.
*/
var Dep = function Dep () {
this.id = uid++;
this.subs = [];
};
// Watcher
// Dep.target, Watcher
// this.get
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};
// subs update
Dep.prototype.notify = function notify () {
// stabilize the subscriber list first
var subs = this.subs.slice();
if (!config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort(function (a, b) { return a.id - b.id; });
}
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
3、watchwatch 를 초기 화 합 니 다.함수 에서 createWatcher 를 호출 합 니 다.createWatcher 는$watch 를 호출 하고$watch 는 new Watcher 인 스 턴 스 를 호출 합 니 다.
function initWatch (vm, watch) {
for (var key in watch) {
var handler = watch[key];
if (Array.isArray(handler)) {
for (var i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i]);
}
} else {
createWatcher(vm, key, handler);
}
}
}
function createWatcher (
vm,
expOrFn,
handler,
options
) {
if (isPlainObject(handler)) {
options = handler;
handler = handler.handler;
}
if (typeof handler === 'string') {
handler = vm[handler];
}
return vm.$watch(expOrFn, handler, options)
}
Vue.prototype.$watch = function (
expOrFn,
cb,
options
) {
var vm = this;
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {};
options.user = true;
var watcher = new Watcher(vm, expOrFn, cb, options);
if (options.immediate) {
try {
cb.call(vm, watcher.value);
} catch (error) {
handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
}
}
return function unwatchFn () {
watcher.teardown();
}
};
}
2、computedcomputed 를 초기 화하 고 new Watcher()를 호출 하 며 defineComputed 함 수 를 통 해 계산 속성 을 vue 인 스 턴 스 에 마 운 트 하여 계산 속성 을 템 플 릿 에서 사용 할 수 있 도록 합 니 다.
var computedWatcherOptions = { lazy: true }
function initComputed (vm, computed) {
// $flow-disable-line
var watchers = vm._computedWatchers = Object.create(null);
// computed properties are just getters during SSR
var isSSR = isServerRendering();
for (var key in computed) {
var userDef = computed[key];
var getter = typeof userDef === 'function' ? userDef : userDef.get;
//getter computed
if (getter == null) {
warn(
("Getter is missing for computed property \"" + key + "\"."),
vm
);
}
if (!isSSR) {
// create internal watcher for the computed property.
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
);
}
//
// 。
// 。
if (!(key in vm)) {
defineComputed(vm, key, userDef);
} else {
if (key in vm.$data) {
warn(("The computed property \"" + key + "\" is already defined in data."), vm);
} else if (vm.$options.props && key in vm.$options.props) {
warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);
}
}
}
}
function defineComputed (
target,
key,
userDef
) {
var shouldCache = !isServerRendering();//true
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: createGetterInvoker(userDef);
sharedPropertyDefinition.set = noop;
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop;
sharedPropertyDefinition.set = userDef.set || noop;
}
if (sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
("Computed property \"" + key + "\" was assigned to but it has no setter."),
this
);
};
}
Object.defineProperty(target, key, sharedPropertyDefinition);
}
//computed getter , computed
function createComputedGetter (key) {
return function computedGetter () {
var watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
if (watcher.dirty) {//true
watcher.evaluate();// watcher.get , computed
}
if (Dep.target) {
watcher.depend();
}
return watcher.value
}
}
}
상기 코드 를 통 해 watch 와 coptute d 는 모두 new Watcher 인 스 턴 스 를 통 해 데이터 감청 을 실현 하 는 것 을 볼 수 있 습 니 다.그러나 coptute d 의 options 에서 lazy 는 true 이 고 이 매개 변 수 는 서로 다른 노선 을 걷 게 합 니 다.computed:템 플 릿 이 데 이 터 를 가 져 올 때 getter 함 수 를 촉발 하고 최종 적 으로 watcher.get 을 호출 합 니 다.즉,대응 하 는 반전 함 수 를 호출 합 니 다.
watch:템 플 릿 이 데 이 터 를 가 져 올 때 getter 함 수 를 터치 하여 해당 하 는 Dep.subs 에 watcher 를 추가 합 니 다.나중에 setter 가 호출 될 때 Dep.notify 는 모든 watcher 에 게 update 를 알 리 고 최종 적 으로 watcher.cb 를 호출 합 니 다.즉,대응 하 는 반전 함 수 를 호출 합 니 다.
3、Watcher
구조 함 수 는 watch 일 때 this.get 을 마지막 으로 호출 하고 속성의 getter 함 수 를 촉발 하여 이 Watcher 를 Dep 의 subs 에 추가 하여 데이터 변동 을 알 릴 때 호출 합 니 다.
Watcher 인 스 턴 스 를 호출 하 는 update 방법 은 run 방법 을 촉발 하고 run 방법 에 서 는 트리거 함 수 를 호출 합 니 다.depend 방법 은 new Dep 의 depend 방법 을 호출 합 니 다.dep 의 depend 는 Watcher 의 addDep 방법 을 호출 합 니 다.최종 적 으로 이 watcher 인 스 턴 스 를 Dep 의 subs 속성 에 추가 합 니 다.
/**
* , ,
* 。
* $watch()api 。
*/
var Watcher = function Watcher (
vm,
expOrFn,
cb,
options,
isRenderWatcher
) {
this.vm = vm;
......
this.cb = cb;//
this.id = ++uid$2; // uid for batching
this.active = true;
this.dirty = this.lazy; // for lazy watchers
......
this.value = this.lazy ? undefined ? this.get();//computed undefined, watch Watcher.get
};
/**
* Scheduler job interface.
* Will be called by the scheduler.
*
*/
Watcher.prototype.run = function run () {
if (this.active) {
var value = this.get();
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// set new value
var oldValue = this.value;
this.value = value;
if (this.user) {
try {
this.cb.call(this.vm, value, oldValue);
} catch (e) {
handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\""));
}
} else {
this.cb.call(this.vm, value, oldValue);
}
}
}
};
/**
* Evaluate the getter, and re-collect dependencies.
*/
Watcher.prototype.get = function get () {
pushTarget(this);
var value;
var vm = this.vm;
try {
value = this.getter.call(vm, vm);
} catch (e) {
if (this.user) {
handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value);
}
popTarget();
this.cleanupDeps();
}
return value
};
/**
* Subscriber interface.
* Will be called when a dependency changes.
* Watcher run
*/
Watcher.prototype.update = function update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true;
} else if (this.sync) {
this.run();
} else {
queueWatcher(this);// run
}
};
/**
* Depend on all deps collected by this watcher. new Dep depend ,dep depend Watcher addDep
*/
Watcher.prototype.depend = function depend () {
var i = this.deps.length;
while (i--) {
this.deps[i].depend();
}
};
/**
* Add a dependency to this directive.
*/
Watcher.prototype.addDep = function addDep (dep) {
var id = dep.id;
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id);
this.newDeps.push(dep);
if (!this.depIds.has(id)) {
dep.addSub(this);
}
}
};
총결산위 에서 말 한 것 은 편집장 이 소개 한 vue 에서 watch 와 coptute d 가 왜 데이터 의 변화 와 차이 점 을 감청 할 수 있 는 지 여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 면 메 시 지 를 남 겨 주세요.편집장 은 신속하게 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
만약 당신 이 본문 이 당신 에 게 도움 이 된다 고 생각한다 면,전 재 를 환영 합 니 다.번 거 로 우 시 겠 지만 출처 를 밝 혀 주 십시오.감사합니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Fastapi websocket 및 vue 3(Composition API)1부: FastAPI virtualenv 만들기(선택 사항) FastAPI 및 필요한 모든 것을 다음과 같이 설치하십시오. 생성main.py 파일 및 실행 - 브라우저에서 이 링크 열기http://127.0.0.1:...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.