這篇文章主要為大家展示了“vue中watch和computed能監(jiān)聽到數(shù)據(jù)改變的原因是什么”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“vue中watch和computed能監(jiān)聽到數(shù)據(jù)改變的原因是什么”這篇文章吧。
河?xùn)|ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書銷售渠道,可以享受市場價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!
先來個(gè)流程圖
首先在創(chuàng)建一個(gè)Vue應(yīng)用時(shí):
var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
Vue構(gòu)造函數(shù)源碼:
//創(chuàng)建Vue構(gòu)造函數(shù) 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和computed,并調(diào)用observe函數(shù)監(jiān)聽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中也會調(diào)用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、observe
observe在initState 時(shí)被調(diào)用,為vue實(shí)例的data屬性值創(chuàng)建getter、setter函數(shù),在setter中dep.depend會把watcher實(shí)例添加到Dep實(shí)例的subs屬性中,在getter中會調(diào)用dep.notify,調(diào)用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. * 該函數(shù)在initState中有調(diào)用 */ 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 全局變量指向的就是指向當(dāng)前正在解析生成的 Watcher //會執(zhí)行到dep.addSub,將Watcher添加到Dep對象的Watcher數(shù)組中 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();//如果數(shù)據(jù)被重新賦值了, 調(diào)用 Dep 的 notify 方法, 通知所有的 Watcher } }); }
2、Dep
Watcher的update方法是在new Dep的notify的方法中被調(diào)用的
/** * A dep is an observable that can have multiple * directives subscribing to it. */ var Dep = function Dep () { this.id = uid++; this.subs = []; }; //設(shè)置某個(gè)Watcher的依賴 //這里添加Dep.target,用來判斷是不是Watcher的構(gòu)造函數(shù)調(diào)用 //也就是其this.get調(diào)用 Dep.prototype.depend = function depend () { if (Dep.target) { Dep.target.addDep(this); } }; //在該方法中會觸發(fā)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、watch
初始化watch,函數(shù)中會調(diào)用createWatcher,createWatcher會調(diào)用$watch,$watch調(diào)用new Watcher實(shí)例。
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、computed
初始化computed,調(diào)用new Watcher(),并通過defineComputed函數(shù)將計(jì)算屬性掛載到vue實(shí)例上,使計(jì)算屬性可以在模板中使用
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的函數(shù) 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 ); } //組件定義的計(jì)算屬性已在 //組件原型。我們只需要定義定義的計(jì)算屬性 //在這里實(shí)例化。 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函數(shù),在模板獲取對應(yīng)computed數(shù)據(jù)時(shí)會調(diào)用 function createComputedGetter (key) { return function computedGetter () { var watcher = this._computedWatchers && this._computedWatchers[key]; if (watcher) { if (watcher.dirty) {//true watcher.evaluate();//該方法會調(diào)用watcher.get方法,也就是computed對應(yīng)的函數(shù) } if (Dep.target) { watcher.depend(); } return watcher.value } } }
通過以上代碼可以看到watch和computed都是通過new Watcher實(shí)例實(shí)現(xiàn)數(shù)據(jù)的監(jiān)聽的,但是computed的options中l(wèi)azy為true,這個(gè)參數(shù)導(dǎo)致它們走的是兩條不同路線。
computed:模板獲取數(shù)據(jù)時(shí),觸發(fā)其getter函數(shù),最終調(diào)用watcher.get,也就是調(diào)用對應(yīng)回調(diào)函數(shù)。
watch:模板獲取數(shù)據(jù)時(shí),觸發(fā)其getter函數(shù),將watcher添加到對應(yīng)的Dep.subs中,在之后setter被調(diào)用時(shí),Dep.notify通知所有watcher進(jìn)行update,最終調(diào)用watcher.cb,也就是調(diào)用對應(yīng)回調(diào)函數(shù)。
3、Watcher
構(gòu)造函數(shù)在是watch時(shí),會最后調(diào)用this.get,會觸發(fā)屬性的getter函數(shù),將該Watcher添加到Dep的subs中,用于通知數(shù)據(jù)變動時(shí)調(diào)用。
調(diào)用Watcher實(shí)例的update方法會觸發(fā)其run方法,run方法中會調(diào)用觸發(fā)函數(shù)。其depend方法會調(diào)用new Dep的depend方法,dep的depend會調(diào)用Watcher的addDep方法,最終會把該watcher實(shí)例添加到Dep的subs屬性中
/** *觀察者解析表達(dá)式,收集依賴項(xiàng), *并在表達(dá)式值更改時(shí)激發(fā)回調(diào)。 *這用于$watch()api和指令。 */ var Watcher = function Watcher ( vm, expOrFn, cb, options, isRenderWatcher ) { this.vm = vm; ...... this.cb = cb;//觸發(fā)函數(shù) 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會執(zhí)行Watcher.get }; /** * Scheduler job interface. * Will be called by the scheduler. * 該方法會執(zhí)行觸發(fā)函數(shù) */ 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. * 在方法中調(diào)用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);//該方法最終也會調(diào)用run方法 } }; /** * Depend on all deps collected by this watcher.會調(diào)用new Dep的depend方法,dep的depend會調(diào)用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和computed能監(jiān)聽到數(shù)據(jù)改變的原因是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
分享標(biāo)題:vue中watch和computed能監(jiān)聽到數(shù)據(jù)改變的原因是什么
本文地址:http://muchs.cn/article40/pgdeeo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、品牌網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、手機(jī)網(wǎng)站建設(shè)、品牌網(wǎng)站設(shè)計(jì)、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)