這篇文章主要介紹了Vue 原始碼分析之Observer實作過程,Observer 最主要的角色就是實作了touch -Data(getter) - Collect as Dependency這段過程,也就是依賴收集的過程,有興趣的朋友一起學習吧
導語:
本文是對Vue 官方文件深入響應式原則(https://cn.vuejs.org/v2/guide/reactivity. html)的理解,並透過原始碼還原實現過程。
響應式原理可分為兩步,依賴收集的過程與觸發-重新渲染的過程。依賴收集的過程,有三個很重要的類,分別是 Watcher、Dep、Observer。本文主要解讀 Observer 。
這篇文章講解上篇文章沒有覆蓋到的Observer 部分的內容,還是先看官網這張圖:
Observer 最主要的作用就是實作了上圖中touch -Data(getter) - Collect as Dependency這段過程,也就是依賴收集的過程。
還是以下面的程式碼為範例進行梳理:
(註:左右滑動即可查看完整程式碼,下同)
varvm = newVue({ el: '#demo', data: { firstName: 'Hello', fullName: '' }, watch: { firstName(val) { this.fullName = val + 'TalkingData'; }, } })
在原始碼中,透過還原Vue 進行實例化的過程,從開始一步一步到Observer 類別的原始碼依次為(省略了很多不在本篇文章討論的程式碼):
// src/core/instance/index.js functionVue(options) { if(process.env.NODE_ENV !== 'production'&& !(thisinstanceofVue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) } // src/core/instance/init.js Vue.prototype._init = function(options?: Object) { constvm: Component = this // ... initState(vm) // ... } // src/core/instance/state.js exportfunctioninitState(vm: Component) { // ... constopts = vm.$options if(opts.data) { initData(vm) } // ... } functioninitData(vm: Component) { letdata = vm.$options.data data = vm._data = typeofdata === 'function' ? getData(data, vm) : data || {} // ... // observe data observe(data, true/* asRootData */) }
在initData 方法中,開始了對data 項中的資料進行“觀察”,會將所有資料的變成observable 的。接下來看observe 方法的程式碼:
// src/core/observer/index.js functionobserve(value: any, asRootData: ?boolean): Observer| void{ // 如果不是对象,直接返回 if(!isObject(value) || value instanceofVNode) { return } letob: Observer | void if(hasOwn(value, '__ob__') && value.__ob__ instanceofObserver) { // 如果有实例则返回实例 ob = value.__ob__ } elseif( // 确保value是单纯的对象,而不是函数或者是Regexp等情况 observerState.shouldConvert && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { // 实例化一个 Observer ob = newObserver(value) } if(asRootData && ob) { ob.vmCount++ } returnob }
exportfunctiondefineReactive( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean ) { constdep = newDep() // 获取属性的自身描述符 constproperty = Object.getOwnPropertyDeor(obj, key) if(property && property.configurable === false) { return } // cater for pre-defined getter/setters // 检查属性之前是否设置了 getter/setter // 如果设置了,则在之后的 get/set 方法中执行设置了的 getter/setter constgetter = property && property.get constsetter = property && property.set // 通过对属性再次调用 observe 方法来判断是否有子对象 // 如果有子对象,对子对象也进行依赖搜集 letchildOb = !shallow && observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: functionreactiveGetter() { // 如果属性原本拥有getter方法则执行 constvalue = getter ? getter.call(obj) : val if(Dep.target) { // 进行依赖收集 dep.depend() if(childOb) { // 如果有子对象,对子对象也进行依赖搜集 childOb.dep.depend() // 如果属性是数组,则对每一个项都进行依赖收集 // 如果某一项还是数组,则递归 if(Array.isArray(value)) { dependArray(value) } } } returnvalue }, set: functionreactiveSetter(newVal) { // 如果属性原本拥有getter方法则执行 // 通过getter方法获取当前值,与新值进行比较 // 如果新旧值一样则不需要执行下面的操作 constvalue = 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方法则执行 setter.call(obj, newVal) } else{ // 如果原本没有setter则直接赋新值 val = newVal } // 判断新的值是否有子对象,有的话继续观察子对象 childOb = !shallow && observe(newVal) // 通知所有的观察者,更新状态 dep.notify() } }) }#依照原始碼中的中文註釋,應該可以明白defineReactive 執行的過程中做了哪些工作。其實整個過程就是遞歸,為每個屬性加上getter/setter。對於getter/setter,同樣也需要對每一個屬性進行遞歸(判斷子物件)的完成觀察者模式。對於getter,用來完成依賴收集,即原始碼中的dep.depend()。對於setter,一旦一個資料觸發其set方法,就會發布更新訊息,通知這個資料的所有觀察者也要改變。即原始碼中的dep.notify()。 ######上面是我整理給大家的,希望今後對大家有幫助。 ######相關文章:#########vue2.0 資源檔案assets與static的差異詳解###############Angular 5.x 學習筆記之Router(路由)應用###############React Router v4 入坑指南####################### ####
以上是Vue 源碼分析之 Observer實作過程的詳細內容。更多資訊請關注PHP中文網其他相關文章!