摘要:在實(shí)例化的過程中,提供的構(gòu)造函數(shù)就使用我們傳入的去完成數(shù)據(jù)的依賴管理,初始化的過程只有一次,但是在你自己的程序當(dāng)中,數(shù)據(jù)的依賴管理的次數(shù)不止一次。當(dāng)響應(yīng)式屬性調(diào)用函數(shù)時(shí),通過方法去遍歷所有的依賴,調(diào)用去完成數(shù)據(jù)的動(dòng)態(tài)響應(yīng)。
首先讓我們從最簡(jiǎn)單的一個(gè)實(shí)例Vue入手:
const app = new Vue({ // options 傳入一個(gè)選項(xiàng)obj.這個(gè)obj即對(duì)于這個(gè)vue實(shí)例的初始化 })
通過查閱文檔,我們可以知道這個(gè)options可以接受:
選項(xiàng)/數(shù)據(jù)
data
props
propsData(方便測(cè)試使用)
computed
methods
watch
選項(xiàng) / DOM
選項(xiàng) / 生命周期鉤子
選項(xiàng) / 資源
選項(xiàng) / 雜項(xiàng)
具體未展開的內(nèi)容請(qǐng)自行查閱相關(guān)文檔,接下來讓我們來看看傳入的選項(xiàng)/數(shù)據(jù)是如何管理數(shù)據(jù)之間的相互依賴的。
const app = new Vue({ el: "#app", props: { a: { type: Object, default () { return { key1: "a", key2: { a: "b" } } } } }, data: { msg1: "Hello world!", arr: { arr1: 1 } }, watch: { a (newVal, oldVal) { console.log(newVal, oldVal) } }, methods: { go () { console.log("This is simple demo") } } })
我們使用Vue這個(gè)構(gòu)造函數(shù)去實(shí)例化了一個(gè)vue實(shí)例app。傳入了props, data, watch, methods等屬性。在實(shí)例化的過程中,Vue提供的構(gòu)造函數(shù)就使用我們傳入的options去完成數(shù)據(jù)的依賴管理,初始化的過程只有一次,但是在你自己的程序當(dāng)中,數(shù)據(jù)的依賴管理的次數(shù)不止一次。
那Vue的構(gòu)造函數(shù)到底是怎么實(shí)現(xiàn)的呢?Vue
// 構(gòu)造函數(shù) function Vue (options) { if (process.env.NODE_ENV !== "production" && !(this instanceof Vue)) { warn("Vue is a constructor and should be called with the `new` keyword") } this._init(options) } // 對(duì)Vue這個(gè)class進(jìn)行mixin,即在原型上添加方法 // Vue.prototype.* = function () {} initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue)
當(dāng)我們調(diào)用new Vue的時(shí)候,事實(shí)上就調(diào)用的Vue原型上的_init方法.
// 原型上提供_init方法,新建一個(gè)vue實(shí)例并傳入options參數(shù) Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ let startTag, endTag // a flag to avoid this being observed vm._isVue = true // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { // 將傳入的這些options選項(xiàng)掛載到vm.$options屬性上 vm.$options = mergeOptions( // components/filter/directive resolveConstructorOptions(vm.constructor), // this._init()傳入的options options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== "production") { initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm // 自身的實(shí)例 // 接下來所有的操作都是在這個(gè)實(shí)例上添加方法 initLifecycle(vm) // lifecycle初始化 initEvents(vm) // events初始化 vm._events, 主要是提供vm實(shí)例上的$on/$emit/$off/$off等方法 initRender(vm) // 初始化渲染函數(shù),在vm上綁定$createElement方法 callHook(vm, "beforeCreate") // 鉤子函數(shù)的執(zhí)行, beforeCreate initInjections(vm) // resolve injections before data/props initState(vm) // Observe data添加對(duì)data的監(jiān)聽, 將data轉(zhuǎn)化為getters/setters initProvide(vm) // resolve provide after data/props callHook(vm, "created") // 鉤子函數(shù)的執(zhí)行, created // vm掛載的根元素 if (vm.$options.el) { vm.$mount(vm.$options.el) } }
其中在this._init()方法中調(diào)用initState(vm),完成對(duì)vm這個(gè)實(shí)例的數(shù)據(jù)的監(jiān)聽,也是本文所要展開說的具體內(nèi)容。
export function initState (vm: Component) { // 首先在vm上初始化一個(gè)_watchers數(shù)組,緩存這個(gè)vm上的所有watcher vm._watchers = [] // 獲取options,包括在new Vue傳入的,同時(shí)還包括了Vue所繼承的options const opts = vm.$options // 初始化props屬性 if (opts.props) initProps(vm, opts.props) // 初始化methods屬性 if (opts.methods) initMethods(vm, opts.methods) // 初始化data屬性 if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } // 初始化computed屬性 if (opts.computed) initComputed(vm, opts.computed) // 初始化watch屬性 if (opts.watch) initWatch(vm, opts.watch) }initProps
我們?cè)趯?shí)例化app的時(shí)候,在構(gòu)造函數(shù)里面?zhèn)魅氲?b>options中有props屬性:
props: { a: { type: Object, default () { return { key1: "a", key2: { a: "b" } } } } }
function initProps (vm: Component, propsOptions: Object) { // propsData主要是為了方便測(cè)試使用 const propsData = vm.$options.propsData || {} // 新建vm._props對(duì)象,可以通過app實(shí)例去訪問 const props = vm._props = {} // cache prop keys so that future props updates can iterate using Array // instead of dynamic object key enumeration. // 緩存的prop key const keys = vm.$options._propKeys = [] const isRoot = !vm.$parent // root instance props should be converted observerState.shouldConvert = isRoot for (const key in propsOptions) { // this._init傳入的options中的props屬性 keys.push(key) // 注意這個(gè)validateProp方法,不僅完成了prop屬性類型驗(yàn)證的,同時(shí)將prop的值都轉(zhuǎn)化為了getter/setter,并返回一個(gè)observer const value = validateProp(key, propsOptions, propsData, vm) // 將這個(gè)key對(duì)應(yīng)的值轉(zhuǎn)化為getter/setter defineReactive(props, key, value) // static props are already proxied on the component"s prototype // during Vue.extend(). We only need to proxy props defined at // instantiation here. // 如果在vm這個(gè)實(shí)例上沒有key屬性,那么就通過proxy轉(zhuǎn)化為proxyGetter/proxySetter, 并掛載到vm實(shí)例上,可以通過app._props[key]這種形式去訪問 if (!(key in vm)) { proxy(vm, `_props`, key) } } observerState.shouldConvert = true }
接下來看下validateProp(key, propsOptions, propsData, vm)方法內(nèi)部到底發(fā)生了什么。
export function validateProp ( key: string, propOptions: Object, // $options.props屬性 propsData: Object, // $options.propsData屬性 vm?: Component ): any { const prop = propOptions[key] // 如果在propsData測(cè)試props上沒有緩存的key const absent = !hasOwn(propsData, key) let value = propsData[key] // 處理boolean類型的數(shù)據(jù) // handle boolean props if (isType(Boolean, prop.type)) { if (absent && !hasOwn(prop, "default")) { value = false } else if (!isType(String, prop.type) && (value === "" || value === hyphenate(key))) { value = true } } // check default value if (value === undefined) { // default屬性值,是基本類型還是function // getPropsDefaultValue見下面第一段代碼 value = getPropDefaultValue(vm, prop, key) // since the default value is a fresh copy, // make sure to observe it. const prevShouldConvert = observerState.shouldConvert observerState.shouldConvert = true // 將value的所有屬性轉(zhuǎn)化為getter/setter形式 // 并添加value的依賴 // observe方法的分析見下面第二段代碼 observe(value) observerState.shouldConvert = prevShouldConvert } if (process.env.NODE_ENV !== "production") { assertProp(prop, key, value, vm, absent) } return value }
// 獲取prop的默認(rèn)值 function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): any { // no default, return undefined // 如果沒有default屬性的話,那么就返回undefined if (!hasOwn(prop, "default")) { return undefined } const def = prop.default // the raw prop value was also undefined from previous render, // return previous default value to avoid unnecessary watcher trigger if (vm && vm.$options.propsData && vm.$options.propsData[key] === undefined && vm._props[key] !== undefined) { return vm._props[key] } // call factory function for non-Function types // a value is Function if its prototype is function even across different execution context // 如果是function 則調(diào)用def.call(vm) // 否則就返回default屬性對(duì)應(yīng)的值 return typeof def === "function" && getType(prop.type) !== "Function" ? def.call(vm) : def }
Vue提供了一個(gè)observe方法,在其內(nèi)部實(shí)例化了一個(gè)Observer類,并返回Observer的實(shí)例。每一個(gè)Observer實(shí)例對(duì)應(yīng)記錄了props中這個(gè)的default value的所有依賴(僅限object類型),這個(gè)Observer實(shí)際上就是一個(gè)觀察者,它維護(hù)了一個(gè)數(shù)組this.subs = []用以收集相關(guān)的subs(訂閱者)(即這個(gè)觀察者的依賴)。通過將default value轉(zhuǎn)化為getter/setter形式,同時(shí)添加一個(gè)自定義__ob__屬性,這個(gè)屬性就對(duì)應(yīng)Observer實(shí)例。
說起來有點(diǎn)繞,還是讓我們看看我們給的demo里傳入的options配置:
props: { a: { type: Object, default () { return { key1: "a", key2: { a: "b" } } } } }
在往上數(shù)的第二段代碼里面的方法obervse(value),即對(duì){key1: "a", key2: {a: "b"}}進(jìn)行依賴的管理,同時(shí)將這個(gè)obj所有的屬性值都轉(zhuǎn)化為getter/setter形式。此外,Vue還會(huì)將props屬性都代理到vm實(shí)例上,通過vm.key1,vm.key2就可以訪問到這個(gè)屬性。
此外,還需要了解下在Vue中管理依賴的一個(gè)非常重要的類: Dep
export default class Dep { constructor () { this.id = uid++ this.subs = [] } addSub () {...} // 添加訂閱者(依賴) removeSub () {...} // 刪除訂閱者(依賴) depend () {...} // 檢查當(dāng)前Dep.target是否存在以及判斷這個(gè)watcher已經(jīng)被添加到了相應(yīng)的依賴當(dāng)中,如果沒有則添加訂閱者(依賴),如果已經(jīng)被添加了那么就不做處理 notify () {...} // 通知訂閱者(依賴)更新 }
在Vue的整個(gè)生命周期當(dāng)中,你所定義的響應(yīng)式的數(shù)據(jù)上都會(huì)綁定一個(gè)Dep實(shí)例去管理其依賴。它實(shí)際上就是觀察者和訂閱者聯(lián)系的一個(gè)橋梁。
剛才談到了對(duì)于依賴的管理,它的核心之一就是觀察者Observer這個(gè)類:
export class Observer { value: any; dep: Dep; vmCount: number; // number of vms that has this object as root $data constructor (value: any) { this.value = value // dep記錄了和這個(gè)value值的相關(guān)依賴 this.dep = new Dep() this.vmCount = 0 // value其實(shí)就是vm._data, 即在vm._data上添加__ob__屬性 def(value, "__ob__", this) // 如果是數(shù)組 if (Array.isArray(value)) { // 首先判斷是否能使用__proto__屬性 const augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) // 遍歷數(shù)組,并將obj類型的屬性改為getter/setter實(shí)現(xiàn) this.observeArray(value) } else { // 遍歷obj上的屬性,將每個(gè)屬性改為getter/setter實(shí)現(xiàn) this.walk(value) } } /** * Walk through each property and convert them into * getter/setters. This method should only be called when * value type is Object. */ // 將每個(gè)property對(duì)應(yīng)的屬性都轉(zhuǎn)化為getter/setters,只能是當(dāng)這個(gè)value的類型為Object時(shí) walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } /** * Observe a list of Array items. */ // 監(jiān)聽array中的item observeArray (items: Array) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } } }
walk方法里面調(diào)用defineReactive方法:通過遍歷這個(gè)object的key,并將對(duì)應(yīng)的value轉(zhuǎn)化為getter/setter形式,通過閉包維護(hù)一個(gè)dep,在getter方法當(dāng)中定義了這個(gè)key是如何進(jìn)行依賴的收集,在setter方法中定義了當(dāng)這個(gè)key對(duì)應(yīng)的值改變后,如何完成相關(guān)依賴數(shù)據(jù)的更新。但是從源碼當(dāng)中,我們卻發(fā)現(xiàn)當(dāng)getter函數(shù)被調(diào)用的時(shí)候并非就一定會(huì)完成依賴的收集,其中還有一層判斷,就是Dep.target是否存在。
/** * Define a reactive property on an Object. */ export function defineReactive ( obj: Object, key: string, val: any, customSetter?: Function ) { // 每個(gè)屬性新建一個(gè)dep實(shí)例,管理這個(gè)屬性的依賴 const dep = new Dep() // 或者屬性描述符 const property = Object.getOwnPropertyDescriptor(obj, key) // 如果這個(gè)屬性是不可配的,即無法更改 if (property && property.configurable === false) { return } // cater for pre-defined getter/setters const getter = property && property.get const setter = property && property.set // 遞歸去將val轉(zhuǎn)化為getter/setter // childOb將子屬性也轉(zhuǎn)化為Observer let childOb = observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, // 定義getter -->> reactiveGetter get: function reactiveGetter () { const value = getter ? getter.call(obj) : val // 定義相應(yīng)的依賴 if (Dep.target) { // Dep.target.addDep(this) // 即添加watch函數(shù) // dep.depend()及調(diào)用了dep.addSub()只不過中間需要判斷是否這個(gè)id的dep已經(jīng)被包含在內(nèi)了 dep.depend() // childOb也添加依賴 if (childOb) { childOb.dep.depend() } if (Array.isArray(value)) { dependArray(value) } } return value }, // 定義setter -->> reactiveSetter set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } if (setter) { setter.call(obj, newVal) } else { val = newVal } // 對(duì)得到的新值進(jìn)行observe childOb = observe(newVal) // 相應(yīng)的依賴進(jìn)行更新 dep.notify() } }) }
在上文中提到了Dep類是鏈接觀察者和訂閱者的橋梁。同時(shí)在Dep的實(shí)現(xiàn)當(dāng)中還有一個(gè)非常重要的屬性就是Dep.target,它事實(shí)就上就是一個(gè)訂閱者,只有當(dāng)Dep.target(訂閱者)存在的時(shí)候,調(diào)用屬性的getter函數(shù)的時(shí)候才能完成依賴的收集工作。
Dep.target = null const targetStack = [] export function pushTarget (_target: Watcher) { if (Dep.target) targetStack.push(Dep.target) Dep.target = _target } export function popTarget () { Dep.target = targetStack.pop() }
那么Vue是如何來實(shí)現(xiàn)訂閱者的呢?Vue里面定義了一個(gè)類: Watcher,在Vue的整個(gè)生命周期當(dāng)中,會(huì)有4類地方會(huì)實(shí)例化Watcher:
Vue實(shí)例化的過程中有watch選項(xiàng)
Vue實(shí)例化的過程中有computed計(jì)算屬性選項(xiàng)
Vue原型上有掛載$watch方法: Vue.prototype.$watch,可以直接通過實(shí)例調(diào)用this.$watch方法
Vue生成了render函數(shù),更新視圖時(shí)
constructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: Object ) { // 緩存這個(gè)實(shí)例vm this.vm = vm // vm實(shí)例中的_watchers中添加這個(gè)watcher 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 for batching this.active = true this.dirty = this.lazy // for lazy watchers .... // parse expression for getter if (typeof expOrFn === "function") { this.getter = expOrFn } else { this.getter = parsePath(expOrFn) if (!this.getter) { this.getter = function () {} } } // 通過get方法去獲取最新的值 // 如果lazy為true, 初始化的時(shí)候?yàn)閡ndefined this.value = this.lazy ? undefined : this.get() } get () {...} addDep () {...} update () {...} run () {...} evaluate () {...} run () {...}
Watcher接收的參數(shù)當(dāng)中expOrFn定義了用以獲取watcher的getter函數(shù)。expOrFn可以有2種類型:string或function.若為string類型,首先會(huì)通過parsePath方法去對(duì)string進(jìn)行分割(僅支持.號(hào)形式的對(duì)象訪問)。在除了computed選項(xiàng)外,其他幾種實(shí)例化watcher的方式都是在實(shí)例化過程中完成求值及依賴的收集工作:this.value = this.lazy ? undefined : this.get().在Watcher的get方法中:
!!!前方高能
get () { // pushTarget即設(shè)置當(dāng)前的需要被執(zhí)行的watcher pushTarget(this) let value const vm = this.vm if (this.user) { try { // $watch(function () {}) // 調(diào)用this.getter的時(shí)候,觸發(fā)了屬性的getter函數(shù) // 在getter中進(jìn)行了依賴的管理 value = this.getter.call(vm, vm) console.log(value) } catch (e) { handleError(e, vm, `getter for watcher "${this.expression}"`) } } else { // 如果是新建模板函數(shù),則會(huì)動(dòng)態(tài)計(jì)算模板與data中綁定的變量,這個(gè)時(shí)候就調(diào)用了getter函數(shù),那么就完成了dep的收集 // 調(diào)用getter函數(shù),則同時(shí)會(huì)調(diào)用函數(shù)內(nèi)部的getter的函數(shù),進(jìn)行dep收集工作 value = this.getter.call(vm, vm) } // "touch" every property so they are all tracked as // dependencies for deep watching // 讓每個(gè)屬性都被作為dependencies而tracked, 這樣是為了deep watching if (this.deep) { traverse(value) } popTarget() this.cleanupDeps() return value }
一進(jìn)入get方法,首先進(jìn)行pushTarget(this)的操作,此時(shí)Vue當(dāng)中Dep.target = 當(dāng)前這個(gè)watcher,接下來進(jìn)行value = this.getter.call(vm, vm)操作,在這個(gè)操作中就完成了依賴的收集工作。還是拿文章一開始的demo來說,在vue實(shí)例化的時(shí)候傳入了watch選項(xiàng):
props: { a: { type: Object, default () { return { key1: "a", key2: { a: "b" } } } } }, watch: { a (newVal, oldVal) { console.log(newVal, oldVal) } },
在Vue的initState()開始執(zhí)行后,首先會(huì)初始化props的屬性為getter/setter函數(shù),然后在進(jìn)行initWatch初始化的時(shí)候,這個(gè)時(shí)候初始化watcher實(shí)例,并調(diào)用get()方法,設(shè)置Dep.target = 當(dāng)前這個(gè)watcher實(shí)例,進(jìn)而到value = this.getter.call(vm, vm)的操作。在調(diào)用this.getter.call(vm, vm)的方法中,便會(huì)訪問props選項(xiàng)中的a屬性即其getter函數(shù)。在a屬性的getter函數(shù)執(zhí)行過程中,因?yàn)?b>Dep.target已經(jīng)存在,那么就進(jìn)入了依賴收集的過程:
if (Dep.target) { // Dep.target.addDep(this) // 即添加watch函數(shù) // dep.depend()及調(diào)用了dep.addSub()只不過中間需要判斷是否這個(gè)id的dep已經(jīng)被包含在內(nèi)了 dep.depend() // childOb也添加依賴 if (childOb) { childOb.dep.depend() } if (Array.isArray(value)) { dependArray(value) } }
dep是一開始初始化的過程中,這個(gè)屬性上的dep屬性。調(diào)用dep.depend()函數(shù):
depend () { if (Dep.target) { // Dep.target為一個(gè)watcher Dep.target.addDep(this) } }
Dep.target也就剛才的那個(gè)watcher實(shí)例,這里也就相當(dāng)于調(diào)用了watcher實(shí)例的addDep方法: watcher.addDep(this),并將dep觀察者傳入。在addDep方法中完成依賴收集:
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) } } }
這個(gè)時(shí)候依賴完成了收集,當(dāng)你去修改a屬性的值時(shí),會(huì)調(diào)用a屬性的setter函數(shù),里面會(huì)執(zhí)行dep.notify(),它會(huì)遍歷所有的訂閱者,然后調(diào)用訂閱者上的update函數(shù)。
initData過程和initProps類似,具體可參見源碼。
initComputed以上就是在initProps過程中Vue是如何進(jìn)行依賴收集的,initData的過程和initProps類似,下來再來看看initComputed的過程.
在computed屬性初始化的過程當(dāng)中,會(huì)為每個(gè)屬性實(shí)例化一個(gè)watcher:
const computedWatcherOptions = { lazy: true } function initComputed (vm: Component, computed: Object) { // 新建_computedWatchers屬性 const watchers = vm._computedWatchers = Object.create(null) for (const key in computed) { const userDef = computed[key] // 如果computed為funtion,即取這個(gè)function為getter函數(shù) // 如果computed為非function.則可以多帶帶為這個(gè)屬性定義getter/setter屬性 let getter = typeof userDef === "function" ? userDef : userDef.get // create internal watcher for the computed property. // lazy屬性為true // 注意這個(gè)地方傳入的getter參數(shù) // 實(shí)例化的過程當(dāng)中不去完成依賴的收集工作 watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions) // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. if (!(key in vm)) { defineComputed(vm, key, userDef) } } }
但是這個(gè)watcher在實(shí)例化的過程中,由于傳入了{lazy: true}的配置選項(xiàng),那么一開始是不會(huì)進(jìn)行求值與依賴收集的: this.value = this.lazy ? undefined : this.get().在initComputed的過程中,Vue會(huì)將computed屬性定義到vm實(shí)例上,同時(shí)將這個(gè)屬性定義為getter/setter。當(dāng)你訪問computed屬性的時(shí)候調(diào)用getter函數(shù):
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { // 是否需要重新計(jì)算 if (watcher.dirty) { watcher.evaluate() } // 管理依賴 if (Dep.target) { watcher.depend() } return watcher.value } } }
在watcher存在的情況下,首先判斷watcher.dirty屬性,這個(gè)屬性主要是用于判斷這個(gè)computed屬性是否需要重新求值,因?yàn)樵谏弦惠喌囊蕾囀占倪^程當(dāng)中,觀察者已經(jīng)將這個(gè)watcher添加到依賴數(shù)組當(dāng)中了,如果觀察者發(fā)生了變化,就會(huì)dep.notify(),通知所有的watcher,而對(duì)于computed的watcher接收到變化的請(qǐng)求后,會(huì)將watcher.dirty = true即表明觀察者發(fā)生了變化,當(dāng)再次調(diào)用computed屬性的getter函數(shù)的時(shí)候便會(huì)重新計(jì)算,否則還是使用之前緩存的值。
initWatchinitWatch的過程中其實(shí)就是實(shí)例化new Watcher完成觀察者的依賴收集的過程,在內(nèi)部的實(shí)現(xiàn)當(dāng)中是調(diào)用了原型上的Vue.prototype.$watch方法。這個(gè)方法也適用于vm實(shí)例,即在vm實(shí)例內(nèi)部調(diào)用this.$watch方法去實(shí)例化watcher,完成依賴的收集,同時(shí)監(jiān)聽expOrFn的變化。
總結(jié):
以上就是在Vue實(shí)例初始化的過程中實(shí)現(xiàn)依賴管理的分析。大致的總結(jié)下就是:
initState的過程中,將props,computed,data等屬性通過Object.defineProperty來改造其getter/setter屬性,并為每一個(gè)響應(yīng)式屬性實(shí)例化一個(gè)observer觀察者。這個(gè)observer內(nèi)部dep記錄了這個(gè)響應(yīng)式屬性的所有依賴。
當(dāng)響應(yīng)式屬性調(diào)用setter函數(shù)時(shí),通過dep.notify()方法去遍歷所有的依賴,調(diào)用watcher.update()去完成數(shù)據(jù)的動(dòng)態(tài)響應(yīng)。
這篇文章主要從初始化的數(shù)據(jù)層面上分析了Vue是如何管理依賴來到達(dá)數(shù)據(jù)的動(dòng)態(tài)響應(yīng)。下一篇文章來分析下Vue中模板中的指令和響應(yīng)式數(shù)據(jù)是如何關(guān)聯(lián)來實(shí)現(xiàn)由數(shù)據(jù)驅(qū)動(dòng)視圖,以及數(shù)據(jù)是如何響應(yīng)視圖變化的。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/83791.html
摘要:碰撞檢測(cè)邊界檢測(cè)在前端游戲,以及涉及拖拽交互的場(chǎng)景應(yīng)用十分廣泛。這就涉及到碰撞檢測(cè)或者叫邊界檢測(cè)的問題了。若四邊均未發(fā)生重合,則未發(fā)生碰撞,反之則發(fā)生碰撞。目前業(yè)務(wù)有遇到碰撞需求,所以抽時(shí)間整理了下。 碰撞檢測(cè)(邊界檢測(cè))在前端游戲,以及涉及拖拽交互的場(chǎng)景應(yīng)用十分廣泛。 那么啥叫碰撞?JavaScript 又是如何檢測(cè) DOM 發(fā)生碰撞的呢? 碰撞,顧名思義,就是兩個(gè)物體碰撞在了一起,...
摘要:碰撞檢測(cè)邊界檢測(cè)在前端游戲,以及涉及拖拽交互的場(chǎng)景應(yīng)用十分廣泛。這就涉及到碰撞檢測(cè)或者叫邊界檢測(cè)的問題了。若四邊均未發(fā)生重合,則未發(fā)生碰撞,反之則發(fā)生碰撞。目前業(yè)務(wù)有遇到碰撞需求,所以抽時(shí)間整理了下。 碰撞檢測(cè)(邊界檢測(cè))在前端游戲,以及涉及拖拽交互的場(chǎng)景應(yīng)用十分廣泛。 那么啥叫碰撞?JavaScript 又是如何檢測(cè) DOM 發(fā)生碰撞的呢? 碰撞,顧名思義,就是兩個(gè)物體碰撞在了一起,...
摘要:用來轉(zhuǎn)換內(nèi)容,內(nèi)部調(diào)用了方法進(jìn)行轉(zhuǎn)換,這里簡(jiǎn)單介紹一下的原理將代碼解析成,對(duì)進(jìn)行轉(zhuǎn)譯,得到新的,新的通過轉(zhuǎn)換成,核心方法在中的方法,有興趣可以去了解一下。將函數(shù)傳入?yún)?shù)和歸并,得到。通常我們是用不上的,估計(jì)在某些中可能會(huì)使用到。 什么是Loader? 繼上兩篇文章webpack工作原理介紹(上篇、下篇),我們了解到Loader:模塊轉(zhuǎn)換器,也就是將模塊的內(nèi)容按照需求裝換成新內(nèi)容,而且每...
閱讀 2579·2023-04-26 03:00
閱讀 1392·2021-10-12 10:12
閱讀 4190·2021-09-22 15:33
閱讀 2908·2021-09-22 15:06
閱讀 1530·2019-08-30 15:44
閱讀 2145·2019-08-30 13:59
閱讀 534·2019-08-30 11:24
閱讀 2407·2019-08-29 17:07