Vue 소스 코드의 종속성 수집 원칙
이 글은 Vue 소스코드의 의존성 수집 원리를 주로 소개하는데, 이는 특정 참조 가치를 갖고 있습니다. 이제는 모든 사람과 공유합니다. 도움이 필요한 친구들은 참고할 수 있습니다.
vue는 현재 국내에 있습니다. 웹사이드는 전 세계의 3분의 1을 차지하며, 일상적으로 사용하는 주요 기술 스택 중 하나이기도 합니다. 게다가 Vue 소스 코드를 읽는 기사가 많은 이유도 궁금합니다. 최근 커뮤니티에 등장한 적이 있는데, 이 기회를 빌어 여러분의 글과 토론을 통해 영양분을 조금 배우고 동시에 소스 코드를 읽을 때 어떤 생각을 정리하고 제 생각을 요약하여 몇 가지 글을 작성했습니다. .레벨이 제한되어 있으니 토론메세지를 남겨주세요~
Goal Vue 버전: 2.5.17-beta.0
Vue 소스코드 댓글: https //github.com/SHERlocked...
진술: 기사의 소스 코드 구문은 Flow를 사용했으며 필요에 따라 소스 코드를 삭제했습니다(혼란을 피하기 위해@_@) .풀버전을 보시려면 위의 github 주소를 입력해주세요. 본 글은 연재글 주소는 하단에서 확인하실 수 있습니다~2.5.17-beta.0
vue源码注释:https://github.com/SHERlocked...
声明:文章中源码的语法都使用 Flow,并且源码根据需要都有删节(为了不被迷糊 @_@),如果要看完整版的请进入上面的github地址,本文是系列文章,文章地址见底部~
1. 响应式系统
通过官网的介绍我们知道 Vue.js 是一个MVVM框架,它并不关心视图变化,而通过数据驱动视图更新,这让我们的状态管理非常简单,而这是怎么实现的呢。盗用官网一张图
每个组件实例都有相应的 Watcher
实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter
被调用时,会通知 watcher
重新计算,从而致使它关联的组件得以更新。
这里有三个重要的概念 Observe
、Dep
、Watcher
,分别位于src/core/observer/index.js
、src/core/observer/dep.js
、src/core/observer/watcher.js
Observe
类主要给响应式对象的属性添加getter/setter
用于依赖收集与派发更新Dep
类用于收集当前响应式对象的依赖关系Watcher
类是观察者,实例分为渲染 watcher、计算属性 watcher、侦听器 watcher三种
2. 代码实现
2.1 initState
响应式化的入口位于 src/core/instance/init.js 的 initState
中:
// src/core/instance/state.js export function initState(vm: Component) { const opts = vm.$options if (opts.props) initProps(vm, opts.props) // 初始化props if (opts.methods) initMethods(vm, opts.methods) // 初始化methods if (opts.data) initData(vm) // 初始化data if (opts.computed) initComputed(vm, opts.computed) // 初始化computed if (opts.watch) initWatch(vm, opts.watch) // 初始化watch } }
它非常规律的定义了几个方法来初始化 props
、methods
、data
、computed
、wathcer
,这里看一下 initData
方法,来窥一豹
// src/core/instance/state.js function initData(vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} observe(data, true /* asRootData */) // 给data做响应式处理 }
首先判断了下 data 是不是函数,是则取返回值不是则取自身,之后有一个 observe
方法对 data
进行处理,这个方法尝试给创建一个Observer实例 __ob__
,如果成功创建则返回新的Observer实例,如果已有Observer实例则返回现有的Observer实例
2.2 Observer/defineReactive
// src/core/observer/index.js export function observe (value: any, asRootData: ?boolean): Observer | void { let ob: Observer | void ob = new Observer(value) return ob }
这个方法主要用 data
作为参数去实例化一个 Observer 对象实例,Observer 是一个 Class,用于依赖收集和 notify
更新,Observer 的构造函数使用 defineReactive
方法给对象的键响应式化,给对象的属性递归添加 getter/setter
,当data被取值的时候触发 getter
并搜集依赖,当被修改值的时候先触发 getter
再触发 setter
并派发更新
// src/core/observer/index.js export class Observer { value: any; dep: Dep; constructor (value: any) { value: any; this.dep = new Dep() def(value, '__ob__', this) // def方法保证不可枚举 this.walk(value) } // 遍历对象的每一个属性并将它们转换为getter/setter walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i <p><code>getter</code> 的时候进行依赖的收集,注意这里,只有在 <code>Dep.target</code> 中有值的时候才会进行依赖收集,这个 <code>Dep.target</code> 是在Watcher实例的 <code>get</code> 方法调用的时候 <code>pushTarget</code> 会把当前取值的watcher推入 <code>Dep.target</code>,原先的watcher压栈到 <code>targetStack</code> 栈中,当前取值的watcher取值结束后出栈并把原先的watcher值赋给 <code>Dep.target</code>,<code>cleanupDeps</code> 最后把新的 <code>newDeps</code> 里已经没有的watcher清空,以防止视图上已经不需要的无用watcher触发</p><p><code>setter</code> 的时候首先 <code>getter</code>,并且比对旧值没有变化则return,如果发生变更,则dep通知所有subs中存放的依赖本数据的Watcher实例 <code>update</code> 进行更新,这里 <code>update</code> 中会 <code>queueWatcher( )</code> 异步推送到调度者观察者队列 <code>queue</code> 中,在nextTick时 <code>flushSchedulerQueue( )</code> 把队列中的watcher取出来执行 <code>watcher.run</code> 且执行相关钩子函数</p><h3 id="Dep">2.3 Dep</h3><p>上面多次提到了一个关键词 <code>Dep</code></p><h2 id="반응형-시스템"> 1. 반응형 시스템</h2> <strong>공식 웹사이트의 소개를 통해 Vue.js가 뷰 변경에 관심이 없지만 데이터를 통해 뷰 업데이트를 구동하므로 상태 관리가 매우 간단하다는 것을 알 수 있습니다. 달성? 공식 홈페이지에서 사진 훔치기</strong><img src="/static/imghw/default1.png" data-src="https://img.php.cn//upload/image/780/349/668/1531105929801901.png" class="lazy" title="1531105929801901. png" alt="Vue 소스 코드의 종속성 수집 원칙">#🎜🎜##🎜🎜#각 구성 요소 인스턴스에는 해당 <code>Watcher</code> 인스턴스 개체가 있으며, 속성은 종속성으로 기록됩니다. , 나중에 종속성의 <code>setter</code>가 호출되면 <code>watcher</code>에 다시 계산하라는 알림이 전달되어 관련 구성 요소가 업데이트됩니다. #🎜🎜##🎜🎜#여기에는 세 가지 중요한 개념이 있습니다: <code>Observe</code>, <code>Dep</code>, <code>Watcher</code>는 <code>에 있습니다. src/core/observer/index.js</code>, <code>src/core/observer/dep.js</code>, <code>src/core/observer/watcher.js</code>#🎜🎜 #
- #🎜🎜#
Observe
클래스는 주로 종속성 수집을 위해 반응형 개체의 속성에getter/setter
를 추가합니다. 업데이트 전달 #🎜🎜# - #🎜🎜#
Dep
클래스는 현재 반응형 객체의 종속성을 수집하는 데 사용됩니다 #🎜🎜# - # 🎜🎜#Watcher 클래스는 관찰자이며 해당 인스턴스는 렌더링 관찰자, 계산된 속성 관찰자 및 리스너 관찰자의 세 가지 유형으로 나뉩니다. #🎜🎜#
2. 코드 구현
2.1 initState
#🎜🎜#반응형 입구는 src/core/instance/init.js의initState
에 있습니다: #🎜🎜# // src/core/observer/dep.js let uid = 0 // Dep实例的id,为了方便去重 export default class Dep { static target: ?Watcher // 当前是谁在进行依赖的收集 id: number subs: Array<watcher> // 观察者集合 constructor() { this.id = uid++ // Dep实例的id,为了方便去重 this.subs = [] // 存储收集器中需要通知的Watcher } addSub(sub: Watcher) { ... } /* 添加一个观察者对象 */ removeSub(sub: Watcher) { ... } /* 移除一个观察者对象 */ depend() { ... } /* 依赖收集,当存在Dep.target的时候把自己添加观察者的依赖中 */ notify() { ... } /* 通知所有订阅者 */ } const targetStack = [] // watcher栈 export function pushTarget(_target: ?Watcher) { ... } /* 将watcher观察者实例设置给Dep.target,用以依赖收集。同时将该实例存入target栈中 */ export function popTarget() { ... } /* 将观察者实例从target栈中取出并设置给Dep.target */</watcher>
props
, methods
, data
, computed
, 그리고 wathcer
, initData
메서드를 살펴보고 살짝 살펴보겠습니다#🎜🎜#// src/core/observer/watcher.js /* 一个解析表达式,进行依赖收集的观察者,同时在表达式数据变更时触发回调函数。它被用于$watch api以及指令 */ export default class Watcher { constructor( vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean // 是否是渲染watcher的标志位 ) { this.getter = expOrFn // 在get方法中执行 if (this.computed) { // 是否是 计算属性 this.value = undefined this.dep = new Dep() // 计算属性创建过程中并未求值 } else { // 不是计算属性会立刻求值 this.value = this.get() } } /* 获得getter的值并且重新进行依赖收集 */ get() { pushTarget(this) // 设置Dep.target = this let value value = this.getter.call(vm, vm) popTarget() // 将观察者实例从target栈中取出并设置给Dep.target this.cleanupDeps() return value } addDep(dep: Dep) { ... } /* 添加一个依赖关系到Deps集合中 */ cleanupDeps() { ... } /* 清理newDeps里没有的无用watcher依赖 */ update() { ... } /* 调度者接口,当依赖发生改变的时候进行回调 */ run() { ... } /* 调度者工作接口,将被调度者回调 */ getAndInvoke(cb: Function) { ... } evaluate() { ... } /* 收集该watcher的所有deps依赖 */ depend() { ... } /* 收集该watcher的所有deps依赖,只有计算属性使用 */ teardown() { ... } /* 将自身从所有依赖收集订阅列表删除 */ }
data
를 처리하는 observe
메서드가 있습니다. 이 메서드는 Observer 인스턴스 를 생성하려고 시도합니다. __ob__
, 성공적으로 생성되면 새 Observer 인스턴스를 반환합니다. Observer 인스턴스가 있으면 기존 Observer 인스턴스 #🎜🎜#2.2 Observer/defineReactive
rrreee#🎜🎜를 반환합니다. #이 메소드는 주로data
를 매개변수로 사용하여 Observer 객체 인스턴스를 인스턴스화합니다. Observer는 종속성 수집 및 notify
업데이트에 사용되는 클래스입니다. code>defineReactive 메소드를 사용하여 객체의 키 형식을 반응적으로 지정합니다. 속성은 getter/setter
를 사용하여 반복적으로 추가되며, getter
가 트리거되고 종속성이 수집됩니다. . 값이 수정되면 getter
가 먼저 트리거됩니다. setter
가 다시 트리거되고 업데이트가 전송되면 #🎜🎜#rrreee#🎜🎜#getter code>에 종속성이 수집됩니다. 여기서는 <code>Dep.target
만 종속성 수집이 code>에 값이 있는 경우에만 수행된다는 점에 유의하세요. Watcher 인스턴스의 get
메서드가 호출됩니다. pushTarget
은 감시자의 현재 값을 Dep.target
에 푸시하고, 원래 감시자를 targetStack
스택, 그리고 값이 완료된 후 감시자의 현재 값을 스택에서 팝합니다. 감시자 값은 Dep.target
, cleanupDeps 그리고 마지막으로 더 이상 새 <code>newDeps
에 없는 감시자는 보기에서 더 이상 필요하지 않도록 지워집니다. 쓸모 없는 감시자가 #🎜🎜##🎜🎜#setter, 먼저 getter
를 수행하고, 이전 값과 비교하여 변경 사항이 없으면 반환합니다. 변경 사항이 있으면 dep는 모든 Watcher 인스턴스 update
에 알립니다. > 이 데이터에 의존하는 하위 항목에 저장된 update
는 queueWatcher( )
에 의해 스케줄러 관찰자 대기열에 비동기적으로 푸시됩니다. , nextTick에서 flushSchedulerQueue( )
는 대기열에 있는 감시자를 꺼내고 watcher.run
을 실행하고 관련 후크 함수를 실행합니다#🎜🎜# 2.3 Dep
#🎜🎜#키워드Dep
는 위에서 여러 번 언급한 바 있으며 종속성 수집을 위한 컨테이너이거나 #🎜🎜#Dependency Collector#🎜🎜라고도 합니다. #, 그는 어떤 Watchers를 기록합니다. 자신의 변경 사항에 의존하거나 자신의 변경 사항을 구독하는 Watchers는 다음 네티즌의 인용문입니다. #🎜🎜#@liuhongyi0101 :简单点说就是引用计数 ,谁借了我的钱,我就把那个人记下来,以后我的钱少了 我就通知他们说我没钱了
而把借钱的人记下来的小本本就是这里 Dep
实例里的subs
// src/core/observer/dep.js let uid = 0 // Dep实例的id,为了方便去重 export default class Dep { static target: ?Watcher // 当前是谁在进行依赖的收集 id: number subs: Array<watcher> // 观察者集合 constructor() { this.id = uid++ // Dep实例的id,为了方便去重 this.subs = [] // 存储收集器中需要通知的Watcher } addSub(sub: Watcher) { ... } /* 添加一个观察者对象 */ removeSub(sub: Watcher) { ... } /* 移除一个观察者对象 */ depend() { ... } /* 依赖收集,当存在Dep.target的时候把自己添加观察者的依赖中 */ notify() { ... } /* 通知所有订阅者 */ } const targetStack = [] // watcher栈 export function pushTarget(_target: ?Watcher) { ... } /* 将watcher观察者实例设置给Dep.target,用以依赖收集。同时将该实例存入target栈中 */ export function popTarget() { ... } /* 将观察者实例从target栈中取出并设置给Dep.target */</watcher>
这里 Dep
的实例中的 subs
搜集的依赖就是 watcher 了,它是 Watcher
的实例,将来用来通知更新
2.4 Watcher
// src/core/observer/watcher.js /* 一个解析表达式,进行依赖收集的观察者,同时在表达式数据变更时触发回调函数。它被用于$watch api以及指令 */ export default class Watcher { constructor( vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean // 是否是渲染watcher的标志位 ) { this.getter = expOrFn // 在get方法中执行 if (this.computed) { // 是否是 计算属性 this.value = undefined this.dep = new Dep() // 计算属性创建过程中并未求值 } else { // 不是计算属性会立刻求值 this.value = this.get() } } /* 获得getter的值并且重新进行依赖收集 */ get() { pushTarget(this) // 设置Dep.target = this let value value = this.getter.call(vm, vm) popTarget() // 将观察者实例从target栈中取出并设置给Dep.target this.cleanupDeps() return value } addDep(dep: Dep) { ... } /* 添加一个依赖关系到Deps集合中 */ cleanupDeps() { ... } /* 清理newDeps里没有的无用watcher依赖 */ update() { ... } /* 调度者接口,当依赖发生改变的时候进行回调 */ run() { ... } /* 调度者工作接口,将被调度者回调 */ getAndInvoke(cb: Function) { ... } evaluate() { ... } /* 收集该watcher的所有deps依赖 */ depend() { ... } /* 收集该watcher的所有deps依赖,只有计算属性使用 */ teardown() { ... } /* 将自身从所有依赖收集订阅列表删除 */ }
get
方法中执行的 getter
就是在一开始new渲染watcher时传入的 updateComponent = () => { vm._update(vm._render(), hydrating) }
,这个方法首先 vm._render()
生成渲染VNode树,在这个过程中完成对当前Vue实例 vm
上的数据访问,触发相应一众响应式对象的 getter
,然后 vm._update()
去 patch
注意这里的 get
方法最后执行了 getAndInvoke
,这个方法首先遍历watcher中存的 deps
,移除 newDep
中已经没有的订阅,然后 depIds = newDepIds; deps = newDeps
,把 newDepIds
和 newDeps
清空。每次添加完新的订阅后移除旧的已经不需要的订阅,这样在某些情况,比如 v-if
已不需要的模板依赖的数据发生变化时就不会通知watcher去 update
了
2.5 小结
整个收集的流程大约是这样的,可以对照着上面的流程看一下
watcher 有下面几种使用场景:
render watcher
渲染 watcher,渲染视图用的 watchercomputed watcher
计算属性 watcher,因为计算属性即依赖别人也被人依赖,因此也会持有一个Dep
实例watch watcher
侦听器 watcher
只要会被别的观察者 (watchers
) 依赖,比如data、data的属性、计算属性、props,就会在闭包里生成一个 Dep 的实例 dep
并在被调用 getter
的时候 dep.depend
收集它被谁依赖了,并把被依赖的watcher存放到自己的subs中 this.subs.push(sub)
,以便在自身改变的时候通知 notify
存放在 dep.subs
数组中依赖自己的 watchers
自己改变了,请及时 update
~
只要依赖别的响应式化对象的对象,都会生成一个观察者 watcher
,用来统计这个 watcher
依赖了哪些响应式对象,在这个 watcher
求值前把当前 watcher
设置到全局 Dep.target
,并在自己依赖的响应式对象发生改变的时候及时 update
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
위 내용은 Vue 소스 코드의 종속성 수집 원칙의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Vue 프레임워크를 사용하여 프런트엔드 프로젝트를 개발할 때 배포 시 여러 환경을 배포하게 되는데, 개발 환경, 테스트 환경, 온라인 환경에서 호출되는 인터페이스 도메인 이름이 다른 경우가 많습니다. 어떻게 구별할 수 있나요? 그것은 환경 변수와 패턴을 사용하는 것입니다.

