初识VUE响应式原理

初识,vue,响应,原理 · 浏览次数 : 22

小编点评

**响应式原理** 响应式原理是 Vue 中实现数据双向绑定的核心技术,它允许通过在组件中绑定数据时,实时更新视图,从而实现数据的动态更新。 **Object.defineProperty()** Object.defineProperty() 方法允许我们动态添加或修改对象的属性,并返回此对象。但它只适用于对象本身,无法用于设置属性的依赖。 **Vue3的数据拦截** Vue3 中的 Proxy 是一个数据拦截器,它可以拦截目标对象的访问,并执行相关的回调函数。使用 Proxy 可以弥补 Object.defineProperty 方法无法监听的新增加对象或某些需要特殊操作的数组的缺陷。 **Vue3 的响应式系统** Vue3 使用 Proxy 代理代替了 Object.defineProperty 方法,该代理可以拦截目标对象的属性读取和设置,并执行相关副作用函数。 **Vue3 的响应式系统实现** 1. 读取时(get)会将副作用函数入栈。 2. 设置时(set)会将副作用函数出栈,执行相关副作用函数。 3. 存储副作用函数的栈。 4. 当修改一个属性时,会从栈中取出依赖关系。 5. 执行依赖关系中的副作用函数。

正文

作者:京东零售 吴静

自从Vue发布以来,就受到了广大开发人员的青睐,提到Vue,我们首先想到的就是Vue的响应式系统,那响应式系统到底是怎么回事呢?接下来我就给大家简单介绍一下Vue中的响应式原理。

vue2的响应式原理

尽管Vue2将于2023年12月31日停止维护,但是我们依然有很多项目是基于Vue2.X进行开发的,那么我们先简单看一看Vue2.X是基于什么实现的吧~

Object.defineProperty

Vue2的响应式原理是基于对象的defineProperty()方法进行开发的,那么这个方法有什么作用呢?MDN是这样介绍的:
**

object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

也就是说,我们可以通过对象的这个方法精确的添加或者修改对象的属性。每个对象都具有get/set属性,当访问get属性时,会调用getter方法,当对象的属性值被修改时,会调用setter方法,正式基于getter和setter方法,Vue才可以利用Object.defineProperty来实现响应式系统。

Object.defineProperty在Vue中的使用

在vue中,当把一个普通的JavaScript对象传入Vue实例作为data选项,Vue会遍历此对象的所有属性,并使用object.defineProperty将这些属性转为getter/setter,

getter/setter可以追踪依赖,在属性被访问的时候通知视图变更。

Object.defineProperty(obj, 'targetObj', {
   get() {
     // 完成依赖收集
   },
   set() {
      // 发生变更,同时通知相关依赖
   }
})

vue3的响应式原理

vue2.0很好的实现了数据的双向绑定,但是也遗留了一个很重要的问题:由于Vue会在初始化实例时将property转化为getter/setter,所以,property必须在data对象上先存在才能让Vue将其转换为响应式数据。那么对于新增加的对象、或者某些需要特殊操作的数组想要转换为响应式数据就需要使用Vue.set等方法。

Vue3就很好的解决了这个问题。那么,Vue3是如何解决的呢?让我们就一起看看吧~

Proxy

提到Vue3的数据拦截,我们首先要了解什么是proxy?

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

原来,Vue3用了Proxy代理代替了Object.defineProperty方法。同样的,在proxy中也有get/set方法,举个例子~

var obj = new Proxy({}, {
  get: function (target, name) {
    return name;
  },
  set: function (target, key, val) {
    target[key] = val
    return target;
  }
});

我们通过给每一个目标对象都建立一个对应的Proxy对象对其代理就可以弥补Object.defineProperty对于新增对象无法监听的缺陷。

简单设计一个Vue3的响应系统

实现一个简单的响应系统的思路:

•读取(get)时,将副作用函数入栈;

•设置(set)时,将副作用函数出栈,执行副作用函数。

// 存储副作用函数的栈
const bucket = new Set()

// 存储被注册的副作用函数
let activeEffect

// 注册副作用函数
function effect (fn) {
    // 存储副作用函数
    activeEffect = fn
    fn()
}

// 副作用函数fn
effect (
    () => {
        document.body.innerText = obj.text
    }
)

执行匿名函数fn方法时,会触发响应式数据obj.text的读取操作,进而触发代理对象Proxy的get拦截函数:

const Proxy = new Proxy(data, {
    get (target, key) {
        if (activeEffect) {
            bucket.add(activeEffect)
        }
        return target[key]
    },
    set (target, key, newVal) {
        target[key] = newVal
        bucket.forEach(fn => fn())
        return true
    }
})

