摘要:通知某屬性改變,遍歷所有的訂閱者,就是實(shí)例,然后調(diào)用實(shí)例的方法些方法最核心的部分就是通過調(diào)用給的每個(gè)屬性添加和方法。
vue生命周期圖示:
Vue.js最顯著的功能就是響應(yīng)式系統(tǒng),它是一個(gè)典型的MVVM框架,Model只是普通的Javascript對(duì)象,修改它則View會(huì)自動(dòng)更新,這種設(shè)計(jì)讓狀態(tài)管理變得非常簡單而直觀。
如何追蹤變化?我們先看一個(gè)簡單的例子:
count:{{times}}
運(yùn)行后,我們可以從頁面中看到,count后面的times每隔一秒遞增1,視圖一直在更新,在代碼中僅僅是通過setInterval方法每隔一秒來修改vm.times的值,并沒有任何dom操作,那么vue是怎么實(shí)現(xiàn)這個(gè)過程呢,我們通過一張圖來看下:
圖中的Model就是data方法返回的{times:1},View是最終在瀏覽器中顯示的DOM,模型通過Observer,Dep,Watcher,Directive等一系列對(duì)象的關(guān)聯(lián),最終和視圖建立起關(guān)系。總的來說,vue在些做了3件事:
通過Observer對(duì)data做監(jiān)聽,并提供了訂閱某個(gè)數(shù)據(jù)項(xiàng)變化的能力。
把template編譯成一段document fragment,然后解析其中的Directive,得到每一個(gè)Directive所依賴的數(shù)據(jù)項(xiàng)和update方法。
通過Watcher把上述2部分結(jié)合起來,即把Directive中的數(shù)據(jù)依賴通過Watcher訂閱在對(duì)應(yīng)數(shù)據(jù)的Observer的Dep上,當(dāng)數(shù)據(jù)變化時(shí),就會(huì)觸發(fā)Observer的Dep上的notify方法通知對(duì)應(yīng)的Watcher的update,進(jìn)而觸發(fā)Directive的update方法來更新dom視圖,最后達(dá)到模型和視圖關(guān)聯(lián)起來。
Observer我們來看下vue是如何給data對(duì)象添加Observer的,我們知道,vue的實(shí)例創(chuàng)建的過程會(huì)有一個(gè)生命周期,其中有一個(gè)過程就是調(diào)用vm.initData方法處理data選項(xiàng),代碼如下:
src/core/instance/state.js function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === "function" ? getData(data, vm) : data || {} if (!isPlainObject(data)) { data = {} process.env.NODE_ENV !== "production" && warn( "data functions should return an object: " + "https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function", vm ) } // proxy data on instance const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (process.env.NODE_ENV !== "production") { if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } if (props && hasOwn(props, key)) { process.env.NODE_ENV !== "production" && warn( `The data property "${key}" is already declared as a prop. ` + `Use prop default value instead.`, vm ) } else if (!isReserved(key)) { proxy(vm, `_data`, key) } } // observe data observe(data, true /* asRootData */) }
我們要注意下proxy方法,它的功能是遍歷data的key,把data上的屬性代理到vm實(shí)例上,proxy源碼如下:
const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) }
方法主要通過Object.defineProperty的getter和setter方法實(shí)現(xiàn)了代理,在 前面的例子中,我們調(diào)用vm.times就相當(dāng)于訪問了vm._data.times.
在initData的最后,我們調(diào)用了observer(data,this)來對(duì)data做監(jiān)聽,observer的源碼定義如下:
src/core/observer/index.js /** *嘗試創(chuàng)建一個(gè)值的觀察者實(shí)例, *如果成功觀察到新的觀察者, *或現(xiàn)有的觀察者,如果該值已經(jīng)有一個(gè)。 */ export function observe (value: any, asRootData: ?boolean): Observer | void { if (!isObject(value) || value instanceof VNode) { return } let ob: Observer | void if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) { ob = value.__ob__ } else if ( observerState.shouldConvert && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value) } if (asRootData && ob) { ob.vmCount++ } return ob }
它會(huì)首先判斷value是否已經(jīng)添加了_ob_屬性,它是一個(gè)Observer對(duì)象的實(shí)例,如果是就直接用,否則在value滿足一些條件(數(shù)組或?qū)ο?,可擴(kuò)展,非vue組件等)的情況下創(chuàng)建一個(gè)Observer對(duì)象,下面看下Observer這個(gè)類的源碼:
class Observer { value: any; dep: Dep; vmCount: number; // 將這個(gè)對(duì)象作為根$data的vm的數(shù)量。 constructor (value: any) { this.value = value this.dep = new Dep() this.vmCount = 0 def(value, "__ob__", this) if (Array.isArray(value)) { const augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) this.observeArray(value) } else { this.walk(value) } } /** *遍歷每個(gè)屬性并將其轉(zhuǎn)換為擁有g(shù)etter / setter。這個(gè)方法應(yīng)該只在什么時(shí)候調(diào)用。當(dāng)obj是對(duì)象。 */ walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } /** 觀察數(shù)組項(xiàng)的列表。 */ observeArray (items: Array) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } } }
這個(gè)構(gòu)造函數(shù)主要做了這么幾件事,首先創(chuàng)建了一個(gè)Dep對(duì)象實(shí)例,然后把自身this添加到value的_ob_屬性上,最后對(duì)value的類型進(jìn)行判斷,如果是數(shù)組則觀察數(shù)組,否則觀察單個(gè)元素。obsersverArray方法就是對(duì)數(shù)組進(jìn)行遍歷,遞歸調(diào)用observer方法,最終都會(huì)調(diào)用walk方法觀察單個(gè)元素。walk方法就是對(duì)obj的key進(jìn)行遍歷。然后調(diào)用了defineReactive,把要觀察的data對(duì)象的每個(gè)屬性都賦予getter和setter方法,這樣一旦屬性被訪問或者更新,我們就可以追蹤到這些變化。源碼如下:
/** * 在對(duì)象上定義一個(gè)反應(yīng)性屬性 setter,getter。 */ export function defineReactive ( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean ) { const dep = new Dep() const property = Object.getOwnPropertyDescriptor(obj, key) if (property && property.configurable === false) { return } // 滿足預(yù)定義的getter / setter const getter = property && property.get const setter = property && property.set let childOb = !shallow && observe(val) // 在這里添加setter,getter。 Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() if (Array.isArray(value)) { dependArray(value) } } } return value }, set: function reactiveSetter (newVal) { const 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 (process.env.NODE_ENV !== "production" && customSetter) { customSetter() } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) // 通知data某屬性改變,遍歷所有的訂閱者,就是watcher實(shí)例,然后調(diào)用watcher實(shí)例的update方法 dep.notify() } }) }
些方法最核心的部分就是通過調(diào)用Object.defineProperty給data的每個(gè)屬性添加getter和setter方法。當(dāng)data的某個(gè)屬性被訪問時(shí),則會(huì)調(diào)用getter方法,判斷當(dāng)Dep.target不為空時(shí)調(diào)用dep.depend和childOb.dep.depend方法做依賴收集,如果訪問的屬性是一個(gè)數(shù)組則會(huì)遍歷這個(gè)數(shù)組收集數(shù)組元素的依賴,當(dāng)改變data的屬性時(shí),則會(huì)調(diào)用setter方法,這時(shí)調(diào)用dep.notify方法進(jìn)行通知。其中用到的Dep類是一個(gè)簡單的觀察者模式的實(shí)現(xiàn),然后我們看下源碼:
src/core/observer/dep.js class Dep { static target: ?Watcher; id: number; subs: Arraywatcher; constructor () { this.id = uid++ this.subs = [] // 用來存儲(chǔ)所有訂閱它的watcher } addSub (sub: Watcher) { this.subs.push(sub) } removeSub (sub: Watcher) { remove(this.subs, sub) } depend () { if (Dep.target) { // Dep.target表示當(dāng)前正在計(jì)算的watcher,是全局唯一的,同一時(shí)間只能有一個(gè)watcher被計(jì)算 // 把當(dāng)前Dep的實(shí)例添加到當(dāng)前正在計(jì)算的watcher依賴中 Dep.target.addDep(this) } } // 遍歷所有的的訂閱watcher,調(diào)用它們的update方法。 notify () { // 首先穩(wěn)定用戶列表。 const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } }
src/core/observer/watcher.js let uid = 0 /** * 觀察者解析表達(dá)式,收集依賴項(xiàng), *當(dāng)表達(dá)式值發(fā)生變化時(shí)觸發(fā)回調(diào)。 這用于$watch() api和指令。 */ export default class Watcher { constructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: Object ) { this.vm = vm vm._watchers.push(this) // options if (options) { this.deep = !!options.deep this.user = !!options.user this.lazy = !!options.lazy this.sync = !!options.sync } else { this.deep = this.user = this.lazy = this.sync = false } this.cb = cb this.id = ++uid // uid 為批處理 this.active = true this.dirty = this.lazy // for lazy watchers this.deps = [] this.newDeps = [] this.depIds = new Set() this.newDepIds = new Set() this.expression = process.env.NODE_ENV !== "production" ? expOrFn.toString() : "" // parse expression for getter if (typeof expOrFn === "function") { this.getter = expOrFn } else { this.getter = parsePath(expOrFn) if (!this.getter) { this.getter = function () {} process.env.NODE_ENV !== "production" && warn( `Failed watching path: "${expOrFn}" ` + "Watcher only accepts simple dot-delimited paths. " + "For full control, use a function instead.", vm ) } } this.value = this.lazy ? undefined : this.get() } /** * 評(píng)估getter并重新收集依賴項(xiàng)。 */ get () { pushTarget(this) let value const 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 { // “觸摸”每個(gè)屬性,所以它們都被跟蹤。 // 對(duì)深度觀察的依賴。 if (this.deep) { traverse(value) } popTarget() this.cleanupDeps() } return value } /** * Add a dependency to this directive.把dep添加到watcher實(shí)例的依賴中,同時(shí)通過 dep.addsup(this)把watcher實(shí)例添加到dep的訂閱者中。 */ addDep (dep: Dep) { const id = dep.id if (!this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep) if (!this.depIds.has(id)) { dep.addSub(this) } } } /** * Clean up for dependency collection. */ cleanupDeps () { let i = this.deps.length while (i--) { const dep = this.deps[i] if (!this.newDepIds.has(dep.id)) { dep.removeSub(this) } } let tmp = this.depIds this.depIds = this.newDepIds this.newDepIds = tmp this.newDepIds.clear() tmp = this.deps this.deps = this.newDeps this.newDeps = tmp this.newDeps.length = 0 } /** *用戶界面。時(shí)將調(diào)用一個(gè)依賴的變化。 */ update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { // 調(diào)用,把watcher實(shí)例推入隊(duì)列中,延遲this.run調(diào)用的時(shí)機(jī)。 queueWatcher(this) } } /** * 調(diào)度器的工作界面。會(huì)被調(diào)度器調(diào)用。 * 再次對(duì)watcher進(jìn)行求值,重新收集依賴,接下來判斷求值結(jié)果和之前value的關(guān)系,如果不變,則什么也不做 * 此方法是directive實(shí)例創(chuàng)建watcher時(shí)傳入的,它對(duì)應(yīng)相關(guān)指令的update方法來真實(shí)更新dom。這樣就完成了數(shù)據(jù)更新到對(duì)應(yīng)視圖的變化過程。 * watcher把observer和directive關(guān)聯(lián)起來,實(shí)現(xiàn)了數(shù)據(jù)一旦更新,視圖就自動(dòng)變化的效果。利用object.defineProperty實(shí)現(xiàn)了數(shù)據(jù)和視圖的綁定 */ run () { if (this.active) { const value = this.get() if ( value !== this.value || // 深入觀察和觀察對(duì)象/陣列甚至應(yīng)該開火。當(dāng)值相等時(shí),因?yàn)橹悼梢浴S型蛔儭? isObject(value) || this.deep ) { // set new value const 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) } } } } /** * 評(píng)估觀察者的價(jià)值。 這只會(huì)被稱為懶惰的觀察者。 */ evaluate () { this.value = this.get() // 對(duì)watcher進(jìn)行求值,同時(shí)收集依賴 this.dirty = false // 不會(huì)再對(duì)watcher求值,也不會(huì)再訪問計(jì)算屬性的getter方法了 } /** * 要看這個(gè)觀察者收集的所有數(shù)據(jù)。 */ depend () { let i = this.deps.length while (i--) { this.deps[i].depend() } } /** * Remove self from all dependencies" subscriber list. */ teardown () { if (this.active) { // remove self from vm"s watcher list // this is a somewhat expensive operation so we skip it // if the vm is being destroyed. if (!this.vm._isBeingDestroyed) { remove(this.vm._watchers, this) } let i = this.deps.length while (i--) { this.deps[i].removeSub(this) } this.active = false } } }
Dep實(shí)例在初始化watcher時(shí),會(huì)傳入指令的expression.在前面的例子中expression是times.get方法的功能是對(duì)當(dāng)前watcher進(jìn)行求值,收集依賴關(guān)系,設(shè)置Dep.target為當(dāng)前watcher的實(shí)例,this.getter.call(vm,vm),這個(gè)方法相當(dāng)于獲取vm.times,這樣就觸發(fā)了對(duì)象的getter.我們之前給data添加Observer時(shí),通過上面defineReactive/Object.defineProperty給data對(duì)象的每一個(gè)屬性添加getter和setter了.
src/core/observer/index.js function defineReactive (obj,key,val,customSetter,shallow) { // 在這里添加setter,getter。 Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() if (Array.isArray(value)) { dependArray(value) } } } return value } }
當(dāng)獲取vm.times時(shí),會(huì)執(zhí)行到get方法體內(nèi),由于我們?cè)谥耙呀?jīng)設(shè)置了Dep.target為當(dāng)前Watcher實(shí)例,所以接下來就調(diào)用dep.depend()完成收集,它實(shí)際上是執(zhí)行了Dep.target.addDep(this),相當(dāng)于執(zhí)行了Watcher實(shí)例的addDep方法,把Dep添加到Watcher實(shí)例的依賴中。
src/observer/watcher.js addDep (dep: Dep) { const id = dep.id if (!this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep) if (!this.depIds.has(id)) { dep.addSub(this) } } }
addDep是把dep添加到Watcher實(shí)例的依賴中,同時(shí)又通過dep.addSup(this)把Watcher實(shí)例添加到dep的訂閱者中
src/observer/dep.js addSub (sub: Watcher) { this.subs.push(sub) }
至此,指令完成了依賴收集,并且通過Watcher完成了對(duì)數(shù)據(jù)變化的訂閱。
我們?cè)倏聪?,?dāng)data發(fā)生變化時(shí),視圖是如何自動(dòng)更新的,在前面的例子中,我們setInterval每隔一秒執(zhí)行一次vm.times++,數(shù)據(jù)改變會(huì)觸發(fā)對(duì)象的setter,執(zhí)行set方法體的代碼。
src/core/observer/index.js function defineReactive (obj,key,val,customSetter,shallow) { // 在這里添加setter,getter。 Object.defineProperty(obj, key, { enumerable: true, configurable: true, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val if (newVal === value || (newVal !== newVal && value !== value)) { return } if (process.env.NODE_ENV !== "production" && customSetter) { customSetter() } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) // 通知data某屬性改變,遍歷所有的訂閱者,就是watcher實(shí)例,然后調(diào)用watcher實(shí)例的update方法 dep.notify() } }
src/observer/watcher.js update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this)// 調(diào)用,把watcher實(shí)例推入隊(duì)列中,延遲this.run調(diào)用的時(shí)機(jī)。 } }
src/core/observer/scheduler.js /** * 把一個(gè)觀察者watcher推入觀察者隊(duì)列。 *將跳過具有重復(fù)id的作業(yè),除非它是。 *當(dāng)隊(duì)列被刷新時(shí)被推。 * 通過nextTick在下一個(gè)事件循環(huán)周期處理watcher隊(duì)列,是一種優(yōu)化手段。因?yàn)槿绻瑫r(shí)觀察的數(shù)據(jù)多次變化,比如同步執(zhí)行3次vm.time++,同時(shí)調(diào)用就會(huì)觸發(fā)3次dom操作 * 而推入隊(duì)列中等待下一個(gè)事件循環(huán)周期再操作隊(duì)列里的watcher,因?yàn)槭峭粋€(gè)watcher,它只會(huì)調(diào)用一次watcher.run,從而只觸發(fā)一次dom操作。 */ export function queueWatcher (watcher: Watcher) { const id = watcher.id if (has[id] == null) { has[id] = true if (!flushing) { queue.push(watcher) } else { // 如果已經(jīng)刷新,則根據(jù)其id將監(jiān)視器拼接起來。 // 如果已經(jīng)超過了它的id,它將會(huì)立即運(yùn)行。 let i = queue.length - 1 while (i > index && queue[i].id > watcher.id) { i-- } queue.splice(i + 1, 0, watcher) } // 隊(duì)列的沖 if (!waiting) { waiting = true nextTick(flushSchedulerQueue) } } }
function flushSchedulerQueue () { flushing = true let watcher, id // 在刷新前排序隊(duì)列。 // 這確保: // 1。組件由父元素更新為子元素。(因?yàn)楦改缚偸窃诤⒆用媲皠?chuàng)建) // 2。組件的用戶觀察者在它的呈現(xiàn)觀察者之前運(yùn)行(因?yàn)橛脩粲^察者是在渲染觀察者之前創(chuàng)建的 // 3。如果組件在父組件的監(jiān)視程序運(yùn)行期間被銷毀, 它的觀察者可以跳過。 queue.sort((a, b) => a.id - b.id) // 不要緩存長度,因?yàn)榭赡軙?huì)有更多的觀察者被推。 // 當(dāng)我們運(yùn)行現(xiàn)有的觀察者時(shí)。遍歷queue中watcher的run方法 for (index = 0; index < queue.length; index++) { watcher = queue[index] id = watcher.id has[id] = null watcher.run() // in dev build, check and stop circular updates. if (process.env.NODE_ENV !== "production" && has[id] != null) { circular[id] = (circular[id] || 0) + 1 if (circular[id] > MAX_UPDATE_COUNT) { warn( "You may have an infinite update loop " + ( watcher.user ? `in watcher with expression "${watcher.expression}"` : `in a component render function.` ), watcher.vm ) break } } } // keep copies of post queues before resetting state const activatedQueue = activatedChildren.slice() const updatedQueue = queue.slice() resetSchedulerState() // call component updated and activated hooks callActivatedHooks(activatedQueue) callUpdatedHooks(updatedQueue) // devtool hook /* istanbul ignore if */ if (devtools && config.devtools) { devtools.emit("flush") } }
遍歷queue中Watcher的run方法,
src/core/observer/watcher.js run () { if (this.active) { const value = this.get() if ( value !== this.value || // 深入觀察和觀察對(duì)象/陣列甚至應(yīng)該開火。當(dāng)值相等時(shí),因?yàn)橹悼梢浴S型蛔儭? isObject(value) || this.deep ) { // set new value const 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) } } } }
run方法再次對(duì)Watcher求值,重新收集依賴,接下來判斷求值結(jié)果和之前value的關(guān)系,如果不變則什么也不做,如果變了則調(diào)用this.cb.call(this.vm,value,oldValue)方法,這個(gè)方法是Directive實(shí)例創(chuàng)建watcher時(shí)傳入的,它對(duì)應(yīng)相關(guān)指令的update方法來真實(shí)更新 DOM,這樣就完成了數(shù)據(jù)更新到對(duì)應(yīng)視圖的變化過程。Watcher巧妙的把Observer和Directive關(guān)聯(lián)起來,實(shí)現(xiàn)了數(shù)據(jù)一旦更新,視圖就會(huì)自動(dòng)變化的效果,vue利用了Object.defineProperty這個(gè)核心技術(shù)實(shí)現(xiàn)了數(shù)據(jù)和視圖的綁定。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/93030.html
摘要:問題為什么修改即可觸發(fā)更新和的關(guān)聯(lián)關(guān)系官方介紹的官網(wǎng)文檔,對(duì)響應(yīng)式屬性的原理有一個(gè)介紹。因此本文在源碼層面,對(duì)響應(yīng)式原理進(jìn)行梳理,對(duì)關(guān)鍵步驟進(jìn)行解析。 描述 ?我們通過一個(gè)簡單的 Vue應(yīng)用 來演示 Vue的響應(yīng)式屬性: html: {{message}} js: let vm = new Vue({ el: #ap...
摘要:所以我今后打算把每一個(gè)內(nèi)容分成白話版和源碼版。有什么錯(cuò)誤的地方,感謝大家能夠指出響應(yīng)式系統(tǒng)我們都知道,只要在實(shí)例中聲明過的數(shù)據(jù),那么這個(gè)數(shù)據(jù)就是響應(yīng)式的。什么是響應(yīng)式,也即是說,數(shù)據(jù)發(fā)生改變的時(shí)候,視圖會(huì)重新渲染,匹配更新為最新的值。 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 V...
摘要:在讀取訪問器屬性時(shí),就會(huì)調(diào)用函數(shù),該函數(shù)負(fù)責(zé)返回有效的值在寫入訪問器屬性時(shí),會(huì)調(diào)用函數(shù)并傳入新值,該函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù),但是這兩個(gè)函數(shù)不一定非要同時(shí)存在。 前言 Vue最明顯的特性之一便是它的響應(yīng)式系統(tǒng),其數(shù)據(jù)模型即是普通的 JavaScript 對(duì)象。而當(dāng)你讀取或?qū)懭胨鼈儠r(shí),視圖便會(huì)進(jìn)行響應(yīng)操作。文章簡要闡述下其實(shí)現(xiàn)原理,如有錯(cuò)誤,還請(qǐng)不吝指正。個(gè)人博客鏈接:hiybm.cn ...
寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧 【Vue原理】Props - 源碼版 今天記錄 Props 源碼流程,哎,這東西,就算是研究過了,也真是會(huì)隨著時(shí)間慢慢忘記的。 幸好我做...
摘要:對(duì)象用戶看到的對(duì)象用戶看到的是這個(gè)對(duì)象即是實(shí)際使用的對(duì)象實(shí)際使用的對(duì)象復(fù)制更新相應(yīng)的代碼實(shí)現(xiàn)對(duì)象代理響應(yīng)式原理前提官網(wǎng)說過,限于現(xiàn)代瀏覽器限制,無法監(jiān)測通過這種方式添加的屬性,所以,他的響應(yīng)式是建立在實(shí)例化對(duì)象的時(shí)候,預(yù)定義屬性的基礎(chǔ)上的。 1. Vue 對(duì)象 1.1 用戶看到的對(duì)象 var app = new Vue({ el: #app , /* * 用...
摘要:原型方法通過原型方法方法來掛載實(shí)例。當(dāng)響應(yīng)式屬性發(fā)生變化時(shí),會(huì)通知依賴列表中的對(duì)象進(jìn)行更新。此時(shí),對(duì)象執(zhí)行方法,重新渲染節(jié)點(diǎn)。在執(zhí)行過程中,如果需要讀取響應(yīng)式屬性,則會(huì)觸發(fā)響應(yīng)式屬性的??偨Y(jié)響應(yīng)式屬性的原理 vue實(shí)例 初始化 完成以后,接下來就要進(jìn)行 掛載。 vue實(shí)例掛載,即為將vue實(shí)例對(duì)應(yīng)的 template模板,渲染成 Dom節(jié)點(diǎn)。 原型方法 - $mount ? 通過原...
閱讀 3517·2021-11-22 12:00
閱讀 750·2019-08-29 13:24
閱讀 2967·2019-08-29 11:31
閱讀 2679·2019-08-26 14:00
閱讀 3269·2019-08-26 11:42
閱讀 2536·2019-08-23 18:31
閱讀 866·2019-08-23 18:27
閱讀 2907·2019-08-23 16:58