Ace는 JavaScript로 작성된 내장형 코드 편집기입니다. Sublime, Vim 및 TextMate와 같은 기본 편집기의 기능 및 성능과 일치합니다. 모든 웹페이지와 JavaScript 애플리케이션에 쉽게 삽입할 수 있습니다. Ace는 Cloud9 IDE의 메인 편집자로 유지되며 Mozilla Skywriter(Bespin) 프로젝트의 후속 버전입니다.

구성요소화와 모듈화의 차이점: 모듈화는 코드 논리의 관점에서 구분되며, 코드 계층 개발을 용이하게 하고 각 기능 모듈의 기능이 일관되게 유지되도록 합니다. 컴포넌트화는 UI 인터페이스 관점에서 계획하는 것으로 프런트엔드의 컴포넌트화는 UI 컴포넌트의 재사용을 용이하게 합니다.

Vue.js는 오늘날 프런트엔드 개발에서 매우 인기 있는 프레임워크가 되었습니다. Vue.js가 계속 발전함에 따라 단위 테스트는 점점 더 중요해지고 있습니다. 오늘은 Vue.js 3에서 단위 테스트를 작성하는 방법을 살펴보고 몇 가지 모범 사례와 일반적인 문제 및 솔루션을 제공하겠습니다.

서문: vue3 개발에서 반응형은 반응형 데이터를 구현하는 방법을 제공합니다. 일상적인 개발에서 자주 사용되는 API입니다. 이 기사에서 저자는 내부 작동 메커니즘을 탐구합니다.

Vue.js에서 개발자는 JSX 구문과 템플릿 구문이라는 두 가지 다른 구문을 사용하여 사용자 인터페이스를 만들 수 있습니다. 두 구문 모두 장점과 단점이 있습니다. 차이점, 장점 및 단점을 논의해 보겠습니다.

Vue3 동적 구성 요소에서 예외를 처리하는 방법은 무엇입니까? 다음 기사에서는 Vue3 동적 구성 요소 예외 처리 방법에 대해 설명합니다. 이것이 모든 사람에게 도움이 되기를 바랍니다.

실제 개발 프로젝트 프로세스에서는 상대적으로 큰 파일을 업로드해야 하는 경우가 있는데, 그러면 업로드가 상대적으로 느려지므로 백그라운드에서 파일 조각을 업로드하려면 매우 간단합니다. 기가바이트 파일 스트림이 여러 개의 작은 파일 스트림으로 절단된 다음 인터페이스는 작은 파일 스트림을 각각 전달하도록 요청됩니다.
