이번에는 Vue를 사용하여 Observer를 구현하는 방법과 Vue를 사용하여 Observer를 구현할 때 Notes에 대해 설명하겠습니다. 다음은 실제 사례입니다.
소개:
이 글은 Vue 공식 문서(https://cn.vuejs.org/v2/guide/reactivity.html)의 반응성 원칙과 구현 프로세스에 대한 심층적인 이해입니다. 소스코드 복원을 통해
반응형 원칙은 수집에 의존하는 프로세스와 트리거 및 다시 렌더링하는 프로세스의 두 단계로 나눌 수 있습니다. 종속성 수집 프로세스에는 Watcher, Dep 및 Observer라는 세 가지 매우 중요한 클래스가 있습니다. 이 글에서는 주로 Observer에 대해 설명합니다.
이 기사에서는 이전 기사에서 다루지 않은 Observer 부분의 내용을 설명합니다. 먼저 공식 홈페이지에 있는 이 사진을 살펴보겠습니다.
Observer의 주요 기능은 터치 -Data( getter) - 위 그림에서 수집하는 과정은 종속성 수집 과정입니다.
정리를 위해 다음 코드를 예로 들어보겠습니다.
(참고: 전체 코드를 보려면 왼쪽과 오른쪽으로 스와이프하세요. 아래도 마찬가지입니다.)
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 메소드에서 데이터를 "관찰"하기 시작합니다. 데이터 항목에서 모든 데이터를 관찰 가능하게 만듭니다. 다음으로, 관찰 메소드의 코드를 살펴보세요.
// 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 }
관찰 메소드의 기능은 데이터에 대한 Observer 인스턴스를 생성하고 이를 반환하는 것입니다. 데이터에 ob 속성이 있으면 이미 Observer 인스턴스가 있다는 의미이며, 기존 인스턴스가 반환됩니다. Vue의 반응형 데이터에는 반복 바인딩을 방지하기 위해 속성의 Observer 인스턴스를 저장하는 ob 속성이 있습니다. new Observer(value) 과정에서 어떤 일이 일어나는지 살펴보겠습니다.
exportclassObserver{ value: any; dep: Dep; vmCount: number; // number of vms that has this object as root $data constructor(value: any) { this.value = value this.dep = newDep() this.vmCount = 0 def(value, 'ob', this) if(Array.isArray(value)) { // ... this.observeArray(value) } else{ this.walk(value) } } walk (obj: Object) { constkeys = Object.keys(obj) for(leti = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } observeArray (items: Array<any>) { for(leti = 0, l = items.length; i < l; i++) { observe(items[i]) } } }
Observer를 인스턴스화하는 과정에서 두 가지 주요 판단이 내려진다는 것을 소스 코드에서 볼 수 있습니다. 배열인 경우 관찰할 배열의 각 항목에 대해 Oberser 메서드를 다시 호출하고, 배열이 아닌 개체인 경우 개체의 각 속성을 탐색하고 해당 항목에 대해 DefineReactive 메서드를 호출합니다. 여기서 DefineReactive 메소드가 핵심입니다! 종속성 수집은 관찰해야 하는 각 속성에 get/set를 추가하기 위해 Object.defineProperty 메서드를 사용하여 완료됩니다. 종속성이 수집된 후 각 속성에는 모든 Watcher 개체를 저장하는 Dep가 있습니다. 기사 시작 부분의 예제에 따르면 get/set이 firstName과 fullName에 각각 추가되고, 각각에는 이를 관찰하는 모든 Watcher 개체를 저장하는 Dep 인스턴스가 있습니다. 다음은 DefineReactive의 소스 코드입니다.
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의 경우 각 속성Observer 패턴에 대해 재귀(하위 개체 판단)를 완료하는 것도 필요합니다. getter의 경우 소스 코드에서 종속성 수집, 즉 dep.dependent()를 완료하는 데 사용됩니다. setter의 경우 데이터 조각이 set 메서드를 트리거하면 업데이트 메시지가 게시되어 모든 관찰자에게 데이터도 변경된다는 사실을 알립니다. 소스 코드의 dep.notify() 입니다.
이 기사의 사례를 읽은 후 방법을 마스터했다고 생각합니다. 더 흥미로운 정보를 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!
추천 도서:
JavaScript EventEmitter 기본 논리 분석
위 내용은 Vue를 사용하여 Observer를 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!