首页 > 编程语言 >vue面试题(vue2响应式源码剖析)

vue面试题(vue2响应式源码剖析)

时间:2024-03-18 23:46:11浏览次数:26  
标签:面试题 vue dep data watcher 源码 当前 方法 属性

一、前言

这篇文章结合Vue2.7.16的源码和一个Vue2的项目,来详细讲解Vue2实现响应式数据的核心代码

1.1 准备

  1. 安装@vue/cli

    npm install -g @vue/cli
    
  2. 创建vue项目

    vue create vue2-test
    
  3. 修改Vue实例的配置对象

    image-20240318200957743

二、响应式处理的入口

  1. 通过 new Vue() 调用 Vue 构造函数,然后会执行里面的this._init(options)方法

    image-20240318201336829
  2. _init(options)方法定义在Vue.prototype上,定义是通过执行initMixin(Vue)实现的

    image-20240318201612226

  3. _init(options)方法中,做了一些初始化的操作。其中initState(vm)用来初始化状态。

    image-20240318201828392

  4. initState(vm),按照props、methods、data、computed、watch的顺序,对一些属性做了初始化操作。其中initData(vm)就是对配置对象中的data做响应式处理的。

    image-20240318202029673

  5. initData()方法中,做了以下处理:

    • 获取到配置对象上面的data配置项,如果是用方法定义的就通过调用方法获取,如果是用对象配置的,就直接访问。然后将data绑定到vm._data上。
    • 检查data中的属性名是否与methods、props中定义重复
    • 通过proxy(),将vm._data上的数据直接放在了vm身上。内部就是使用Object.defineProperty()实现通过vm.属性去访问vm._data.属性。这样访问要方便一点。
    • 调用observe(data)来对数据进行观测,这也是响应式处理的入口

    image-20240318202356812

    image-20240318203317526

