目录1:前言响应式系统Vue框架的核心内容之一,涉及到很多知识点和源码部分。这里的目的是梳理一下主要流程。建立响应能力的知识体系,细节需要反复体验!所以分享的思路是着重介绍流程,相关的功能只做介绍。2:什么是响应式的观察数据发生变化时会通知view重新渲染,也就是model->view的过程,那么vue中常见的观察数据有哪些呢?datapropscomputedwatch/method(和之前的数据相关)那么问题来了,修改这些数据会触发视图更新吗?3:主要流程数据监控依赖采集/分发更新观察者模式3.1:监控实现数据监控的主要目的是什么?它允许我们在访问和赋值时处理一个响应式数据,以实现数据中的数据观察。相关函数如下:observe(data)defineReactive(obj,key)Object.definePropertyvue中的数据出于作用域的考虑,设计成一个函数,其中数据为对象的最终返回值,observe会遍历每个值data,并递归处理嵌套对象/*模拟observe函数的实现*/functionobserve(data){letdep=newDep()//这是用来收集watchers的subscriber,在get和set中引用closure//需要保证提供的数据是对象if(!data||typeofdata!=='object')return//遍历数据,处理Object.keys(data).forEach((item)=>{//递归处理if(typeofitem==='object'){observe(item)}else{defineReactive(data,item,data[item])}})}functiondefineReactive(obj,key,value){Object.defineProperty(obj,key,{enumerable:true,//可枚举,可以遍历访问configurable:true,//可以删除get:functionreactiveGetter(){/*收集依赖,返回值*/console.log('collect')returnvalue},set:functionreactiveSetter(){/*触发依赖*/dep.notify()}})}3.1.1:基于Object.defineProperty的数据观察存在一些问题。delete,只能处理值变化,不能监听数组变化,所以需要修改基于Object.create(Array.prototype)的数组方法,主要是修改数组方法的下标访问修改,或者长度修改不能monitor基于proxy的数据监控待完善(待处理)3.2:依赖收集/分发更新3.2.1实现过程前面我们粗略的提到了会在getter中收集依赖,下面我们具体看下这个过程是如何实现的.我们在观察数据的时候,看到有一个letdep=newDep(),这个是订阅者有自己的dep实例,每个数据观察者是一个观察者,是一个使用数据数据的表达式或者函数//如何dep和watcher构成一个观察者Mode的classDep{constructor(){//这是用来保存watcher的数组this.subs=[]}addSubs(sub){this.subs.push(sub)},notify(){this.subs.forEach((sub)=>{sub.update()})}}这里回顾一下,get触发时,watchers被收集。触发设置时,必须更新所有观察者。需要明确一个概念。这里,dep--观察者之间建立的是观察者模式,不是发布订阅模式!在很多文章中,你都会看到一句话“观察者模式(observe)或者叫做发布订阅模式(pub/sub)”。两者之间是有区别的。读者需要有意识地识别同类文章!这个知识点不是本文的重点。简单列举一两个pub/sub模型是基于observe的解耦,多一个调度中心的概念。Observe无法控制通知哪些订阅者,触发更新时只能看到所有的通知这时候watchers不管不同都会更新,但是如果是pub/sub实现,它可以决定更新哪些watchers3.2.2:Watcher的实现在上一节中,我们只介绍了名为watcher的观察者。我们还需要了解一下watcher的大致实现:它是一个表达式或者函数,使用data数据,在getter中收集,在setter中逐一触发。classWatcher{constructor(obj,key,cb){//Dep.target获取watcher实例Dep.target=thisthis.cb=cbthis.obj=objthis.key=key//在这里触发getterthis.value=obj[key]//这是一个需要清除的闭包变量dep.target=null}update(){//获取一个新值this.value=this.obj[this.key]//我们定义了一个cb函数,用来模拟视图Update,调用它就是更新视图this.cb(this.value)}}回顾一下这个过程,代码中是通过表达式/函数来操作数据值的,也就是watcher被触发,在watcher中触发getter收集当前的watcherb=obj.a这个操作增加了watcher的概念。本来我们以为只是触发了a的getter和b的setter//我们来看响应式的实现函数defineReactive(obj,key,value){letdp=newDep()//作为观察者的一个闭包变量Object.defineProperty(obj,key,{enumerable:true,//可枚举(可遍历)configurable:true,//可配置(例如可删除)get:functionreactiveGetter(){console.log('get',value)//Listen//添加Watcher到订阅if(Dep.target){dp.addSub(Dep.target)//添加}returnvalue},set:functionreactiveSetter(newVal){observe(newVal)//如果赋值是对象,也递归子属性if(newVal!==value){console.log('set',newVal)//监听render()value=newVal//执行watcher的update方法dp.notify()//添加}}})}这个时候还剩下一个地方,是什么导致watcher被触发的,答案是render4:reviewandsummarydata的数据通过observe处理重写getter/setter在渲染时通知watcher,触发getter收集依赖于每条数据的dep,所有watchers构成观察者模式。当setter被触发时,通知所有watcher触发更新,然后执行render将virtual-dom和virtual-dom更新到view的过程会有一个patch/mount过程,属于virtualdom的概念和这里就不介绍了
