最近在学习Vue,一直对它的一个核心概念——响应性感到困惑。无意间发现了EvanYou的课程vueadvancedworkshop。Vue的作者尤玉玺亲自对Vue进行了讲解。以下是课程学习的总结。我们欢迎您的参考和意见。什么叫反应灵敏?响应式是Vue的一个核心特性,用于监控视图中绑定的数据,当数据发生变化时视图自动更新。响应性是每当状态发生变化时系统依赖部分的自动更新。在web场景中,变化的状态反映在DOM上的变化上。响应式实现数据驱动视图的第一步。DataDrivenView现在有这样一个例子:变量a和变量b,变量b的值总是等于a的10倍。如果你使用命令式编程,它可以很容易地实现。leta=3;letb=a*10;console.log(b)//30但当我们将b的值设置为4时,b仍然等于30leta=3;letb=a*10;console.log(b)//30a=4;//命令式,b不会保持关系同步console.log(b)//30那么如何实现当a改变时,b也同时改变?这里有个神奇的函数onChanged(),它接收一个函数,当a的值发生变化时,可以自动执行里面的代码,我们把b的update放在里面,问题就解决了。onChanged(()=>b=a*10)//声明式,b随着a的变化而变化。让我们扩大它。下面的代码还有一个神奇的函数onStateChange,当状态改变时会自动运行。那么我们只需要在函数中编写操作dom的代码,就可以实现dom的自动更新了。//DOM元素//魔法函数,当状态值改变时,会自动重新运行onStateChange(()=>{document.querySelector('.cell.b1').textContent=state.a*10})下面我们进一步抽象,把dom操作换成渲染引擎,但是我们不去研究渲染引擎的实现,只是简单的认为它会自动解析模板代码并进行关联有了数据,那么代码就会变成这样。//DOM元素{{state.a*10}}//魔法函数,当状态值改变时,会自动重新运行onStateChange(()=>{view=render(state)})如何实现响应式getters和settersVue中的对象会转化为响应式对象,使用ES5的defineProperty()覆盖所有属性的getter和setter方法。MDN上Object.defineProperty的介绍下面将演示如何通过隐蔽函数修改传入对象的getter和setter来修改对象属性letrealValueObject.defineProperty(obj,'foo',{get(){returnrealValue},set(newValue){realValue=newValue}})functioncovert(obj){Object.keys(obj).forEach(key=>{letinternalValue=obj[key]//闭包,为内部值提供存储机会Object.defineProperty(obj,key,{get(){returninternalValue},set(newValue){internalValue=newValue}})})}依赖跟踪(订阅发布模式)创建一个依赖跟踪类Dep,它有两个方法:“依赖”“通知”。'depend':收集这个依赖。'notify':表示依赖发生变化,之前定义为依赖的任何表达式、函数和计算都会被通知重新执行。这意味着我们需要找到一种让他们联系起来的方式。我们称这种计算关系为依赖关系。这种计算也称为订阅者模式。下面是Dep类的预期效果。调用dep.depend方法收集依赖。当调用dep.notify方法时,控制台会再次输出更新后的语句。constdep=newDep()autorun(()=>{dep.depend()//实际上是在订阅者列表中把这个函数添加到dep中,然后不管你在哪里调用console.log("updated")})//shouldlog:"updated"dep.notify()//再次调用该函数shouldlog:"updated"autorun这个autorun函数接收一个update函数或者表达式,当你进入这个update函数时,一切都是要特别的,当代码放在这个响应区,可以通过dep.depend方法注册依赖。代码实现window.Dep=classDep{constructor(){this.subsctibers=newSet()}depend(){if(activeUpdate){//将这个activeUpdate注册为订阅者this.subscribers.add(activeUpdate)}},notify(){//通知所有订阅者this.subscribers.forEach(sub=>sub())//获取订阅函数并执行}}letactiveUpdate//可以作为发布者functionautorun(update){functionwrappedUpdate(){activeUpdate=wrappedUpdate//赋值给wrappedUpdate会使更新函数在依赖发生变化时重新执行动态更新依赖,保证依赖永远是最新的update()activeUpdate=null}wrappedUpdate();}autorun(()=>{dep.depend()})结合前两个函数covert()autorun()并重命名covert为observe()来实现一个迷你响应式系统。观察者需要一个监听器对象,并监听它们的getter和setter。在getters和setters中,我们可以设置依赖关系。集成之后,我们相当于创建了一个对象,我们访问了一个属性,它收集依赖,当我们通过赋值改变属性值时调用dep.depend,调用notify触发改变。预期调用效果:conststate={count:0}observe(state)autorun(()=>console.log(state.count))//应该立即记录"countis:0"state.count++//应该log"countis:1"最后集成代码如下:classDep{constructor(){this.subscribers=newSet()}depend(){if(activeUpdate){this.subscribers.add(activeUpdate)}}notify(){this.subscribers.forEach(sub=>sub())}}functionobserve(obj){Object.keys(obj).forEach(key=>{letinternalValue=obj[key]constdep=newDep()Object.defineProperty(obj,key,{//在getter中收集依赖,触发notify时重新运行get(){dep.depend()returninternalValue},//setter用于调用notifyset(newVal){if(internalValue!==newVal){internalValue=newValdep.notify()}}})})returnobj}letactiveUpdate=nullfunctionautorun(update){constwrappedUpdate=()=>{activeUpdate=wrappedUpdateupdate()activeUpdate=null}wrappedUpdate()}以上是个人理解,写的学习笔记,欢迎提建议