三、入口函数observe

  1. observe接收一个value(第一次传入的就是data),然后判断其身上是否有_ob_属性,该属性用来标识是否已经被观测。如果已经被观测过了,就直接返回value身上的ob对象。

  2. 判断数据类型等条件,条件满足,则创建一个Observer实例并返回。

  3. 在上面initData()方法中,会拿到返回的ob对象,并将ob对象身上的vmCount++。它的作用就是用来区分我们操作的对象是根\(data还是其子属性。 在Vue中,应该避免直接在一个Vue实例或其根`\)data`对象上添加或删除响应式属性。

    image-20240318203541883

    image-20240318204737737

    image-20240318204810530

四、 Observer类

  1. Observer类有两个属性:depvmCountvmCount的作用已经说过了。dep是一个Dep实例,用来收集依赖和派发更新。

  2. Observer的构造器中对depvmCount进行了初始化,然后在def(value, '__ob__', this)方法中,通过Object.defineProperty为当前的value(第一次调用时是data)添加了一个_ob_属性,属性值为当前的Observer实例

  3. 根据当前的value是不是数组,来进行不同的操作。如果不是数组,则遍历当前value的所有属性,执行defineReactive()方法。是数组的情况,在后面单独说明。

    image-20240318205354618

五、defineReactive

  1. defineReactive()方法就是用来对当前的属性做响应式处理的,主要做了以下操作:

    • 创建一个Dep实例对象,用来收集当前属性的依赖和派发更新。
    • 通过Object.getOwnPropertyDescriptor(obj, key)来获取当前属性的所有自有描述信息,比如:是否可写、可枚举、可配置、get()、set()
    • 如果当前属性不可配置,就直接返回不做处理。
    • 获取当前属性的getter和setter,后面会用到。获取当前属性的属性值,后面会用到。
    • 根据是否深度观测,来决定是否调用observe(value)方法。调用该方法,就可以实现递归地对所有属性进行响应式处理,同时,该方法还会为当前属性身上添加一个_ob_属性,指向一个Observer实例,然后将该实例对象返回,即childOb
    • 使用Object.defineProperty为当前属性添加getter、setter、enumerable: true、configurable: true等属性,即在此做数据劫持。

    image-20240318210214193

  2. 在get方法中,首先会获取当前属性的属性值(有getter就通过getter,没有就使用前面获取到的val)。

    然后通过dep.depend()Dep.target(当前访问该属性的watcher)添加到当前属性的依赖列表中,同时也添加到childOb的依赖列表中。这里之所以添加到childOb的列表中,是为了在其它地方也能知道有哪些watcher依赖该属性。因为可以通过属性._ob_.dep来进行派发更新。这也是$set$delete实现数据响应式的前提。

    最后将数据返回。

    image-20240318211537805

  3. 在set方法中,首先获取到当前属性的属性值(有getter就通过getter,没有就使用前面获取到的val)。

    然后对比当前set方法接收到的值,如果没有变化,就不做处理。如果发生了变化,就修改当前的值(有setter就通过setter修改,没有就手动修改)。

    考虑到set方法接收的新值可能也是一个对象,所以需要对这个新的值再次调用observe(newValue)进行观测。

    最后调用dep.notify()派发更新(通知依赖列表中的所有watcher执行update方法)。

    image-20240318212315488

六、收集依赖和派发更新

  1. 在get方法中,会收集依赖当前属性的所有watcher

    image-20240318212558614

  2. Dep类的结构如下

    根据Dep类的定义,可以知道每一个Dep实例都有一个id(唯一标识)和一个subs(用来保存watcher)。

    此外还有两个最重要的方法,depend()(收集依赖)和notify()(派发更新)。

    同时Dep还有一个静态属性target,指向的就是当前的watcher。

    image-20240318212825867

  3. depend()

    需要注意的是depend()方法并没有直接将Dep.target添加到subs中,它反而是调用了Dep.targetaddDep(this)方法,将当前的Dep实例传给了watcher

    image-20240318212948340

    watcher的addDep()方法,将传过来的dep信息保存下来,并通过dep.addSub(this),将当前watcher添加到dep的subs中。

    这里保证了watcher不重复收集dep,dep不重复收集watcher。而且,当watcher被销毁的时候,就可以根据收集的dep信息,通知相应的dep将自己从subs中移除,以免后面进行派发更新的时候通知给一个已经不存在的watcher。

    image-20240318213154409

  4. notify()

    这里根据watcher的id进行了排序,因为watcher有三种:渲染watcher、计算属性watcher、用户watcher。这三者要保证计算属性watcher、用户watcher、渲染watcher的顺序执行。

    遍历调用watcher的update方法实现更新操作。

    image-20240318213335207

七、数组的响应式处理

  1. 对于数组的响应式处理,这里首先做了一个判断,在if (!mock)里面,对数组的七个方法进行了重写,准确来说,是为响应式的数组修改了原型对象。

    重写的七个方法,在原有方法的功能之上,实现了派发更新,就是通过数组身上的_ob_.dep实现的。

    image-20240318215518076

    这里的hasProto是如何定义的?就是判断对象是否支持原型,那一般情况都是支持的。所以就先不管下面的分支处理了。

    image-20240318221925392

    再来看一下arrayMethods是什么

    • 首先通过Object.create(arrayProto),将新创建的arrayMethods对象的_proto_属性指向arrayProto,也就是arrayMethods._proto_ = = Array.prototype
    • 对会改变数组的七个方法,进行了重写。具体实现是
      • 获取数组原始的方法original
      • 通过def()方法,调用Object.definePropertyarrayMethods身上添加新的方法。新方法在原始方法original的基础上进行增强
      • 对于push、unshift、splice等会插入新值的方法,需要获取到新的值,然后对这个值做观测。

    image-20240318220012088

    最后,再将数组的原型属性指向新的对象arrayMethods,即

    array._proto_ == arrayMethods

    arrayMethods._proto_ = = Array.prototype

    image-20240318224636418

  2. 此外,还在if (!shallow)这个判断中,调用了observeArray(value)方法。该方法遍历数组元素,对每一个元素执行observe(),就是为了将数组中那些对象元素身上的属性变成响应式的。而对于基本类型的元素不做任何处理。因为数组可能会有很多的元素,为每一个元素添加getter和setter是很耗费性能的。

    image-20240318220937633

标签:面试题,vue,dep,data,watcher,源码,当前,方法,属性
From: https://www.cnblogs.com/finish/p/18081819

相关文章

  • vue面试题(前置知识)
    一、渐进式框架Vue是一个框架,也是一个生态,其功能覆盖了大部分前端开发常见的需求。包含了声明式渲染、组件化系统、客户端路由、大规模状态管理、构建工具等,但vue的核心库只关注视图层。在实际开发中,可以根据业务需求的变化,来不断增加这些功能,实现vue的渐进式增强。二、声明式框......
  • vue面试题(vue2响应式数据基础)
    一、什么是响应式数据响应式数据是指当数据发生变化时,相关的视图或组件会自动更新,保持与数据的同步。这样的设计使得开发者能够更方便地管理和更新数据,无需手动操作DOM或显式地更新视图。当数据发生变化时,所有使用该数据的地方都会自动更新。二、观察者模式观察者模式定义对......
  • 【20.5】Django框架Form组件之源码
    【一】切入点切入点form_obj.is_valid()defis_valid(self):"""ReturnTrueiftheformhasnoerrors,orFalseotherwise."""returnself.is_boundandnotself.errors如果is_valid要想返回True那么self.is_bound要为Trueself.errors......
  • 前端学习-vue视频学习009-defineProps(子组件接收父组件的数据)
    尚硅谷视频链接defineProps-只接收父:要有数据letpersonList=reactive<personArr>([{id:'qqq1',name:'aaa',age:10,gender:'F'},{id:'qqq2',name:'vvv',age:30,gender:'F'},{id:'qq......
  • 前端框架Vue--Part 01
    1.为什么要学习Vue前端必备技能:Vue.js作为现代前端开发领域中的主流框架之一,已成为前端开发者必备的技术栈。随着前端技术的不断发展和企业需求的变化,掌握Vue.js能够确保开发者跟上行业发展步伐,适应各类Web项目的开发需求。广泛应用与岗位需求:目前,在国内外绝大多数互联网公......
  • 前端面试题-vue2和vue3的区别
    监测机制的改变vue2对数据监测使用的是Object.definePropert()对数据进行劫持,结合发布订阅者模式来实现vue3通过使用proxyAPI对数据直接进行代理。vue3优于vue3的的地方就是:vue3的proxyAPI监测的是整个对象,而不再是某个属性消除了Vue2当中基于Object.defineProperty......
  • Java毕业设计-基于SSM框架的学生成绩管理系统项目实战(附源码+论文)
    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • 【前端素材】推荐优质综合购物电子商城网站设计Cropium平台模板(附源码)
    一、需求分析在线电子数码商店网站是指专门销售电子产品和数码设备的网上商店。这类网站通常提供广泛的产品选择,涵盖手机、平板电脑、相机、电脑配件、智能家居设备等多种数码产品。以下是在线电子数码商店网站的一般功能:产品展示与购买: 网站上展示各种电子数码产品的详细......
  • 计算机毕业设计-基于Java+SSM架构的学生综合考评管理系统项目开发实战(附论文+源码)
    大家好!我是职场程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • 史上最全Java核心面试题(带全部答案)2024年最新版
    今天要谈的主题是关于求职,求职是在每个技术人员的生涯中都要经历多次。对于我们大部分人而言,在进入自己心仪的公司之前少不了准备工作,有一份全面细致面试题将帮助我们减少许多麻烦。在跳槽季来临之前,特地做这个系列的文章,一方面帮助自己巩固下基础,另一方面也希望帮助想要换工......