到此,我们会发现,有一个疑问,我们怎样能保证修改一个属性之后触发的副作用函数是我预期想要触发的副作用函数呢?为了解决这个问题,我们还需要建立副作用函数与目标对象的联系:

我们仅需要用WeakMap代替Set数据结构:

const bucket = new WeakMap()

修改Proxy对象:

const Proxy = new Proxy(data, { 
    get (target, key) { 
        if (!activeEffect) return target[key]
        // 先从栈中取出depsMap,depsMap中保存目标对象和其相关副作用函数的一对多的关系        
        let depsMap = bucket.get(target)
        if (!depsMap) {
            bucket.set(target, (depsMap = new Map())
        }
        // 再根据key从depsMap中取得deps,deps保存所有与key相关联的副作用函数
        let deps = depsMap.get(key)
        if (!deps) {
            depsMap.set(key, (deps = new Set())
        }
        deps.add(activeEffect)
        
        return target[key] 
    }, 
    set (target, key, newVal) { 
        target[key] = newVal 
        const depsMap = bucket.get(target)
        if (!depsMap) return
        const effects = depsMap.get(key)
        effects && effects.forEach(fn => fn())  
    } 
})

这样,我们就实现了一个简易的响应系统。那么为什么要用weakMap而不是使用Map呢?就交给大家一起思考啦~

参考文献

《Vue.js设计与实现_霍春阳》

《ECMAScript 6入门》-阮一峰

与初识VUE响应式原理相似的内容:

初识VUE响应式原理

自从Vue发布以来,就受到了广大开发人员的青睐,提到Vue,我们首先想到的就是Vue的响应式系统,那响应式系统到底是怎么回事呢?接下来小编就给大家简单介绍一下Vue中的响应式原理。

Vue基础语法整理

# vue基础用法&基础原理整理 1. vue基础知识和原理 1.1 初识Vue 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象 demo容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法 demo容器里的代码被称为【Vue模板】 Vue实例和容器是一一对应的

02.前后端分离中台框架前端 admin.ui.plus 学习-介绍与简单使用

## 中台框架前台项目 admin.ui.plus 的初识 > 基于 vue3.x + CompositionAPI setup 语法糖 + typescript + vite + element plus + vue-router-next + pinia 技术,内置支持一键生成微服务接口,适配手

前端资源公共cdn哪里找

写一些demo的时候使用vue/react脚手架来初始项目太小题大做,直接在html中写代码需要找到一些框架和库的cdn,这里做下推荐,仅限在测试环境用。 bootcdn 优点:是国内速度快,使用简单。 缺点:是资源有限,比如vue目前只有3.x版本,没有2.x版本 unpkg 缺点: 优势网络会中

[转帖]初识SkyWalking

https://zhuanlan.zhihu.com/p/361579294 一、SkyWalking 是什么? 一个开源的可观测平台,用于从服务和云原生基础设施收集,分析, 聚合及可视化数据。 SkyWalking 提供了一种简便的方式来清晰地观测分布式系统,甚至横跨多个云平台。SkyWalkin

[转帖]初识SkyWalking

https://zhuanlan.zhihu.com/p/361579294 一、SkyWalking 是什么? 一个开源的可观测平台,用于从服务和云原生基础设施收集,分析, 聚合及可视化数据。 SkyWalking 提供了一种简便的方式来清晰地观测分布式系统,甚至横跨多个云平台。SkyWalkin

初识Redis与桌面客户端

Redis介绍 什么是Redis Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。 Redis 可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:字符串、列表、

初识上位机(上):搭建PLC模拟仿真环境

作为一个工业自动化领域的程序员,不懂点PLC和上位机,貌似有点说不过去。这里我用两篇小文带你快速进入上位机开发领域。首先,我们先要搭建一个PLC的模拟仿真环境,以便后续能够使用C#开发上位机程序。

初识上位机(下):C#读写PLC数据块数据

作为一个工业自动化领域的程序员,不懂点PLC和上位机,貌似有点说不过去。这里我用两篇小文带你快速进入上位机开发领域。上一篇,我们搭建了一个PLC的模拟仿真环境,本篇我们使用C#开发一个简单的PLC数据读取和写入的应用程序。

云图说丨初识云应用引擎CAE

摘要:开发运营一个应用软件,面临种种挑战:软件栈厚重、开发上线慢、资源易浪费、运维投入高、突发流量应对困难。 华为云应用引擎CAE面对挑战,一扫而光!! 本文分享自华为云社区《【云图说】 | 第266期 初识云应用引擎CAE》,作者: 阅识风云。 CAE(Cloud Application Engi