当前位置: 首页 > Web前端 > vue.js

哔哩哔哩面试官:你能手写Vue2的响应式原理吗?

时间:2023-03-31 22:00:46 vue.js

前面写的问题是面试中很常见的问题,但是如果你的简历上写着:“精通Vue的使用并阅读了它的部分源代码”,那么这个问题面试官很可能会回答。问你。什么?如果你简历上不写你读过源码,面试官也可能会问你有没有读过响应式相关的源码或者歌词:Youcan'tescape,butyoucan'tescapethe解不开的结。整体流程作为一个前端MVVM框架,Vue的基本思想和Angular、React是一样的。它的核心是:当数据发生变化时,会自动刷新页面的DOM,将我们从繁琐的DOM操作中解放出来,从而专心处理业务逻辑。这就是Vue的双向数据绑定(又称响应式原则)。双向数据绑定是Vue最独特的功能之一。这里我们用官方的一张流程图来简单说明一下Vue响应式系统的整个流程:dependency被调用时,watcher将被通知重新计算,导致其相关组件被更新。这是一个典型的观察者模式。关键角色在Vue数据双向绑定的实现逻辑中,有3个关键角色:Observer:其作用是为对象的属性添加getter和setter,进行依赖收集和调度更新Dep:用于收集当前响应的对象dependencies,每个响应对象包括子对象都有一个Dep实例(其中subs是一个Watcher实例数组),当数据发生变化时,会通过dep.notify()通知每个watcher。Watcher:观察者对象,实例分为渲染观察者(renderwatcher)、计算属性观察者(computedwatcher)、监听者观察者(userwatcher)。为什么要把Watcher和Dep的关系单独挑出来专门写一个section来说明这个有什么问题呢?因为大部分同学只是知道:Vue的响应式原理是通过Object.defineProperty来实现的。由Object.defineProperty绑定的对象将变得“响应式”。即当对象改变时,会触发get和set事件。但是里面具体的对象依赖关系不是很清楚,给面试官一种感觉:你只是背了答案,对response的内部实现细节并没有很清晰的印象。关于Watcher和Dep的关系,一开始不是很清楚。在查阅了相关资料后,对里面的具体实现也渐渐有了清晰的认识。刚接触Dep这个词的同学会一头雾水:Dep到底是干什么用的?我们通过defineReactive方法让data中的数据响应后,虽然我们可以监听到数据的变化,但是我们如何处理notificationview的更新呢?dep就是帮我们靠管理。如上图所示:一个属性可能有多个依赖关系,每个响应式数据都有一个Dep来管理它的依赖关系。一段总结了原理。上面说了这么多。我总结一下Vue响应式的核心设计思想:在创建Vue实例时,Vue会遍历data选项的属性,使用Object.defineProperty为属性添加getters和setters来读取数据。获取劫持(getter用于收集依赖项,setter用于调度更新),并在内部跟踪依赖项,在访问和修改属性时通知更改。每个组件实例都会有一个对应的watcher实例,它会在组件渲染时记录所有依赖的数据属性(依赖集合,以及computedwatcher,userwatcher实例),之后当依赖发生变化时,setter方法会通知watcher实例依赖于此数据的重新计算(调度更新),以便重新呈现其相关组件。至此,“套路”我们都了解了,下面我们就用伪代码来实现Vue的响应式吧!核心实现/***@nameVue数据双向绑定(响应式)实现原理*///observe方法遍历并包装对象属性functionobserve(target){//如果target是对象,则遍历if(target&&typeoftarget==="Object"){Object.keys(target).forEach((key)=>{//defineReactive方法将在目标属性上安装一个“侦听器”defineReactive(target,key,target[钥匙]);});}}//定义defineReactive方法functiondefineReactive(target,key,val){constdep=newDep();//属性值也可能是对象类型,此时需要调用observe递归遍历observe(val);//为当前属性安装监听器Object.defineProperty(target,key,{//Enumerableenumerable:true,//不可配置configurable:false,get:function(){returnval;},//监听器函数集:function(value){dep.notify();},});}classDep{constructor(){this.subs=[];}addSub(sub){这个.subs。推(分);}通知(){这个。潜艇。forEach((sub)=>{sub.update();});}}